Commit e22effd4 authored by Phil Hughes's avatar Phil Hughes

Merge branch '6469-issue-board-milestone-lists' into 'master'

Backport issue board milestone lists EE changes

See merge request gitlab-org/gitlab-ce!21023
parents 21c18070 ddb14e40
...@@ -112,12 +112,20 @@ export default { ...@@ -112,12 +112,20 @@ export default {
if (e.target) { if (e.target) {
const containerEl = e.target.closest('.js-board-list') || e.target.querySelector('.js-board-list'); const containerEl = e.target.closest('.js-board-list') || e.target.querySelector('.js-board-list');
const toBoardType = containerEl.dataset.boardType; const toBoardType = containerEl.dataset.boardType;
const cloneActions = {
label: ['milestone', 'assignee'],
assignee: ['milestone', 'label'],
milestone: ['label', 'assignee'],
};
if (toBoardType) { if (toBoardType) {
const fromBoardType = this.list.type; const fromBoardType = this.list.type;
// For each list we check if the destination list is
// a the list were we should clone the issue
const shouldClone = Object.entries(cloneActions).some(entry => (
fromBoardType === entry[0] && entry[1].includes(toBoardType)));
if ((fromBoardType === 'assignee' && toBoardType === 'label') || if (shouldClone) {
(fromBoardType === 'label' && toBoardType === 'assignee')) {
return 'clone'; return 'clone';
} }
} }
...@@ -145,7 +153,8 @@ export default { ...@@ -145,7 +153,8 @@ export default {
}); });
}, },
onUpdate: (e) => { onUpdate: (e) => {
const sortedArray = this.sortable.toArray().filter(id => id !== '-1'); const sortedArray = this.sortable.toArray()
.filter(id => id !== '-1');
gl.issueBoards.BoardsStore gl.issueBoards.BoardsStore
.moveIssueInList(this.list, Store.moving.issue, e.oldIndex, e.newIndex, sortedArray); .moveIssueInList(this.list, Store.moving.issue, e.oldIndex, e.newIndex, sortedArray);
}, },
......
...@@ -108,6 +108,16 @@ gl.issueBoards.BoardsStore = { ...@@ -108,6 +108,16 @@ gl.issueBoards.BoardsStore = {
issue.findAssignee(listTo.assignee)) { issue.findAssignee(listTo.assignee)) {
const targetIssue = listTo.findIssue(issue.id); const targetIssue = listTo.findIssue(issue.id);
targetIssue.removeAssignee(listFrom.assignee); targetIssue.removeAssignee(listFrom.assignee);
} else if (listTo.type === 'milestone') {
const currentMilestone = issue.milestone;
const currentLists = this.state.lists
.filter(list => (list.type === 'milestone' && list.id !== listTo.id))
.filter(list => list.issues.some(listIssue => issue.id === listIssue.id));
issue.removeMilestone(currentMilestone);
issue.addMilestone(listTo.milestone);
currentLists.forEach(currentList => currentList.removeIssue(issue));
listTo.addIssue(issue, listFrom, newIndex);
} else { } else {
// Add to new lists issues if it doesn't already exist // Add to new lists issues if it doesn't already exist
listTo.addIssue(issue, listFrom, newIndex); listTo.addIssue(issue, listFrom, newIndex);
...@@ -125,6 +135,9 @@ gl.issueBoards.BoardsStore = { ...@@ -125,6 +135,9 @@ gl.issueBoards.BoardsStore = {
} else if (listTo.type === 'backlog' && listFrom.type === 'assignee') { } else if (listTo.type === 'backlog' && listFrom.type === 'assignee') {
issue.removeAssignee(listFrom.assignee); issue.removeAssignee(listFrom.assignee);
listFrom.removeIssue(issue); listFrom.removeIssue(issue);
} else if (listTo.type === 'backlog' && listFrom.type === 'milestone') {
issue.removeMilestone(listFrom.milestone);
listFrom.removeIssue(issue);
} else if (this.shouldRemoveIssue(listFrom, listTo)) { } else if (this.shouldRemoveIssue(listFrom, listTo)) {
listFrom.removeIssue(issue); listFrom.removeIssue(issue);
} }
...@@ -144,7 +157,7 @@ gl.issueBoards.BoardsStore = { ...@@ -144,7 +157,7 @@ gl.issueBoards.BoardsStore = {
}, },
findList (key, val, type = 'label') { findList (key, val, type = 'label') {
const filteredList = this.state.lists.filter((list) => { const filteredList = this.state.lists.filter((list) => {
const byType = type ? (list.type === type) || (list.type === 'assignee') : true; const byType = type ? (list.type === type) || (list.type === 'assignee') || (list.type === 'milestone') : true;
return list[key] === val && byType; return list[key] === val && byType;
}); });
......
...@@ -4,7 +4,7 @@ class List < ActiveRecord::Base ...@@ -4,7 +4,7 @@ class List < ActiveRecord::Base
belongs_to :board belongs_to :board
belongs_to :label belongs_to :label
enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3 } enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3, milestone: 4 }
validates :board, :list_type, presence: true validates :board, :list_type, presence: true
validates :label, :position, presence: true, if: :label? validates :label, :position, presence: true, if: :label?
...@@ -27,11 +27,11 @@ class List < ActiveRecord::Base ...@@ -27,11 +27,11 @@ class List < ActiveRecord::Base
end end
def destroyable? def destroyable?
label? self.class.destroyable_types.include?(list_type&.to_sym)
end end
def movable? def movable?
label? self.class.movable_types.include?(list_type&.to_sym)
end end
def title def title
......
...@@ -6,12 +6,13 @@ ...@@ -6,12 +6,13 @@
%i.fa.fa-fw.board-title-expandable-toggle{ "v-if": "list.isExpandable", %i.fa.fa-fw.board-title-expandable-toggle{ "v-if": "list.isExpandable",
":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded }", ":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded }",
"aria-hidden": "true" } "aria-hidden": "true" }
= render_if_exists "shared/boards/components/list_milestone"
%a.user-avatar-link.js-no-trigger{ "v-if": "list.type === \"assignee\"", ":href": "list.assignee.path" } %a.user-avatar-link.js-no-trigger{ "v-if": "list.type === \"assignee\"", ":href": "list.assignee.path" }
-# haml-lint:disable AltText -# haml-lint:disable AltText
%img.avatar.s20.has-tooltip{ height: "20", width: "20", ":src": "list.assignee.avatar", ":alt": "list.assignee.name" } %img.avatar.s20.has-tooltip{ height: "20", width: "20", ":src": "list.assignee.avatar", ":alt": "list.assignee.name" }
%span.board-title-text.has-tooltip{ "v-if": "list.type !== \"label\"", %span.board-title-text.has-tooltip.block-truncated{ "v-if": "list.type !== \"label\"",
":title" => '((list.label && list.label.description) || list.title || "")', data: { container: "body" } } ":title" => '((list.label && list.label.description) || list.title || "")', data: { container: "body" } }
{{ list.title }} {{ list.title }}
......
...@@ -71,12 +71,10 @@ module API ...@@ -71,12 +71,10 @@ module API
success Entities::List success Entities::List
end end
params do params do
requires :label_id, type: Integer, desc: 'The ID of an existing label' use :list_creation_params
end end
post '/lists' do post '/lists' do
unless available_labels_for(user_project).exists?(params[:label_id]) authorize_list_type_resource!
render_api_error!({ error: 'Label not found!' }, 400)
end
authorize!(:admin_list, user_project) authorize!(:admin_list, user_project)
......
...@@ -14,7 +14,7 @@ module API ...@@ -14,7 +14,7 @@ module API
def create_list def create_list
create_list_service = create_list_service =
::Boards::Lists::CreateService.new(board_parent, current_user, { label_id: params[:label_id] }) ::Boards::Lists::CreateService.new(board_parent, current_user, create_list_params)
list = create_list_service.execute(board) list = create_list_service.execute(board)
...@@ -25,6 +25,10 @@ module API ...@@ -25,6 +25,10 @@ module API
end end
end end
def create_list_params
params.slice(:label_id)
end
def move_list(list) def move_list(list)
move_list_service = move_list_service =
::Boards::Lists::MoveService.new(board_parent, current_user, { position: params[:position].to_i }) ::Boards::Lists::MoveService.new(board_parent, current_user, { position: params[:position].to_i })
...@@ -44,6 +48,16 @@ module API ...@@ -44,6 +48,16 @@ module API
end end
end end
end end
def authorize_list_type_resource!
unless available_labels_for(board_parent).exists?(params[:label_id])
render_api_error!({ error: 'Label not found!' }, 400)
end
end
params :list_creation_params do
requires :label_id, type: Integer, desc: 'The ID of an existing label'
end
end end
end end
end end
......
...@@ -70,12 +70,10 @@ module API ...@@ -70,12 +70,10 @@ module API
success Entities::List success Entities::List
end end
params do params do
requires :label_id, type: Integer, desc: 'The ID of an existing label' use :list_creation_params
end end
post '/lists' do post '/lists' do
unless available_labels_for(board_parent).exists?(params[:label_id]) authorize_list_type_resource!
render_api_error!({ error: 'Label not found!' }, 400)
end
authorize!(:admin_list, user_group) authorize!(:admin_list, user_group)
......
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