Commit 88a90bb1 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ee

parents d071ac92 09cdabd2
...@@ -8,4 +8,4 @@ ...@@ -8,4 +8,4 @@
karma.config.js karma.config.js
webpack.config.js webpack.config.js
svg.config.js svg.config.js
/app/assets/javascripts/locale/**/*.js /app/assets/javascripts/locale/**/app.js
...@@ -85,6 +85,8 @@ entry. ...@@ -85,6 +85,8 @@ entry.
- [FIXED] Fixed merge request changes bar jumping. - [FIXED] Fixed merge request changes bar jumping.
- [FIXED] Improve migrations using triggers. - [FIXED] Improve migrations using triggers.
- [FIXED] Fix ConvDev Index nav item and Monitoring submenu regression. - [FIXED] Fix ConvDev Index nav item and Monitoring submenu regression.
- [FIXED] disabling notifications globally now properly turns off group/project added
emails !13325
- [DEPRECATED] Deprecate custom SSH client configuration for the git user. !13930 - [DEPRECATED] Deprecate custom SSH client configuration for the git user. !13930
- [CHANGED] allow all users to delete their account. !13636 (Jacopo Beschi @jacopo-beschi) - [CHANGED] allow all users to delete their account. !13636 (Jacopo Beschi @jacopo-beschi)
- [CHANGED] Use full path of project's avatar in webhooks. !13649 (Vitaliy @blackst0ne Klachkov) - [CHANGED] Use full path of project's avatar in webhooks. !13649 (Vitaliy @blackst0ne Klachkov)
......
...@@ -414,7 +414,7 @@ group :ed25519 do ...@@ -414,7 +414,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.33.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.38.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -299,7 +299,7 @@ GEM ...@@ -299,7 +299,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly-proto (0.33.0) gitaly-proto (0.38.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -1060,7 +1060,7 @@ DEPENDENCIES ...@@ -1060,7 +1060,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.33.0) gitaly-proto (~> 0.38.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 1.0) gitlab-license (~> 1.0)
......
...@@ -40,10 +40,10 @@ export default () => { ...@@ -40,10 +40,10 @@ export default () => {
class="text-center" class="text-center"
v-if="error"> v-if="error">
<span v-if="loadError"> <span v-if="loadError">
An error occured whilst loading the file. Please try again later. An error occurred whilst loading the file. Please try again later.
</span> </span>
<span v-else> <span v-else>
An error occured whilst parsing the file. An error occurred whilst parsing the file.
</span> </span>
</p> </p>
</div> </div>
......
...@@ -48,10 +48,10 @@ export default () => { ...@@ -48,10 +48,10 @@ export default () => {
class="text-center" class="text-center"
v-if="error"> v-if="error">
<span v-if="loadError"> <span v-if="loadError">
An error occured whilst loading the file. Please try again later. An error occurred whilst loading the file. Please try again later.
</span> </span>
<span v-else> <span v-else>
An error occured whilst decoding the file. An error occurred whilst decoding the file.
</span> </span>
</p> </p>
</div> </div>
......
...@@ -96,7 +96,7 @@ export default { ...@@ -96,7 +96,7 @@ export default {
<div class="flash-container" <div class="flash-container"
v-if="error"> v-if="error">
<div class="flash-alert"> <div class="flash-alert">
An error occured. Please try again. An error occurred. Please try again.
</div> </div>
</div> </div>
<label class="label-light" <label class="label-light"
......
...@@ -167,7 +167,7 @@ window.Build = (function () { ...@@ -167,7 +167,7 @@ window.Build = (function () {
Build.prototype.getBuildTrace = function () { Build.prototype.getBuildTrace = function () {
return $.ajax({ return $.ajax({
url: `${this.pageUrl}/trace.json`, url: `${this.pageUrl}/trace.json`,
data: this.state, data: { state: this.state },
}) })
.done((log) => { .done((log) => {
setCiStatusFavicon(`${this.pageUrl}/status.json`); setCiStatusFavicon(`${this.pageUrl}/status.json`);
......
...@@ -191,7 +191,7 @@ export default { ...@@ -191,7 +191,7 @@ export default {
this.service.postAction(endpoint) this.service.postAction(endpoint)
.then(() => this.fetchEnvironments()) .then(() => this.fetchEnvironments())
.catch(() => new Flash('An error occured while making the request.')); .catch(() => new Flash('An error occurred while making the request.'));
} }
}, },
......
...@@ -170,7 +170,7 @@ export default { ...@@ -170,7 +170,7 @@ export default {
this.service.postAction(endpoint) this.service.postAction(endpoint)
.then(() => this.fetchEnvironments()) .then(() => this.fetchEnvironments())
.catch(() => new Flash('An error occured while making the request.')); .catch(() => new Flash('An error occurred while making the request.'));
} }
}, },
}, },
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* causes reflows, visit https://gist.github.com/paulirish/5d52fb081b3570c81e3a * causes reflows, visit https://gist.github.com/paulirish/5d52fb081b3570c81e3a
*/ */
import Cookies from 'js-cookie';
const LINE_NUMBER_CLASS = 'diff-line-num'; const LINE_NUMBER_CLASS = 'diff-line-num';
const UNFOLDABLE_LINE_CLASS = 'js-unfold'; const UNFOLDABLE_LINE_CLASS = 'js-unfold';
const NO_COMMENT_CLASS = 'no-comment-btn'; const NO_COMMENT_CLASS = 'no-comment-btn';
...@@ -27,9 +29,7 @@ export default { ...@@ -27,9 +29,7 @@ export default {
this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('can-create-note') === ''; this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('can-create-note') === '';
} }
if (typeof notes !== 'undefined' && !this.isParallelView) { this.isParallelView = Cookies.get('diff_view') === 'parallel';
this.isParallelView = notes.isParallelView && notes.isParallelView();
}
if (this.userCanCreateNote) { if (this.userCanCreateNote) {
$diffFile.on('mouseover', LINE_COLUMN_CLASSES, e => this.showButton(this.isParallelView, e)) $diffFile.on('mouseover', LINE_COLUMN_CLASSES, e => this.showButton(this.isParallelView, e))
......
...@@ -14,7 +14,7 @@ class DropdownEmoji extends gl.FilteredSearchDropdown { ...@@ -14,7 +14,7 @@ class DropdownEmoji extends gl.FilteredSearchDropdown {
loadingTemplate: this.loadingTemplate, loadingTemplate: this.loadingTemplate,
onError() { onError() {
/* eslint-disable no-new */ /* eslint-disable no-new */
new Flash('An error occured fetching the dropdown data.'); new Flash('An error occurred fetching the dropdown data.');
/* eslint-enable no-new */ /* eslint-enable no-new */
}, },
}, },
......
...@@ -17,7 +17,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { ...@@ -17,7 +17,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown {
preprocessing, preprocessing,
onError() { onError() {
/* eslint-disable no-new */ /* eslint-disable no-new */
new Flash('An error occured fetching the dropdown data.'); new Flash('An error occurred fetching the dropdown data.');
/* eslint-enable no-new */ /* eslint-enable no-new */
}, },
}, },
......
...@@ -27,7 +27,7 @@ class DropdownUser extends gl.FilteredSearchDropdown { ...@@ -27,7 +27,7 @@ class DropdownUser extends gl.FilteredSearchDropdown {
}, },
onError() { onError() {
/* eslint-disable no-new */ /* eslint-disable no-new */
new Flash('An error occured fetching the dropdown data.'); new Flash('An error occurred fetching the dropdown data.');
/* eslint-enable no-new */ /* eslint-enable no-new */
}, },
}, },
......
...@@ -44,7 +44,7 @@ class FilteredSearchManager { ...@@ -44,7 +44,7 @@ class FilteredSearchManager {
.catch((error) => { .catch((error) => {
if (error.name === 'RecentSearchesServiceError') return undefined; if (error.name === 'RecentSearchesServiceError') return undefined;
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new window.Flash('An error occured while parsing recent searches'); new window.Flash('An error occurred while parsing recent searches');
// Gracefully fail to empty array // Gracefully fail to empty array
return []; return [];
}) })
......
...@@ -16,9 +16,8 @@ const locales = allLocales.reduce((d, obj) => { ...@@ -16,9 +16,8 @@ const locales = allLocales.reduce((d, obj) => {
return data; return data;
}, {}); }, {});
let lang = document.querySelector('html').getAttribute('lang') || 'en'; const langAttribute = document.querySelector('html').getAttribute('lang');
lang = lang.replace(/-/g, '_'); const lang = (langAttribute || 'en').replace(/-/g, '_');
const locale = new Jed(locales[lang]); const locale = new Jed(locales[lang]);
/** /**
......
...@@ -311,7 +311,10 @@ $(function () { ...@@ -311,7 +311,10 @@ $(function () {
return $container.remove(); return $container.remove();
// Commit show suppressed diff // Commit show suppressed diff
}); });
$('.navbar-toggle').on('click', () => $('.header-content').toggleClass('menu-expanded')); $('.navbar-toggle').on('click', () => {
$('.header-content').toggleClass('menu-expanded');
gl.lazyLoader.loadCheck();
});
// Show/hide comments on diff // Show/hide comments on diff
$body.on('click', '.js-toggle-diff-comments', function (e) { $body.on('click', '.js-toggle-diff-comments', function (e) {
var $this = $(this); var $this = $(this);
......
...@@ -97,7 +97,7 @@ export default { ...@@ -97,7 +97,7 @@ export default {
postAction(endpoint) { postAction(endpoint) {
this.service.postAction(endpoint) this.service.postAction(endpoint)
.then(() => eventHub.$emit('refreshPipelines')) .then(() => eventHub.$emit('refreshPipelines'))
.catch(() => new Flash('An error occured while making the request.')); .catch(() => new Flash('An error occurred while making the request.'));
}, },
}, },
}; };
...@@ -73,7 +73,8 @@ import _ from 'underscore'; ...@@ -73,7 +73,8 @@ import _ from 'underscore';
aspectRatio: 1, aspectRatio: 1,
modal: true, modal: true,
scalable: false, scalable: false,
rotatable: false, rotatable: true,
checkOrientation: true,
zoomable: true, zoomable: true,
dragMode: 'move', dragMode: 'move',
guides: false, guides: false,
......
...@@ -37,14 +37,14 @@ export default { ...@@ -37,14 +37,14 @@ export default {
content: f.newContent, content: f.newContent,
})); }));
const payload = { const payload = {
branch: Store.targetBranch, branch: Store.currentBranch,
commit_message: commitMessage, commit_message: commitMessage,
actions, actions,
}; };
Store.submitCommitsLoading = true; Store.submitCommitsLoading = true;
Service.commitFiles(payload) Service.commitFiles(payload)
.then(this.resetCommitState) .then(this.resetCommitState)
.catch(() => Flash('An error occured while committing your changes')); .catch(() => Flash('An error occurred while committing your changes'));
}, },
resetCommitState() { resetCommitState() {
...@@ -105,7 +105,7 @@ export default { ...@@ -105,7 +105,7 @@ export default {
</label> </label>
<div class="col-md-6"> <div class="col-md-6">
<span class="help-block"> <span class="help-block">
{{targetBranch}} {{currentBranch}}
</span> </span>
</div> </div>
</div> </div>
......
...@@ -26,16 +26,6 @@ export default { ...@@ -26,16 +26,6 @@ export default {
this.editMode = !this.editMode; this.editMode = !this.editMode;
Store.toggleBlobView(); Store.toggleBlobView();
}, },
toggleProjectRefsForm() {
$('.project-refs-form').toggleClass('disabled', this.editMode);
$('.js-tree-ref-target-holder').toggle(this.editMode);
},
},
watch: {
editMode() {
this.toggleProjectRefsForm();
},
}, },
}; };
</script> </script>
......
...@@ -49,7 +49,7 @@ export default { ...@@ -49,7 +49,7 @@ export default {
v-else v-else
class="vertical-center render-error"> class="vertical-center render-error">
<p class="text-center"> <p class="text-center">
The source could not be displayed because a rendering error occured. You can <a :href="activeFile.raw_path">download</a> it instead. The source could not be displayed because a rendering error occurred. You can <a :href="activeFile.raw_path">download</a> it instead.
</p> </p>
</div> </div>
</div> </div>
......
...@@ -37,17 +37,24 @@ export default { ...@@ -37,17 +37,24 @@ export default {
let file = clickedFile; let file = clickedFile;
if (file.loading) return; if (file.loading) return;
file.loading = true; file.loading = true;
if (file.type === 'tree' && file.opened) { if (file.type === 'tree' && file.opened) {
file = Store.removeChildFilesOfTree(file); file = Store.removeChildFilesOfTree(file);
file.loading = false; file.loading = false;
} else { } else {
Service.url = file.url; const openFile = Helper.getFileFromPath(file.url);
Helper.getContent(file) if (openFile) {
.then(() => { file.loading = false;
file.loading = false; Store.setActiveFiles(openFile);
Helper.scrollTabsRight(); } else {
}) Service.url = file.url;
.catch(Helper.loadingError); Helper.getContent(file)
.then(() => {
file.loading = false;
Helper.scrollTabsRight();
})
.catch(Helper.loadingError);
}
} }
}, },
......
...@@ -263,6 +263,10 @@ const RepoHelper = { ...@@ -263,6 +263,10 @@ const RepoHelper = {
return Store.openedFiles.find(openedFile => Store.activeFile.url === openedFile.url); return Store.openedFiles.find(openedFile => Store.activeFile.url === openedFile.url);
}, },
getFileFromPath(path) {
return Store.openedFiles.find(file => file.url === path);
},
loadingError() { loadingError() {
Flash('Unable to load this content at this time.'); Flash('Unable to load this content at this time.');
}, },
......
...@@ -11,10 +11,6 @@ function initDropdowns() { ...@@ -11,10 +11,6 @@ function initDropdowns() {
} }
function addEventsForNonVueEls() { function addEventsForNonVueEls() {
$(document).on('change', '.dropdown', () => {
Store.targetBranch = $('.project-refs-target-form input[name="ref"]').val();
});
window.onbeforeunload = function confirmUnload(e) { window.onbeforeunload = function confirmUnload(e) {
const hasChanged = Store.openedFiles const hasChanged = Store.openedFiles
.some(file => file.changed); .some(file => file.changed);
......
...@@ -32,7 +32,6 @@ const RepoStore = { ...@@ -32,7 +32,6 @@ const RepoStore = {
isCommitable: false, isCommitable: false,
binary: false, binary: false,
currentBranch: '', currentBranch: '',
targetBranch: 'new-branch',
commitMessage: '', commitMessage: '',
binaryTypes: { binaryTypes: {
png: false, png: false,
......
...@@ -43,6 +43,8 @@ import Cookies from 'js-cookie'; ...@@ -43,6 +43,8 @@ import Cookies from 'js-cookie';
$allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
$('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
$('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
if (gl.lazyLoader) gl.lazyLoader.loadCheck();
} }
if (!triggered) { if (!triggered) {
return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
......
...@@ -38,7 +38,7 @@ class SidebarMoveIssue { ...@@ -38,7 +38,7 @@ class SidebarMoveIssue {
data: (searchTerm, callback) => { data: (searchTerm, callback) => {
this.mediator.fetchAutocompleteProjects(searchTerm) this.mediator.fetchAutocompleteProjects(searchTerm)
.then(callback) .then(callback)
.catch(() => new Flash('An error occured while fetching projects autocomplete.')); .catch(() => new Flash('An error occurred while fetching projects autocomplete.'));
}, },
renderRow: project => ` renderRow: project => `
<li> <li>
...@@ -73,7 +73,7 @@ class SidebarMoveIssue { ...@@ -73,7 +73,7 @@ class SidebarMoveIssue {
this.mediator.moveIssue() this.mediator.moveIssue()
.catch(() => { .catch(() => {
Flash('An error occured while moving the issue.'); Flash('An error occurred while moving the issue.');
this.$confirmButton this.$confirmButton
.enable() .enable()
.removeClass('is-loading'); .removeClass('is-loading');
......
...@@ -41,7 +41,7 @@ export default class SidebarMediator { ...@@ -41,7 +41,7 @@ export default class SidebarMediator {
this.store.setAssigneeData(data); this.store.setAssigneeData(data);
this.store.setTimeTrackingData(data); this.store.setTimeTrackingData(data);
}) })
.catch(() => new Flash('Error occured when fetching sidebar data')); .catch(() => new Flash('Error occurred when fetching sidebar data'));
} }
fetchAutocompleteProjects(searchTerm) { fetchAutocompleteProjects(searchTerm) {
......
...@@ -27,7 +27,7 @@ export default { ...@@ -27,7 +27,7 @@ export default {
<button <button
v-if="showDisabledButton" v-if="showDisabledButton"
type="button" type="button"
class="btn btn-success btn-sm" class="js-disabled-merge-button btn btn-success btn-sm"
disabled="true"> disabled="true">
Merge Merge
</button> </button>
......
...@@ -10,27 +10,37 @@ export default { ...@@ -10,27 +10,37 @@ export default {
}, },
template: ` template: `
<div class="mr-widget-body media"> <div class="mr-widget-body media">
<status-icon status="failed" showDisabledButton /> <status-icon
status="failed"
showDisabledButton />
<div class="media-body space-children"> <div class="media-body space-children">
<span class="bold"> <span
There are merge conflicts<span v-if="!mr.canMerge">.</span> v-if="mr.shouldBeRebased"
<span v-if="!mr.canMerge"> class="bold">
Resolve these conflicts or ask someone with write access to this repository to merge it locally Fast-forward merge is not possible.
</span> To merge this request, first rebase locally.
</span> </span>
<a <template v-else>
v-if="mr.canMerge && mr.conflictResolutionPath" <span class="bold">
:href="mr.conflictResolutionPath" There are merge conflicts<span v-if="!mr.canMerge">.</span>
class="btn btn-default btn-xs js-resolve-conflicts-button"> <span v-if="!mr.canMerge">
Resolve conflicts Resolve these conflicts or ask someone with write access to this repository to merge it locally
</a> </span>
<a </span>
v-if="mr.canMerge" <a
class="btn btn-default btn-xs js-merge-locally-button" v-if="mr.canMerge && mr.conflictResolutionPath"
data-toggle="modal" :href="mr.conflictResolutionPath"
href="#modal_merge_info"> class="js-resolve-conflicts-button btn btn-default btn-xs">
Merge locally Resolve conflicts
</a> </a>
<a
v-if="mr.canMerge"
class="js-merge-locally-button btn btn-default btn-xs"
data-toggle="modal"
href="#modal_merge_info">
Merge locally
</a>
</template>
</div> </div>
</div> </div>
`, `,
......
...@@ -288,14 +288,16 @@ export default { ...@@ -288,14 +288,16 @@ export default {
:mr="mr" :mr="mr"
:is-merge-button-disabled="isMergeButtonDisabled" /> :is-merge-button-disabled="isMergeButtonDisabled" />
<span v-if="mr.ffOnlyEnabled"> <span
v-if="mr.ffOnlyEnabled"
class="js-fast-forward-message">
Fast-forward merge without a merge commit Fast-forward merge without a merge commit
</span> </span>
<button <button
v-else v-else
@click="toggleCommitMessageEditor" @click="toggleCommitMessageEditor"
:disabled="isMergeButtonDisabled" :disabled="isMergeButtonDisabled"
class="btn btn-default btn-xs" class="js-modify-commit-message-button btn btn-default btn-xs"
type="button"> type="button">
Modify commit message Modify commit message
</button> </button>
......
...@@ -59,6 +59,8 @@ export default class MergeRequestStore { ...@@ -59,6 +59,8 @@ export default class MergeRequestStore {
this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false; this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false;
this.mergeWhenPipelineSucceeds = data.merge_when_pipeline_succeeds || false; this.mergeWhenPipelineSucceeds = data.merge_when_pipeline_succeeds || false;
this.mergePath = data.merge_path; this.mergePath = data.merge_path;
this.ffOnlyEnabled = data.ff_only_enabled;
this.shouldBeRebased = !!data.should_be_rebased;
this.statusPath = data.status_path; this.statusPath = data.status_path;
this.emailPatchesPath = data.email_patches_path; this.emailPatchesPath = data.email_patches_path;
this.plainDiffPath = data.plain_diff_path; this.plainDiffPath = data.plain_diff_path;
......
...@@ -535,7 +535,6 @@ ...@@ -535,7 +535,6 @@
} }
.diff-notes-collapse { .diff-notes-collapse {
position: relative;
width: 19px; width: 19px;
height: 19px; height: 19px;
padding: 0; padding: 0;
...@@ -543,11 +542,7 @@ ...@@ -543,11 +542,7 @@
z-index: 100; z-index: 100;
svg { svg {
position: absolute; vertical-align: text-top;
left: 50%;
top: 50%;
margin-left: -5.5px;
margin-top: -5.5px;
} }
path { path {
......
...@@ -16,7 +16,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -16,7 +16,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :authorize_create_issue!, only: [:new, :create] before_action :authorize_create_issue!, only: [:new, :create]
# Allow modify issue # Allow modify issue
before_action :authorize_update_issue!, only: [:edit, :update, :move] before_action :authorize_update_issue!, only: [:update, :move]
# Allow create a new branch and empty WIP merge request from current issue # Allow create a new branch and empty WIP merge request from current issue
before_action :authorize_create_merge_request!, only: [:create_merge_request] before_action :authorize_create_merge_request!, only: [:create_merge_request]
...@@ -65,10 +65,6 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -65,10 +65,6 @@ class Projects::IssuesController < Projects::ApplicationController
respond_with(@issue) respond_with(@issue)
end end
def edit
respond_with(@issue)
end
def show def show
@noteable = @issue @noteable = @issue
@note = @project.notes.new(noteable: @issue) @note = @project.notes.new(noteable: @issue)
...@@ -128,10 +124,6 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -128,10 +124,6 @@ class Projects::IssuesController < Projects::ApplicationController
@issue = Issues::UpdateService.new(project, current_user, update_params).execute(issue) @issue = Issues::UpdateService.new(project, current_user, update_params).execute(issue)
respond_to do |format| respond_to do |format|
format.html do
recaptcha_check_with_fallback { render :edit }
end
format.json do format.json do
render_issue_json render_issue_json
end end
......
...@@ -347,6 +347,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -347,6 +347,7 @@ class ProjectsController < Projects::ApplicationController
:tag_list, :tag_list,
:visibility_level, :visibility_level,
:template_name, :template_name,
:merge_method,
project_feature_attributes: %i[ project_feature_attributes: %i[
builds_access_level builds_access_level
......
module CustomAttributesFilter
def by_custom_attributes(items)
return items unless params[:custom_attributes].is_a?(Hash)
return items unless Ability.allowed?(current_user, :read_custom_attribute)
association = items.reflect_on_association(:custom_attributes)
attributes_table = association.klass.arel_table
attributable_table = items.model.arel_table
custom_attributes = association.klass.select('true').where(
attributes_table[association.foreign_key]
.eq(attributable_table[association.association_primary_key])
)
# perform a subquery for each attribute to be filtered
params[:custom_attributes].inject(items) do |scope, (key, value)|
scope.where('EXISTS (?)', custom_attributes.where(key: key, value: value))
end
end
end
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# #
class UsersFinder class UsersFinder
include CreatedAtFilter include CreatedAtFilter
include CustomAttributesFilter
attr_accessor :current_user, :params attr_accessor :current_user, :params
...@@ -33,6 +34,7 @@ class UsersFinder ...@@ -33,6 +34,7 @@ class UsersFinder
users = by_external_identity(users) users = by_external_identity(users)
users = by_external(users) users = by_external(users)
users = by_created_at(users) users = by_created_at(users)
users = by_custom_attributes(users)
users = by_non_ldap(users) users = by_non_ldap(users)
users users
......
...@@ -10,11 +10,7 @@ module BreadcrumbsHelper ...@@ -10,11 +10,7 @@ module BreadcrumbsHelper
def breadcrumb_title_link def breadcrumb_title_link
return @breadcrumb_link if @breadcrumb_link return @breadcrumb_link if @breadcrumb_link
if controller.available_action?(:index) request.path
url_for(action: "index")
else
request.path
end
end end
def breadcrumb_title(title) def breadcrumb_title(title)
......
...@@ -249,16 +249,25 @@ module IssuablesHelper ...@@ -249,16 +249,25 @@ module IssuablesHelper
Gitlab::IssuablesCountForState.new(finder)[state] Gitlab::IssuablesCountForState.new(finder)[state]
end end
def close_issuable_url(issuable) def close_issuable_path(issuable)
issuable_url(issuable, close_reopen_params(issuable, :close)) issuable_path(issuable, close_reopen_params(issuable, :close))
end end
def reopen_issuable_url(issuable) def reopen_issuable_path(issuable)
issuable_url(issuable, close_reopen_params(issuable, :reopen)) issuable_path(issuable, close_reopen_params(issuable, :reopen))
end end
def close_reopen_issuable_url(issuable, should_inverse = false) def close_reopen_issuable_path(issuable, should_inverse = false)
issuable.closed? ^ should_inverse ? reopen_issuable_url(issuable) : close_issuable_url(issuable) issuable.closed? ^ should_inverse ? reopen_issuable_path(issuable) : close_issuable_path(issuable)
end
def issuable_path(issuable, *options)
case issuable
when Issue
issue_path(issuable, *options)
when MergeRequest
merge_request_path(issuable, *options)
end
end end
def issuable_url(issuable, *options) def issuable_url(issuable, *options)
......
...@@ -23,6 +23,7 @@ class GeoNode < ActiveRecord::Base ...@@ -23,6 +23,7 @@ class GeoNode < ActiveRecord::Base
validates :encrypted_secret_access_key, presence: true validates :encrypted_secret_access_key, presence: true
validates :geo_node_key, presence: true, if: :secondary? validates :geo_node_key, presence: true, if: :secondary?
validate :check_not_adding_primary_as_secondary, if: :secondary?
after_initialize :build_dependents after_initialize :build_dependents
after_save :expire_cache! after_save :expire_cache!
...@@ -216,12 +217,12 @@ class GeoNode < ActiveRecord::Base ...@@ -216,12 +217,12 @@ class GeoNode < ActiveRecord::Base
end end
end end
def validate(record) # Prevent locking yourself out
# Prevent locking yourself out def check_not_adding_primary_as_secondary
if record.host == Gitlab.config.gitlab.host && if host == Gitlab.config.gitlab.host &&
record.port == Gitlab.config.gitlab.port && port == Gitlab.config.gitlab.port &&
record.relative_url_root == Gitlab.config.gitlab.relative_url_root && !record.primary relative_url_root == Gitlab.config.gitlab.relative_url_root
record.errors[:base] << 'Current node must be the primary node or you will be locking yourself out' errors.add(:base, 'Current node must be the primary node or you will be locking yourself out')
end end
end end
......
...@@ -12,7 +12,6 @@ class License < ActiveRecord::Base ...@@ -12,7 +12,6 @@ class License < ActiveRecord::Base
contribution_analytics contribution_analytics
elastic_search elastic_search
export_issues export_issues
fast_forward_merge
group_webhooks group_webhooks
issuable_default_templates issuable_default_templates
issue_board_focus_mode issue_board_focus_mode
...@@ -63,7 +62,6 @@ class License < ActiveRecord::Base ...@@ -63,7 +62,6 @@ class License < ActiveRecord::Base
cross_project_pipelines cross_project_pipelines
deploy_board deploy_board
export_issues export_issues
fast_forward_merge
file_locks file_locks
group_webhooks group_webhooks
issuable_default_templates issuable_default_templates
......
...@@ -549,6 +549,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -549,6 +549,14 @@ class MergeRequest < ActiveRecord::Base
true true
end end
def ff_merge_possible?
project.repository.ancestor?(target_branch_sha, diff_head_sha)
end
def should_be_rebased?
project.ff_merge_must_be_possible? && !ff_merge_possible?
end
def can_cancel_merge_when_pipeline_succeeds?(current_user) def can_cancel_merge_when_pipeline_succeeds?(current_user)
can_be_merged_by?(current_user) || self.author == current_user can_be_merged_by?(current_user) || self.author == current_user
end end
......
...@@ -1035,7 +1035,7 @@ class Project < ActiveRecord::Base ...@@ -1035,7 +1035,7 @@ class Project < ActiveRecord::Base
# Forked import is handled asynchronously # Forked import is handled asynchronously
return if forked? && !force return if forked? && !force
if gitlab_shell.add_repository(repository_storage_path, disk_path) if gitlab_shell.add_repository(repository_storage, disk_path)
repository.after_create repository.after_create
true true
else else
...@@ -1586,6 +1586,34 @@ class Project < ActiveRecord::Base ...@@ -1586,6 +1586,34 @@ class Project < ActiveRecord::Base
Gitlab::GlRepository.gl_repository(self, is_wiki) Gitlab::GlRepository.gl_repository(self, is_wiki)
end end
def merge_method
if self.merge_requests_ff_only_enabled
:ff
elsif self.merge_requests_rebase_enabled
:rebase_merge
else
:merge
end
end
def merge_method=(method)
case method.to_s
when "ff"
self.merge_requests_ff_only_enabled = true
self.merge_requests_rebase_enabled = true
when "rebase_merge"
self.merge_requests_ff_only_enabled = false
self.merge_requests_rebase_enabled = true
when "merge"
self.merge_requests_ff_only_enabled = false
self.merge_requests_rebase_enabled = false
end
end
def ff_merge_must_be_possible?
self.merge_requests_ff_only_enabled || self.merge_requests_rebase_enabled
end
private private
def storage def storage
......
...@@ -189,7 +189,7 @@ class ProjectWiki ...@@ -189,7 +189,7 @@ class ProjectWiki
private private
def init_repo(disk_path) def init_repo(disk_path)
gitlab_shell.add_repository(project.repository_storage_path, disk_path) gitlab_shell.add_repository(project.repository_storage, disk_path)
end end
def commit_details(action, message = nil, title = nil) def commit_details(action, message = nil, title = nil)
......
...@@ -496,13 +496,7 @@ class Repository ...@@ -496,13 +496,7 @@ class Repository
def exists? def exists?
return false unless full_path return false unless full_path
Gitlab::GitalyClient.migrate(:repository_exists) do |enabled| raw_repository.exists?
if enabled
raw_repository.exists?
else
refs_directory_exists?
end
end
end end
cache_method :exists? cache_method :exists?
...@@ -1143,12 +1137,6 @@ class Repository ...@@ -1143,12 +1137,6 @@ class Repository
blob.data blob.data
end end
def refs_directory_exists?
circuit_breaker.perform do
File.exist?(File.join(path_to_repo, 'refs'))
end
end
def cache def cache
# TODO: should we use UUIDs here? We could move repositories without clearing this cache # TODO: should we use UUIDs here? We could move repositories without clearing this cache
@cache ||= RepositoryCache.new(full_path, @project.id) @cache ||= RepositoryCache.new(full_path, @project.id)
...@@ -1200,10 +1188,6 @@ class Repository ...@@ -1200,10 +1188,6 @@ class Repository
Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, false)) Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, false))
end end
def circuit_breaker
@circuit_breaker ||= Gitlab::Git::Storage::CircuitBreaker.for_storage(project.repository_storage)
end
def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset) def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
ref ||= root_ref ref ||= root_ref
......
...@@ -133,6 +133,8 @@ class User < ActiveRecord::Base ...@@ -133,6 +133,8 @@ class User < ActiveRecord::Base
has_many :assigned_issues, class_name: "Issue", through: :issue_assignees, source: :issue has_many :assigned_issues, class_name: "Issue", through: :issue_assignees, source: :issue
has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" # rubocop:disable Cop/ActiveRecordDependent has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" # rubocop:disable Cop/ActiveRecordDependent
has_many :custom_attributes, class_name: 'UserCustomAttribute'
# #
# Validations # Validations
# #
......
class UserCustomAttribute < ActiveRecord::Base
belongs_to :user
validates :user_id, :key, :value, presence: true
validates :key, uniqueness: { scope: [:user_id] }
end
...@@ -47,4 +47,9 @@ class GlobalPolicy < BasePolicy ...@@ -47,4 +47,9 @@ class GlobalPolicy < BasePolicy
rule { ~(anonymous & restricted_public_level) }.policy do rule { ~(anonymous & restricted_public_level) }.policy do
enable :read_users_list enable :read_users_list
end end
rule { admin }.policy do
enable :read_custom_attribute
enable :update_custom_attribute
end
end end
...@@ -14,6 +14,7 @@ module MergeRequests ...@@ -14,6 +14,7 @@ module MergeRequests
notification_service.merge_mr(merge_request, current_user) notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge') execute_hooks(merge_request, 'merge')
invalidate_cache_counts(merge_request, users: merge_request.assignees) invalidate_cache_counts(merge_request, users: merge_request.assignees)
merge_request.update_project_counter_caches
end end
private private
......
...@@ -11,7 +11,7 @@ module Tags ...@@ -11,7 +11,7 @@ module Tags
begin begin
new_tag = repository.add_tag(current_user, tag_name, target, message) new_tag = repository.add_tag(current_user, tag_name, target, message)
rescue Rugged::TagError rescue Gitlab::Git::Repository::TagExistsError
return error("Tag #{tag_name} already exists") return error("Tag #{tag_name} already exists")
rescue Gitlab::Git::HooksService::PreReceiveError => ex rescue Gitlab::Git::HooksService::PreReceiveError => ex
return error(ex.message) return error(ex.message)
......
...@@ -12,10 +12,10 @@ module Users ...@@ -12,10 +12,10 @@ module Users
def execute(validate: true, &block) def execute(validate: true, &block)
yield(@user) if block_given? yield(@user) if block_given?
assign_attributes(&block)
user_exists = @user.persisted? user_exists = @user.persisted?
assign_attributes(&block)
if @user.save(validate: validate) if @user.save(validate: validate)
notify_success(user_exists) notify_success(user_exists)
else else
...@@ -31,7 +31,7 @@ module Users ...@@ -31,7 +31,7 @@ module Users
true true
end end
protected private
def notify_success(user_exists) def notify_success(user_exists)
notify_new_user(@user, nil) unless user_exists notify_new_user(@user, nil) unless user_exists
...@@ -39,8 +39,6 @@ module Users ...@@ -39,8 +39,6 @@ module Users
success success
end end
private
def assign_attributes(&block) def assign_attributes(&block)
if @user.user_synced_attributes_metadata if @user.user_synced_attributes_metadata
params.except!(*@user.user_synced_attributes_metadata.read_only_attributes) params.except!(*@user.user_synced_attributes_metadata.read_only_attributes)
......
- confirmation_link = confirmation_url(@resource, confirmation_token: @token)
- if @resource.unconfirmed_email.present? - if @resource.unconfirmed_email.present?
#content #content
= email_default_heading(@resource.unconfirmed_email) = email_default_heading(@resource.unconfirmed_email)
%p Click the link below to confirm your email address. %p Click the link below to confirm your email address.
#cta #cta
= link_to 'Confirm your email address', confirmation_url(@resource, confirmation_token: @token) = link_to confirmation_link, confirmation_link
- else - else
#content #content
- if Gitlab.com? - if Gitlab.com?
...@@ -12,4 +13,4 @@ ...@@ -12,4 +13,4 @@
= email_default_heading("Welcome, #{@resource.name}!") = email_default_heading("Welcome, #{@resource.name}!")
%p To get started, click the link below to confirm your account. %p To get started, click the link below to confirm your account.
#cta #cta
= link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token) = link_to confirmation_link, confirmation_link
...@@ -26,19 +26,19 @@ ...@@ -26,19 +26,19 @@
= link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('search') = icon('search')
- if current_user - if current_user
%li.user-counter = nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= custom_icon('issues') = custom_icon('issues')
- issues_count = assigned_issuables_count(:issues) - issues_count = assigned_issuables_count(:issues)
%span.badge.issues-count{ class: ('hidden' if issues_count.zero?) } %span.badge.issues-count{ class: ('hidden' if issues_count.zero?) }
= number_with_delimiter(issues_count) = number_with_delimiter(issues_count)
%li.user-counter = nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter" }) do
= link_to assigned_mrs_dashboard_path, title: 'Merge requests', class: 'dashboard-shortcuts-merge_requests', aria: { label: "Merge requests" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to assigned_mrs_dashboard_path, title: 'Merge requests', class: 'dashboard-shortcuts-merge_requests', aria: { label: "Merge requests" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= custom_icon('mr_bold') = custom_icon('mr_bold')
- merge_requests_count = assigned_issuables_count(:merge_requests) - merge_requests_count = assigned_issuables_count(:merge_requests)
%span.badge.merge-requests-count{ class: ('hidden' if merge_requests_count.zero?) } %span.badge.merge-requests-count{ class: ('hidden' if merge_requests_count.zero?) }
= number_with_delimiter(merge_requests_count) = number_with_delimiter(merge_requests_count)
%li.user-counter = nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
= link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= custom_icon('todo_done') = custom_icon('todo_done')
%span.badge.todos-count{ class: ('hidden' if todos_pending_count.zero?) } %span.badge.todos-count{ class: ('hidden' if todos_pending_count.zero?) }
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
= breadcrumb_list_item link_to(extra[:text], extra[:link]) = breadcrumb_list_item link_to(extra[:text], extra[:link])
= render "layouts/nav/breadcrumbs/collapsed_dropdown", location: :after = render "layouts/nav/breadcrumbs/collapsed_dropdown", location: :after
%li %li
%h2.breadcrumbs-sub-title= @breadcrumb_title %h2.breadcrumbs-sub-title= link_to @breadcrumb_title, breadcrumb_title_link
= yield :header_content = yield :header_content
- form = local_assigns.fetch(:form)
- project = local_assigns.fetch(:project)
.radio
= label_tag :project_merge_method_ff do
= form.radio_button :merge_method, :ff, class: "js-merge-method-radio"
%strong Fast-forward merge
%br
%span.descr
No merge commits are created and all merges are fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded.
%br
%span.descr
When fast-forward merge is not possible, the user must first rebase locally.
- form = local_assigns.fetch(:form)
.radio
= label_tag :project_merge_method_rebase_merge do
= form.radio_button :merge_method, :rebase_merge, class: "js-merge-method-radio"
%strong Merge commit with semi-linear history
%br
%span.descr
A merge commit is created for every merge, but merging is only allowed if fast-forward merge is possible.
This way you could make sure that if this merge request would build, after merging to target branch it would also build.
%br
%span.descr
When fast-forward merge is not possible, the user must first rebase locally.
- form = local_assigns.fetch(:form) - form = local_assigns.fetch(:form)
= render 'projects/ee/merge_request_settings', form: form, project: @project .form-group
= label_tag :merge_method_merge, class: 'label-light' do
Merge method
.radio
= label_tag :project_merge_method_merge do
= form.radio_button :merge_method, :merge, class: "js-merge-method-radio"
%strong Merge commit
%br
%span.descr
A merge commit is created for every merge, and merging is allowed as long as there are no conflicts.
= render 'merge_request_rebase_settings', form: form
= render 'merge_request_fast_forward_settings', project: @project, form: form
= render 'projects/merge_request_merge_settings', form: form = render 'projects/merge_request_merge_settings', form: form
...@@ -95,7 +95,7 @@ ...@@ -95,7 +95,7 @@
= render 'shared/promotions/promote_mr_features' = render 'shared/promotions/promote_mr_features'
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form" }, authenticity_token: true do |f| = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form" }, authenticity_token: true do |f|
= render 'merge_request_settings', form: f = render 'projects/ee/merge_request_settings', form: f
= f.submit 'Save changes', class: "btn btn-save" = f.submit 'Save changes', class: "btn btn-save"
= render 'projects/ee/service_desk_settings' = render 'projects/ee/service_desk_settings'
......
- page_title "Edit", "#{@issue.title} (#{@issue.to_reference})", "Issues"
%h3.page-title
Edit Issue ##{@issue.iid}
%hr
= render "form"
- access = note_max_access_for_user(note) - access = note_max_access_for_user(note)
- if note.has_special_role?(Note::SpecialRole::FIRST_TIME_CONTRIBUTOR) - if note.has_special_role?(Note::SpecialRole::FIRST_TIME_CONTRIBUTOR)
%span.note-role.note-role-special.has-tooltip{ title: _("This is the author's first Merge Request to this project. Handle with care.") } %span.note-role.note-role-special.has-tooltip{ title: _("This is the author's first Merge Request to this project.") }
= issuable_first_contribution_icon = issuable_first_contribution_icon
- if access.nonzero? - if access.nonzero?
%span.note-role.note-role-access= Gitlab::Access.human_access(access) %span.note-role.note-role-access= Gitlab::Access.human_access(access)
......
.tree-ref-container .tree-ref-container
.tree-ref-holder .tree-ref-holder
= render 'shared/ref_switcher', destination: 'tree', path: @path = render 'shared/ref_switcher', destination: 'tree', path: @path
- if show_new_repo?
.tree-ref-target-holder.js-tree-ref-target-holder
= icon('long-arrow-right', title: 'to target branch')
= render 'shared/target_switcher', destination: 'tree', path: @path
- unless show_new_repo? - unless show_new_repo?
= render 'projects/tree/old_tree_header' = render 'projects/tree/old_tree_header'
......
- dropdown_toggle_text = @ref || @project.default_branch
= form_tag nil, method: :get, class: "project-refs-form project-refs-target-form" do
= hidden_field_tag :destination, destination
- if defined?(path)
= hidden_field_tag :path, path
- @options && @options.each do |key, value|
= hidden_field_tag key, value, id: nil
.dropdown
= dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_project_path(@project, find: ['branches']), field_name: 'ref', input_field_name: 'new-branch', submit_form_on_click: true, visit: false }, { toggle_class: "js-project-refs-dropdown" }
.dropdown-menu.dropdown-menu-selectable.git-revision-dropdown{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) }
= dropdown_title _("Create a new branch")
= dropdown_input _("Create a new branch")
= dropdown_title _("Select existing branch"), options: {close: false}
= dropdown_filter _("Search branches and tags")
= dropdown_content
= dropdown_loading
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
- case purchase_link.action - case purchase_link.action
- when 'downgrade' - when 'downgrade'
= plan_purchase_link(href, s_("Billinglans|Downgrade")) = plan_purchase_link(href, s_("BillingPlans|Downgrade"))
- when 'current_plan' - when 'current_plan'
= plan_purchase_link(href, s_("BillingPlans|Current plan")) = plan_purchase_link(href, s_("BillingPlans|Current plan"))
- when 'upgrade' - when 'upgrade'
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
- button_method = issuable_close_reopen_button_method(issuable) - button_method = issuable_close_reopen_button_method(issuable)
- if can_update && is_current_user - if can_update && is_current_user
= link_to "Close #{display_issuable_type}", close_issuable_url(issuable), method: button_method, = link_to "Close #{display_issuable_type}", close_issuable_path(issuable), method: button_method,
class: "hidden-xs hidden-sm btn btn-grouped btn-close js-btn-issue-action #{issuable_button_visibility(issuable, true)}", title: "Close #{display_issuable_type}" class: "hidden-xs hidden-sm btn btn-grouped btn-close js-btn-issue-action #{issuable_button_visibility(issuable, true)}", title: "Close #{display_issuable_type}"
= link_to "Reopen #{display_issuable_type}", reopen_issuable_url(issuable), method: button_method, = link_to "Reopen #{display_issuable_type}", reopen_issuable_path(issuable), method: button_method,
class: "hidden-xs hidden-sm btn btn-grouped btn-reopen js-btn-issue-action #{issuable_button_visibility(issuable, false)}", title: "Reopen #{display_issuable_type}" class: "hidden-xs hidden-sm btn btn-grouped btn-reopen js-btn-issue-action #{issuable_button_visibility(issuable, false)}", title: "Reopen #{display_issuable_type}"
- elsif can_update && !is_current_user - elsif can_update && !is_current_user
= render 'shared/issuable/close_reopen_report_toggle', issuable: issuable = render 'shared/issuable/close_reopen_report_toggle', issuable: issuable
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- button_method = issuable_close_reopen_button_method(issuable) - button_method = issuable_close_reopen_button_method(issuable)
.pull-left.btn-group.prepend-left-10.issuable-close-dropdown.droplab-dropdown.js-issuable-close-dropdown .pull-left.btn-group.prepend-left-10.issuable-close-dropdown.droplab-dropdown.js-issuable-close-dropdown
= link_to "#{display_button_action} #{display_issuable_type}", close_reopen_issuable_url(issuable), = link_to "#{display_button_action} #{display_issuable_type}", close_reopen_issuable_path(issuable),
method: button_method, class: "#{button_class} btn-#{button_action}", title: "#{display_button_action} #{display_issuable_type}" method: button_method, class: "#{button_class} btn-#{button_action}", title: "#{display_button_action} #{display_issuable_type}"
= button_tag type: 'button', class: "#{toggle_class} btn-#{button_action}-color", = button_tag type: 'button', class: "#{toggle_class} btn-#{button_action}-color",
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
%ul#issuable-close-menu.js-issuable-close-menu.dropdown-menu{ class: button_responsive_class, data: { dropdown: true } } %ul#issuable-close-menu.js-issuable-close-menu.dropdown-menu{ class: button_responsive_class, data: { dropdown: true } }
%li.close-item{ class: "#{issuable_button_visibility(issuable, true) || 'droplab-item-selected'}", %li.close-item{ class: "#{issuable_button_visibility(issuable, true) || 'droplab-item-selected'}",
data: { text: "Close #{display_issuable_type}", url: close_issuable_url(issuable), data: { text: "Close #{display_issuable_type}", url: close_issuable_path(issuable),
button_class: "#{button_class} btn-close", toggle_class: "#{toggle_class} btn-close-color", method: button_method } } button_class: "#{button_class} btn-close", toggle_class: "#{toggle_class} btn-close-color", method: button_method } }
%button.btn.btn-transparent %button.btn.btn-transparent
= icon('check', class: 'icon') = icon('check', class: 'icon')
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
= display_issuable_type = display_issuable_type
%li.reopen-item{ class: "#{issuable_button_visibility(issuable, false) || 'droplab-item-selected'}", %li.reopen-item{ class: "#{issuable_button_visibility(issuable, false) || 'droplab-item-selected'}",
data: { text: "Reopen #{display_issuable_type}", url: reopen_issuable_url(issuable), data: { text: "Reopen #{display_issuable_type}", url: reopen_issuable_path(issuable),
button_class: "#{button_class} btn-reopen", toggle_class: "#{toggle_class} btn-reopen-color", method: button_method } } button_class: "#{button_class} btn-reopen", toggle_class: "#{toggle_class} btn-reopen-color", method: button_method } }
%button.btn.btn-transparent %button.btn.btn-transparent
= icon('check', class: 'icon') = icon('check', class: 'icon')
......
...@@ -16,7 +16,7 @@ class StuckImportJobsWorker ...@@ -16,7 +16,7 @@ class StuckImportJobsWorker
private private
def mark_projects_without_jid_as_failed! def mark_projects_without_jid_as_failed!
started_projects_without_jid.each do |project| enqueued_projects_without_jid.each do |project|
project.mark_import_as_failed(error_message) project.mark_import_as_failed(error_message)
end.count end.count
end end
...@@ -24,7 +24,7 @@ class StuckImportJobsWorker ...@@ -24,7 +24,7 @@ class StuckImportJobsWorker
def mark_projects_with_jid_as_failed! def mark_projects_with_jid_as_failed!
completed_jids_count = 0 completed_jids_count = 0
started_projects_with_jid.find_in_batches(batch_size: 500) do |group| enqueued_projects_with_jid.find_in_batches(batch_size: 500) do |group|
jids = group.map(&:import_jid) jids = group.map(&:import_jid)
# Find the jobs that aren't currently running or that exceeded the threshold. # Find the jobs that aren't currently running or that exceeded the threshold.
...@@ -43,16 +43,16 @@ class StuckImportJobsWorker ...@@ -43,16 +43,16 @@ class StuckImportJobsWorker
completed_jids_count completed_jids_count
end end
def started_projects def enqueued_projects
Project.with_import_status(:started) Project.with_import_status(:scheduled, :started)
end end
def started_projects_with_jid def enqueued_projects_with_jid
started_projects.where.not(import_jid: nil) enqueued_projects.where.not(import_jid: nil)
end end
def started_projects_without_jid def enqueued_projects_without_jid
started_projects.where(import_jid: nil) enqueued_projects.where(import_jid: nil)
end end
def error_message def error_message
......
---
title: Adds hoverstates for collapsed Issue/Merge Request sidebar for Time tracking Icon
merge_request:
author:
---
title: Fix a Geo node validation, preventing admins from locking themselves out
merge_request: 3040
author:
type: fixed
---
title: Find stuck scheduled import jobs and also mark them as failed.
merge_request: 3055
author:
type: fixed
---
title: Fix EE delta size check handling with annotated tags
merge_request:
author:
type: fixed
---
title: "Fix v3 api project_hooks POST and PUT operations for build_events"
merge_request: 12673
author: Richard Clamp
---
title: "reset text-align to initial to let elements with dir="auto" align texts to right in RTL languages ( default is left )"
merge_request: 12892
author: goshhob
---
title: Confirmation email shows link as text instead of human readable text
merge_request: 14243
author: bitsapien
type: changed
...@@ -2,5 +2,5 @@ ...@@ -2,5 +2,5 @@
title: Allow to use same periods for different housekeeping tasks (effectively title: Allow to use same periods for different housekeeping tasks (effectively
skipping the lesser task) skipping the lesser task)
merge_request: 13711 merge_request: 13711
author: @cernvcs author: cernvcs
type: added type: added
---
title: Handle unsubscribe from email notifications via replying to reply+%{key}+unsubscribe@ address
merge_request: 6597
author:
---
title: Refactor Timelogs structure to use foreign keys.
merge_request: 8769
author:
---
title: "Correction to documention for manual steps on the Jobs API"
merge_request: 11411
author: Zac Sturgess
\ No newline at end of file
---
title: "Fix API to serve binary diffs that are treated as text."
merge_request: 14038
---
title: Remove the ability to visit the issue edit form directly
merge_request: 14523
author:
type: removed
--- ---
title: Add GitLab-Pages version to Admin Dashboard title: Add GitLab-Pages version to Admin Dashboard
merge_request: 14040 merge_request: 14040
author: @travismiller author: travismiller
type: added type: added
---
title: Add active states to nav bar counters
merge_request:
author:
type: changed
---
title: Fix 500 error on merged merge requests when GitLab is restored from a backup
merge_request:
author:
type: fixed
---
title: Fix notes type created from import. This should fix some missing notes issues
from imported projects
merge_request: 14524
author:
type: fixed
---
title: Adjust MRs being stuck on "process of being merged" for more than 2 hours
merge_request:
author:
type: fixed
---
title: Fixes data parameter not being sent in ajax request for jobs log
merge_request:
author:
type: fixed
---
title: Add index for merge_requests.merge_commit_sha
merge_request:
author:
type: other
---
title: Added mock deployment and monitoring service with environments fixtures
merge_request:
author:
---
title: Fixed issue/merge request breadcrumb titles not having links
merge_request:
author:
type: fixed
---
title: Fix CSRF validation issue when closing/opening merge requests from the UI
merge_request: 14555
author:
type: fixed
---
title: Fixed commenting on side-by-side commit diff
merge_request:
author:
type: fixed
---
title: Make sure API responds with 401 when invalid authentication info is provided
merge_request:
author:
type: fixed
---
title: Clarify artifact download via the API only accepts branch or tag name for ref
merge_request:
author:
type: other
---
title: Change recommended MySQL version to 5.6
merge_request:
author:
type: other
---
title: Support custom attributes on users
merge_request: 13038
author: Markus Koller
---
title: Fix merge request counter updates after merge
merge_request:
author:
type: fixed
---
title: Detect when changelog entries are invalid
merge_request:
author:
type: other
---
title: Added ability to put emojis into repository name
merge_request: 7420
author: Vincent Composieux
---
title: Fix profile image orientation based on EXIF data gvieira37
merge_request: 14461
author: gvieira37
type: fixed
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment