summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-11 18:06:15 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-11 18:06:15 +0000
commit0dfbcd8f8b1587a7e10eb79940a8dc13bd72c664 (patch)
tree769b7b5eaea4354498ca0b91945e4733895bba43 /app
parentcd631619f465a0eee2fe714e720f6b6312dd3e56 (diff)
downloadgitlab-ce-0dfbcd8f8b1587a7e10eb79940a8dc13bd72c664.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue20
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue218
-rw-r--r--app/assets/javascripts/boards/constants.js11
-rw-r--r--app/assets/javascripts/boards/index.js18
-rw-r--r--app/assets/javascripts/boards/models/list.js92
-rw-r--r--app/assets/javascripts/boards/services/board_service.js10
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js148
-rw-r--r--app/assets/javascripts/test_utils/index.js2
-rw-r--r--app/assets/stylesheets/pages/boards.scss6
-rw-r--r--app/assets/stylesheets/pages/help.scss1
-rw-r--r--app/controllers/groups/boards_controller.rb3
-rw-r--r--app/controllers/projects/boards_controller.rb3
-rw-r--r--app/models/concerns/atomic_internal_id.rb7
-rw-r--r--app/services/git/process_ref_changes_service.rb57
-rw-r--r--app/services/issues/update_service.rb2
-rw-r--r--app/services/merge_requests/update_service.rb3
-rw-r--r--app/services/notes/update_service.rb2
-rw-r--r--app/views/help/show.html.haml2
-rw-r--r--app/workers/post_receive.rb21
19 files changed, 92 insertions, 534 deletions
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 12d68256598..faf722f61af 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -42,19 +42,12 @@ export default {
return {
showDetail: false,
detailIssue: boardsStore.detail,
- multiSelect: boardsStore.multiSelect,
};
},
computed: {
issueDetailVisible() {
return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
},
- multiSelectVisible() {
- return this.multiSelect.list.findIndex(issue => issue.id === this.issue.id) > -1;
- },
- canMultiSelect() {
- return gon.features && gon.features.multiSelectBoard;
- },
},
methods: {
mouseDown() {
@@ -65,20 +58,14 @@ export default {
},
showIssue(e) {
if (e.target.classList.contains('js-no-trigger')) return;
+
if (this.showDetail) {
this.showDetail = false;
- // If CMD or CTRL is clicked
- const isMultiSelect = this.canMultiSelect && (e.ctrlKey || e.metaKey);
-
if (boardsStore.detail.issue && boardsStore.detail.issue.id === this.issue.id) {
- eventHub.$emit('clearDetailIssue', isMultiSelect);
-
- if (isMultiSelect) {
- eventHub.$emit('newDetailIssue', this.issue, isMultiSelect);
- }
+ eventHub.$emit('clearDetailIssue');
} else {
- eventHub.$emit('newDetailIssue', this.issue, isMultiSelect);
+ eventHub.$emit('newDetailIssue', this.issue);
boardsStore.setListDetail(this.list);
}
}
@@ -90,7 +77,6 @@ export default {
<template>
<li
:class="{
- 'multi-select': multiSelectVisible,
'user-can-drag': !disabled && issue.id,
'is-disabled': disabled || !issue.id,
'is-active': issueDetailVisible,
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 1273fcc6a91..de41698ca04 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -1,22 +1,12 @@
<script>
-import { Sortable, MultiDrag } from 'sortablejs';
+/* eslint-disable @gitlab/vue-i18n/no-bare-strings */
+import Sortable from 'sortablejs';
import { GlLoadingIcon } from '@gitlab/ui';
-import _ from 'underscore';
import boardNewIssue from './board_new_issue.vue';
import boardCard from './board_card.vue';
import eventHub from '../eventhub';
import boardsStore from '../stores/boards_store';
-import { sprintf, __ } from '~/locale';
-import createFlash from '~/flash';
-import {
- getBoardSortableDefaultOptions,
- sortableStart,
- sortableEnd,
-} from '../mixins/sortable_default_options';
-
-if (gon.features && gon.features.multiSelectBoard) {
- Sortable.mount(new MultiDrag());
-}
+import { getBoardSortableDefaultOptions, sortableStart } from '../mixins/sortable_default_options';
export default {
name: 'BoardList',
@@ -64,14 +54,6 @@ export default {
showIssueForm: false,
};
},
- computed: {
- paginatedIssueText() {
- return sprintf(__('Showing %{pageSize} of %{total} issues'), {
- pageSize: this.list.issues.length,
- total: this.list.issuesSize,
- });
- },
- },
watch: {
filters: {
handler() {
@@ -105,20 +87,11 @@ export default {
eventHub.$on(`scroll-board-list-${this.list.id}`, this.scrollToTop);
},
mounted() {
- const multiSelectOpts = {};
- if (gon.features && gon.features.multiSelectBoard) {
- multiSelectOpts.multiDrag = true;
- multiSelectOpts.selectedClass = 'js-multi-select';
- multiSelectOpts.animation = 500;
- }
-
const options = getBoardSortableDefaultOptions({
scroll: true,
disabled: this.disabled,
filter: '.board-list-count, .is-disabled',
dataIdAttr: 'data-issue-id',
- removeCloneOnHide: false,
- ...multiSelectOpts,
group: {
name: 'issues',
/**
@@ -172,66 +145,25 @@ export default {
card.showDetail = false;
const { list } = card;
-
const issue = list.findIssue(Number(e.item.dataset.issueId));
-
boardsStore.startMoving(list, issue);
sortableStart();
},
onAdd: e => {
- const { items = [], newIndicies = [] } = e;
- if (items.length) {
- // Not using e.newIndex here instead taking a min of all
- // the newIndicies. Basically we have to find that during
- // a drop what is the index we're going to start putting
- // all the dropped elements from.
- const newIndex = Math.min(...newIndicies.map(obj => obj.index).filter(i => i !== -1));
- const issues = items.map(item =>
- boardsStore.moving.list.findIssue(Number(item.dataset.issueId)),
- );
+ boardsStore.moveIssueToList(
+ boardsStore.moving.list,
+ this.list,
+ boardsStore.moving.issue,
+ e.newIndex,
+ );
- boardsStore.moveMultipleIssuesToList({
- listFrom: boardsStore.moving.list,
- listTo: this.list,
- issues,
- newIndex,
- });
- } else {
- boardsStore.moveIssueToList(
- boardsStore.moving.list,
- this.list,
- boardsStore.moving.issue,
- e.newIndex,
- );
- this.$nextTick(() => {
- e.item.remove();
- });
- }
+ this.$nextTick(() => {
+ e.item.remove();
+ });
},
onUpdate: e => {
const sortedArray = this.sortable.toArray().filter(id => id !== '-1');
-
- const { items = [], newIndicies = [], oldIndicies = [] } = e;
- if (items.length) {
- const newIndex = Math.min(...newIndicies.map(obj => obj.index));
- const issues = items.map(item =>
- boardsStore.moving.list.findIssue(Number(item.dataset.issueId)),
- );
- boardsStore.moveMultipleIssuesInList({
- list: this.list,
- issues,
- oldIndicies: oldIndicies.map(obj => obj.index),
- newIndex,
- idArray: sortedArray,
- });
- e.items.forEach(el => {
- Sortable.utils.deselect(el);
- });
- boardsStore.clearMultiSelect();
- return;
- }
-
boardsStore.moveIssueInList(
this.list,
boardsStore.moving.issue,
@@ -240,133 +172,9 @@ export default {
sortedArray,
);
},
- onEnd: e => {
- const { items = [], clones = [], to } = e;
-
- // This is not a multi select operation
- if (!items.length && !clones.length) {
- sortableEnd();
- return;
- }
-
- let toList;
- if (to) {
- const containerEl = to.closest('.js-board-list');
- toList = boardsStore.findList('id', Number(containerEl.dataset.board));
- }
-
- /**
- * onEnd is called irrespective if the cards were moved in the
- * same list or the other list. Don't remove items if it's same list.
- */
- const isSameList = toList && toList.id === this.list.id;
-
- if (toList && !isSameList && boardsStore.shouldRemoveIssue(this.list, toList)) {
- const issues = items.map(item => this.list.findIssue(Number(item.dataset.issueId)));
-
- if (_.compact(issues).length && !boardsStore.issuesAreContiguous(this.list, issues)) {
- const indexes = [];
- const ids = this.list.issues.map(i => i.id);
- issues.forEach(issue => {
- const index = ids.indexOf(issue.id);
- if (index > -1) {
- indexes.push(index);
- }
- });
-
- // Descending sort because splice would cause index discrepancy otherwise
- const sortedIndexes = indexes.sort((a, b) => (a < b ? 1 : -1));
-
- sortedIndexes.forEach(i => {
- /**
- * **setTimeout and splice each element one-by-one in a loop
- * is intended.**
- *
- * The problem here is all the indexes are in the list but are
- * non-contiguous. Due to that, when we splice all the indexes,
- * at once, Vue -- during a re-render -- is unable to find reference
- * nodes and the entire app crashes.
- *
- * If the indexes are contiguous, this piece of code is not
- * executed. If it is, this is a possible regression. Only when
- * issue indexes are far apart, this logic should ever kick in.
- */
- setTimeout(() => {
- this.list.issues.splice(i, 1);
- }, 0);
- });
- }
- }
-
- if (!toList) {
- createFlash(__('Something went wrong while performing the action.'));
- }
-
- if (!isSameList) {
- boardsStore.clearMultiSelect();
-
- // Since Vue's list does not re-render the same keyed item, we'll
- // remove `multi-select` class to express it's unselected
- if (clones && clones.length) {
- clones.forEach(el => el.classList.remove('multi-select'));
- }
-
- // Due to some bug which I am unable to figure out
- // Sortable does not deselect some pending items from the
- // source list.
- // We'll just do it forcefully here.
- Array.from(document.querySelectorAll('.js-multi-select') || []).forEach(item => {
- Sortable.utils.deselect(item);
- });
-
- /**
- * SortableJS leaves all the moving items "as is" on the DOM.
- * Vue picks up and rehydrates the DOM, but we need to explicity
- * remove the "trash" items from the DOM.
- *
- * This is in parity to the logic on single item move from a list/in
- * a list. For reference, look at the implementation of onAdd method.
- */
- this.$nextTick(() => {
- if (items && items.length) {
- items.forEach(item => {
- item.remove();
- });
- }
- });
- }
- sortableEnd();
- },
onMove(e) {
return !e.related.classList.contains('board-list-count');
},
- onSelect(e) {
- const {
- item: { classList },
- } = e;
-
- if (
- classList &&
- classList.contains('js-multi-select') &&
- !classList.contains('multi-select')
- ) {
- Sortable.utils.deselect(e.item);
- }
- },
- onDeselect: e => {
- const {
- item: { dataset, classList },
- } = e;
-
- if (
- classList &&
- classList.contains('multi-select') &&
- !classList.contains('js-multi-select')
- ) {
- const issue = this.list.findIssue(Number(dataset.issueId));
- boardsStore.toggleMultiSelect(issue);
- }
- },
});
this.sortable = Sortable.create(this.$refs.list, options);
@@ -452,7 +260,7 @@ export default {
<li v-if="showCount" class="board-list-count text-center" data-issue-id="-1">
<gl-loading-icon v-show="list.loadingMore" label="Loading more issues" />
<span v-if="list.issues.length === list.issuesSize">{{ __('Showing all issues') }}</span>
- <span v-else>{{ paginatedIssueText }}</span>
+ <span v-else> Showing {{ list.issues.length }} of {{ list.issuesSize }} issues </span>
</li>
</ul>
</div>
diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js
deleted file mode 100644
index 3c66c7a0660..00000000000
--- a/app/assets/javascripts/boards/constants.js
+++ /dev/null
@@ -1,11 +0,0 @@
-export const ListType = {
- assignee: 'assignee',
- milestone: 'milestone',
- backlog: 'backlog',
- closed: 'closed',
- label: 'label',
-};
-
-export default {
- ListType,
-};
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index befca70eeae..da2669e7cde 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -146,7 +146,7 @@ export default () => {
updateTokens() {
this.filterManager.updateTokens();
},
- updateDetailIssue(newIssue, multiSelect = false) {
+ updateDetailIssue(newIssue) {
const { sidebarInfoEndpoint } = newIssue;
if (sidebarInfoEndpoint && newIssue.subscribed === undefined) {
newIssue.setFetchingState('subscriptions', true);
@@ -185,23 +185,9 @@ export default () => {
});
}
- if (multiSelect) {
- boardsStore.toggleMultiSelect(newIssue);
-
- if (boardsStore.detail.issue) {
- boardsStore.clearDetailIssue();
- return;
- }
-
- return;
- }
-
boardsStore.setIssueDetail(newIssue);
},
- clearDetailIssue(multiSelect = false) {
- if (multiSelect) {
- boardsStore.clearMultiSelect();
- }
+ clearDetailIssue() {
boardsStore.clearDetailIssue();
},
toggleSubscription(id) {
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 1e213c324eb..b3e56a34c28 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -5,7 +5,6 @@ import ListLabel from './label';
import ListAssignee from './assignee';
import ListIssue from 'ee_else_ce/boards/models/issue';
import { urlParamsToObject } from '~/lib/utils/common_utils';
-import flash from '~/flash';
import boardsStore from '../stores/boards_store';
import ListMilestone from './milestone';
@@ -177,53 +176,6 @@ class List {
});
}
- addMultipleIssues(issues, listFrom, newIndex) {
- let moveBeforeId = null;
- let moveAfterId = null;
-
- const listHasIssues = issues.every(issue => this.findIssue(issue.id));
-
- if (!listHasIssues) {
- if (newIndex !== undefined) {
- if (this.issues[newIndex - 1]) {
- moveBeforeId = this.issues[newIndex - 1].id;
- }
-
- if (this.issues[newIndex]) {
- moveAfterId = this.issues[newIndex].id;
- }
-
- this.issues.splice(newIndex, 0, ...issues);
- } else {
- this.issues.push(...issues);
- }
-
- if (this.label) {
- issues.forEach(issue => issue.addLabel(this.label));
- }
-
- if (this.assignee) {
- if (listFrom && listFrom.type === 'assignee') {
- issues.forEach(issue => issue.removeAssignee(listFrom.assignee));
- }
- issues.forEach(issue => issue.addAssignee(this.assignee));
- }
-
- if (IS_EE && this.milestone) {
- if (listFrom && listFrom.type === 'milestone') {
- issues.forEach(issue => issue.removeMilestone(listFrom.milestone));
- }
- issues.forEach(issue => issue.addMilestone(this.milestone));
- }
-
- if (listFrom) {
- this.issuesSize += issues.length;
-
- this.updateMultipleIssues(issues, listFrom, moveBeforeId, moveAfterId);
- }
- }
- }
-
addIssue(issue, listFrom, newIndex) {
let moveBeforeId = null;
let moveAfterId = null;
@@ -278,23 +230,6 @@ class List {
});
}
- moveMultipleIssues({ issues, oldIndicies, newIndex, moveBeforeId, moveAfterId }) {
- oldIndicies.reverse().forEach(index => {
- this.issues.splice(index, 1);
- });
- this.issues.splice(newIndex, 0, ...issues);
-
- gl.boardService
- .moveMultipleIssues({
- ids: issues.map(issue => issue.id),
- fromListId: null,
- toListId: null,
- moveBeforeId,
- moveAfterId,
- })
- .catch(() => flash(__('Something went wrong while moving issues.')));
- }
-
updateIssueLabel(issue, listFrom, moveBeforeId, moveAfterId) {
gl.boardService
.moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId)
@@ -303,37 +238,10 @@ class List {
});
}
- updateMultipleIssues(issues, listFrom, moveBeforeId, moveAfterId) {
- gl.boardService
- .moveMultipleIssues({
- ids: issues.map(issue => issue.id),
- fromListId: listFrom.id,
- toListId: this.id,
- moveBeforeId,
- moveAfterId,
- })
- .catch(() => flash(__('Something went wrong while moving issues.')));
- }
-
findIssue(id) {
return this.issues.find(issue => issue.id === id);
}
- removeMultipleIssues(removeIssues) {
- const ids = removeIssues.map(issue => issue.id);
-
- this.issues = this.issues.filter(issue => {
- const matchesRemove = ids.includes(issue.id);
-
- if (matchesRemove) {
- this.issuesSize -= 1;
- issue.removeLabel(this.label);
- }
-
- return !matchesRemove;
- });
- }
-
removeIssue(removeIssue) {
this.issues = this.issues.filter(issue => {
const matchesRemove = removeIssue.id === issue.id;
diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js
index 03369febb4a..0d11db89511 100644
--- a/app/assets/javascripts/boards/services/board_service.js
+++ b/app/assets/javascripts/boards/services/board_service.js
@@ -48,16 +48,6 @@ export default class BoardService {
return boardsStore.moveIssue(id, fromListId, toListId, moveBeforeId, moveAfterId);
}
- moveMultipleIssues({
- ids,
- fromListId = null,
- toListId = null,
- moveBeforeId = null,
- moveAfterId = null,
- }) {
- return boardsStore.moveMultipleIssues({ ids, fromListId, toListId, moveBeforeId, moveAfterId });
- }
-
newIssue(id, issue) {
return boardsStore.newIssue(id, issue);
}
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 8b737d1dab0..6da1cca9628 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -11,7 +11,6 @@ import { __ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import eventHub from '../eventhub';
-import { ListType } from '../constants';
const boardsStore = {
disabled: false,
@@ -40,7 +39,6 @@ const boardsStore = {
issue: {},
list: {},
},
- multiSelect: { list: [] },
setEndpoints({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId, recentBoardsEndpoint }) {
const listsEndpointGenerate = `${listsEndpoint}/generate.json`;
@@ -53,6 +51,7 @@ const boardsStore = {
recentBoardsEndpoint: `${recentBoardsEndpoint}.json`,
};
},
+
create() {
this.state.lists = [];
this.filter.path = getUrlParamsArray().join('&');
@@ -135,107 +134,6 @@ const boardsStore = {
Object.assign(this.moving, { list, issue });
},
- moveMultipleIssuesToList({ listFrom, listTo, issues, newIndex }) {
- const issueTo = issues.map(issue => listTo.findIssue(issue.id));
- const issueLists = _.flatten(issues.map(issue => issue.getLists()));
- const listLabels = issueLists.map(list => list.label);
-
- const hasMoveableIssues = _.compact(issueTo).length > 0;
-
- if (!hasMoveableIssues) {
- // Check if target list assignee is already present in this issue
- if (
- listTo.type === ListType.assignee &&
- listFrom.type === ListType.assignee &&
- issues.some(issue => issue.findAssignee(listTo.assignee))
- ) {
- const targetIssues = issues.map(issue => listTo.findIssue(issue.id));
- targetIssues.forEach(targetIssue => targetIssue.removeAssignee(listFrom.assignee));
- } else if (listTo.type === 'milestone') {
- const currentMilestones = issues.map(issue => issue.milestone);
- const currentLists = this.state.lists
- .filter(list => list.type === 'milestone' && list.id !== listTo.id)
- .filter(list =>
- list.issues.some(listIssue => issues.some(issue => listIssue.id === issue.id)),
- );
-
- issues.forEach(issue => {
- currentMilestones.forEach(milestone => {
- issue.removeMilestone(milestone);
- });
- });
-
- issues.forEach(issue => {
- issue.addMilestone(listTo.milestone);
- });
-
- currentLists.forEach(currentList => {
- issues.forEach(issue => {
- currentList.removeIssue(issue);
- });
- });
-
- listTo.addMultipleIssues(issues, listFrom, newIndex);
- } else {
- // Add to new lists issues if it doesn't already exist
- listTo.addMultipleIssues(issues, listFrom, newIndex);
- }
- } else {
- listTo.updateMultipleIssues(issues, listFrom);
- issues.forEach(issue => {
- issue.removeLabel(listFrom.label);
- });
- }
-
- if (listTo.type === ListType.closed && listFrom.type !== ListType.backlog) {
- issueLists.forEach(list => {
- issues.forEach(issue => {
- list.removeIssue(issue);
- });
- });
-
- issues.forEach(issue => {
- issue.removeLabels(listLabels);
- });
- } else if (listTo.type === ListType.backlog && listFrom.type === ListType.assignee) {
- issues.forEach(issue => {
- issue.removeAssignee(listFrom.assignee);
- });
- issueLists.forEach(list => {
- issues.forEach(issue => {
- list.removeIssue(issue);
- });
- });
- } else if (listTo.type === ListType.backlog && listFrom.type === ListType.milestone) {
- issues.forEach(issue => {
- issue.removeMilestone(listFrom.milestone);
- });
- issueLists.forEach(list => {
- issues.forEach(issue => {
- list.removeIssue(issue);
- });
- });
- } else if (
- this.shouldRemoveIssue(listFrom, listTo) &&
- this.issuesAreContiguous(listFrom, issues)
- ) {
- listFrom.removeMultipleIssues(issues);
- }
- },
-
- issuesAreContiguous(list, issues) {
- // When there's only 1 issue selected, we can return early.
- if (issues.length === 1) return true;
-
- // Create list of ids for issues involved.
- const listIssueIds = list.issues.map(issue => issue.id);
- const movedIssueIds = issues.map(issue => issue.id);
-
- // Check if moved issue IDs is sub-array
- // of source list issue IDs (i.e. contiguous selection).
- return listIssueIds.join('|').includes(movedIssueIds.join('|'));
- },
-
moveIssueToList(listFrom, listTo, issue, newIndex) {
const issueTo = listTo.findIssue(issue.id);
const issueLists = issue.getLists();
@@ -297,17 +195,6 @@ const boardsStore = {
list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId);
},
- moveMultipleIssuesInList({ list, issues, oldIndicies, newIndex, idArray }) {
- const beforeId = parseInt(idArray[newIndex - 1], 10) || null;
- const afterId = parseInt(idArray[newIndex + issues.length], 10) || null;
- list.moveMultipleIssues({
- issues,
- oldIndicies,
- newIndex,
- moveBeforeId: beforeId,
- moveAfterId: afterId,
- });
- },
findList(key, val, type = 'label') {
const filteredList = this.state.lists.filter(list => {
const byType = type
@@ -373,10 +260,6 @@ const boardsStore = {
}`;
},
- generateMultiDragPath(boardId) {
- return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues/bulk_move`;
- },
-
all() {
return axios.get(this.state.endpoints.listsEndpoint);
},
@@ -426,16 +309,6 @@ const boardsStore = {
});
},
- moveMultipleIssues({ ids, fromListId, toListId, moveBeforeId, moveAfterId }) {
- return axios.put(this.generateMultiDragPath(this.state.endpoints.boardId), {
- from_list_id: fromListId,
- to_list_id: toListId,
- move_before_id: moveBeforeId,
- move_after_id: moveAfterId,
- ids,
- });
- },
-
newIssue(id, issue) {
return axios.post(this.generateIssuesPath(id), {
issue,
@@ -506,25 +379,6 @@ const boardsStore = {
setCurrentBoard(board) {
this.state.currentBoard = board;
},
-
- toggleMultiSelect(issue) {
- const selectedIssueIds = this.multiSelect.list.map(issue => issue.id);
- const index = selectedIssueIds.indexOf(issue.id);
-
- if (index === -1) {
- this.multiSelect.list.push(issue);
- return;
- }
-
- this.multiSelect.list = [
- ...this.multiSelect.list.slice(0, index),
- ...this.multiSelect.list.slice(index + 1),
- ];
- },
-
- clearMultiSelect() {
- this.multiSelect.list = [];
- },
};
BoardsStoreEE.initEESpecific(boardsStore);
diff --git a/app/assets/javascripts/test_utils/index.js b/app/assets/javascripts/test_utils/index.js
index 1a1f3e8d0a8..1e75ee60671 100644
--- a/app/assets/javascripts/test_utils/index.js
+++ b/app/assets/javascripts/test_utils/index.js
@@ -1,10 +1,8 @@
import 'core-js/es/map';
import 'core-js/es/set';
-import { Sortable } from 'sortablejs';
import simulateDrag from './simulate_drag';
import simulateInput from './simulate_input';
// Export to global space for rspec to use
window.simulateDrag = simulateDrag;
window.simulateInput = simulateInput;
-window.Sortable = Sortable;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 2a7a53d8bd7..d540a347dde 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -245,7 +245,6 @@
box-shadow: 0 1px 2px $issue-boards-card-shadow;
line-height: $gl-padding;
list-style: none;
- position: relative;
&:not(:last-child) {
margin-bottom: $gl-padding-8;
@@ -256,11 +255,6 @@
background-color: $blue-50;
}
- &.multi-select {
- border-color: $blue-200;
- background-color: $blue-50;
- }
-
.badge {
border: 0;
outline: 0;
diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss
index ef872e693e0..ab281bc7f23 100644
--- a/app/assets/stylesheets/pages/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -37,6 +37,7 @@
.documentation {
padding: 7px;
+ font-size: $gl-font-size-large;
}
.card.links-card {
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index 3c86f3108ab..40b8d5ed72c 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -5,9 +5,6 @@ class Groups::BoardsController < Groups::ApplicationController
include RecordUserLastActivity
before_action :assign_endpoint_vars
- before_action do
- push_frontend_feature_flag(:multi_select_board)
- end
private
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index 3b335fa4af4..14b02993e6e 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -7,9 +7,6 @@ class Projects::BoardsController < Projects::ApplicationController
before_action :check_issues_available!
before_action :authorize_read_board!, only: [:index, :show]
before_action :assign_endpoint_vars
- before_action do
- push_frontend_feature_flag(:multi_select_board)
- end
private
diff --git a/app/models/concerns/atomic_internal_id.rb b/app/models/concerns/atomic_internal_id.rb
index b510129b35d..7a7e485a95a 100644
--- a/app/models/concerns/atomic_internal_id.rb
+++ b/app/models/concerns/atomic_internal_id.rb
@@ -60,12 +60,11 @@ module AtomicInternalId
iid_always_track = Feature.enabled?(:iid_always_track, default_enabled: true)
return unless @internal_id_needs_tracking || iid_always_track
- @internal_id_needs_tracking = false
-
scope_value = internal_id_read_scope(scope)
- value = read_attribute(column)
return unless scope_value
+ value = read_attribute(column)
+
if value.present?
# The value was set externally, e.g. by the user
# We update the InternalId record to keep track of the greatest value.
@@ -75,6 +74,8 @@ module AtomicInternalId
internal_id_scope_usage,
value,
init)
+
+ @internal_id_needs_tracking = false
end
end
diff --git a/app/services/git/process_ref_changes_service.rb b/app/services/git/process_ref_changes_service.rb
new file mode 100644
index 00000000000..33925147750
--- /dev/null
+++ b/app/services/git/process_ref_changes_service.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Git
+ class ProcessRefChangesService < BaseService
+ PIPELINE_PROCESS_LIMIT = 4
+
+ def execute
+ changes = params[:changes]
+
+ process_changes_by_action(:branch, changes.branch_changes)
+ process_changes_by_action(:tag, changes.tag_changes)
+ end
+
+ private
+
+ def process_changes_by_action(ref_type, changes)
+ changes_by_action = group_changes_by_action(changes)
+
+ changes_by_action.each do |_, changes|
+ process_changes(ref_type, changes) if changes.any?
+ end
+ end
+
+ def group_changes_by_action(changes)
+ changes.group_by do |change|
+ change_action(change)
+ end
+ end
+
+ def change_action(change)
+ return :created if Gitlab::Git.blank_ref?(change[:oldrev])
+ return :removed if Gitlab::Git.blank_ref?(change[:newrev])
+
+ :pushed
+ end
+
+ def process_changes(ref_type, changes)
+ push_service_class = push_service_class_for(ref_type)
+
+ changes.each do |change|
+ push_service_class.new(
+ project,
+ current_user,
+ change: change,
+ push_options: params[:push_options],
+ create_pipelines: change[:index] < PIPELINE_PROCESS_LIMIT || Feature.enabled?(:git_push_create_all_pipelines, project)
+ ).execute
+ end
+ end
+
+ def push_service_class_for(ref_type)
+ return Git::TagPushService if ref_type == :tag
+
+ Git::BranchPushService
+ end
+ end
+end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index dc3c363f650..528b1ea61b3 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -56,7 +56,7 @@ module Issues
handle_milestone_change(issue)
- added_mentions = issue.mentioned_users - old_mentioned_users
+ added_mentions = issue.mentioned_users(current_user) - old_mentioned_users
if added_mentions.present?
notification_service.async.new_mentions_in_issue(issue, added_mentions, current_user)
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 4acc3f1981a..ae678d4c036 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -69,7 +69,8 @@ module MergeRequests
)
end
- added_mentions = merge_request.mentioned_users - old_mentioned_users
+ added_mentions = merge_request.mentioned_users(current_user) - old_mentioned_users
+
if added_mentions.present?
notification_service.async.new_mentions_in_merge_request(
merge_request,
diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb
index 853faed9d85..573be8fbe8b 100644
--- a/app/services/notes/update_service.rb
+++ b/app/services/notes/update_service.rb
@@ -5,7 +5,7 @@ module Notes
def execute(note)
return note unless note.editable?
- old_mentioned_users = note.mentioned_users.to_a
+ old_mentioned_users = note.mentioned_users(current_user).to_a
note.update(params.merge(updated_by: current_user))
diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml
index dce27dee9be..dace8a77736 100644
--- a/app/views/help/show.html.haml
+++ b/app/views/help/show.html.haml
@@ -1,3 +1,5 @@
- page_title @path.split("/").reverse.map(&:humanize)
+- @content_class = "limit-container-width" unless fluid_layout
+
.documentation.md.prepend-top-default
= markdown @markdown
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index a4b9ef18a3b..4f193e95faa 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -3,8 +3,6 @@
class PostReceive
include ApplicationWorker
- PIPELINE_PROCESS_LIMIT = 4
-
def perform(gl_repository, identifier, changes, push_options = {})
project, repo_type = Gitlab::GlRepository.parse(gl_repository)
@@ -49,8 +47,7 @@ class PostReceive
expire_caches(post_received, post_received.project.repository)
enqueue_repository_cache_update(post_received)
- process_changes(Git::BranchPushService, project, user, push_options, changes.branch_changes)
- process_changes(Git::TagPushService, project, user, push_options, changes.tag_changes)
+ process_ref_changes(project, user, push_options: push_options, changes: changes)
update_remote_mirrors(post_received)
after_project_changes_hooks(project, user, changes.refs, changes.repository_data)
end
@@ -75,18 +72,10 @@ class PostReceive
)
end
- def process_changes(service_class, project, user, push_options, changes)
- return if changes.empty?
-
- changes.each do |change|
- service_class.new(
- project,
- user,
- change: change,
- push_options: push_options,
- create_pipelines: change[:index] < PIPELINE_PROCESS_LIMIT || Feature.enabled?(:git_push_create_all_pipelines, project)
- ).execute
- end
+ def process_ref_changes(project, user, params = {})
+ return unless params[:changes].any?
+
+ Git::ProcessRefChangesService.new(project, user, params).execute
end
def update_remote_mirrors(post_received)