From 1c7a9e4de8d9e59f7e106670eb9b308b3386483b Mon Sep 17 00:00:00 2001 From: "John R. Supplee" Date: Fri, 22 Jan 2021 01:26:08 +0200 Subject: [PATCH 1/3] Copy rules, triggers and actions when copying a board --- models/boards.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/models/boards.js b/models/boards.js index 006b58ed5..2e10c6f6b 100644 --- a/models/boards.js +++ b/models/boards.js @@ -537,6 +537,29 @@ Boards.helpers({ }, }); }); + + // copy rules, actions, and triggers + const actionsMap = {}; + Actions.find({ boardId: oldId }).forEach(action => { + const id = action._id; + delete action._id; + action.boardId = _id; + actionsMap[id] = Actions.insert(action); + }); + const triggersMap = {}; + Triggers.find({ boardId: oldId }).forEach(trigger => { + const id = trigger._id; + delete trigger._id; + trigger.boardId = _id; + triggersMap[id] = Triggers.insert(trigger); + }); + Rules.find({ boardId: oldId }).forEach(rule => { + delete rule._id; + rule.boardId = _id; + rule.actionId = actionsMap[rule.actionId]; + rule.triggerId = triggersMap[rule.triggerId]; + Rules.insert(rule); + }); }, /** * Is supplied user authorized to view this board? From ff8a36653a9c04dbe37e481aaa7af939a4c4f3fe Mon Sep 17 00:00:00 2001 From: "John R. Supplee" Date: Fri, 22 Jan 2021 12:49:48 +0200 Subject: [PATCH 2/3] Use a Meteor call to copy a board The current method was to copy a board on the client side. But not all data was available for copying rules. Moving the copy function to the server side solves this problem. --- client/components/lists/listBody.js | 19 +++++++++++++------ models/boards.js | 1 + server/publications/boards.js | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index c08f82673..45e8e506f 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -675,12 +675,19 @@ BlazeComponent.extendComponent({ element.type = 'swimlane'; _id = element.copy(this.boardId); } else if (this.isBoardTemplateSearch) { - board = Boards.findOne(element.linkedId); - board.sort = Boards.find({ archived: false }).count(); - board.type = 'board'; - board.title = element.title; - delete board.slug; - _id = board.copy(); + Meteor.call( + 'copyBoard', + element.linkedId, + { + sort: Boards.find({ archived: false }).count(), + type: 'board', + title: element.title, + }, + (err, data) => { + _id = data; + }, + ); + // _id = board.copy(); } Popup.close(); }, diff --git a/models/boards.js b/models/boards.js index 2e10c6f6b..4069489fa 100644 --- a/models/boards.js +++ b/models/boards.js @@ -508,6 +508,7 @@ Boards.helpers({ copy() { const oldId = this._id; delete this._id; + delete this.slug; const _id = Boards.insert(this); // Copy all swimlanes in board diff --git a/server/publications/boards.js b/server/publications/boards.js index 1ceadd021..8aab2e281 100644 --- a/server/publications/boards.js +++ b/server/publications/boards.js @@ -209,3 +209,19 @@ Meteor.publishRelations('board', function(boardId, isArchived) { return this.ready(); }); + +Meteor.methods({ + copyBoard(boardId, properties) { + check(boardId, String); + check(properties, Object); + + const board = Boards.findOne(boardId); + if (board) { + for (const key in properties) { + board[key] = properties[key]; + } + return board.copy(); + } + return null; + }, +}); From b249fcbb2e120c8cefe0064ed062acde3aa7aa2f Mon Sep 17 00:00:00 2001 From: "John R. Supplee" Date: Fri, 22 Jan 2021 14:24:39 +0200 Subject: [PATCH 3/3] Use the copyBoard method to duplicate a board * Use `copyBoard` instead of `cloneBoard` to duplicate a board * Give duplicated boards a unique title by appending number --- client/components/boards/boardsList.js | 8 ++++++-- client/components/lists/listBody.js | 1 - models/boards.js | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/client/components/boards/boardsList.js b/client/components/boards/boardsList.js index 145f67892..14174938a 100644 --- a/client/components/boards/boardsList.js +++ b/client/components/boards/boardsList.js @@ -124,9 +124,13 @@ BlazeComponent.extendComponent({ }, 'click .js-clone-board'(evt) { Meteor.call( - 'cloneBoard', + 'copyBoard', this.currentData()._id, - Session.get('fromBoard'), + { + sort: Boards.find({ archived: false }).count(), + type: 'board', + title: Boards.findOne(this.currentData()._id).copyTitle(), + }, (err, res) => { if (err) { this.setError(err.error); diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 45e8e506f..f607219fd 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -687,7 +687,6 @@ BlazeComponent.extendComponent({ _id = data; }, ); - // _id = board.copy(); } Popup.close(); }, diff --git a/models/boards.js b/models/boards.js index 4069489fa..cf1e8da08 100644 --- a/models/boards.js +++ b/models/boards.js @@ -562,6 +562,28 @@ Boards.helpers({ Rules.insert(rule); }); }, + /** + * Return a unique title based on the current title + * + * @returns {string|null} + */ + copyTitle() { + const m = this.title.match(/^(?.*?)\s*(\[(?<num>\d+)]\s*$|\s*$)/); + const title = m.groups.title; + let num = 0; + Boards.find({ title: new RegExp(`^${title}\\s*\\[\\d+]\\s*$`) }).forEach( + board => { + const m = board.title.match(/^(?<title>.*?)\s*\[(?<num>\d+)]\s*$/); + if (m) { + const n = parseInt(m.groups.num, 10); + num = num < n ? n : num; + } + }, + ); + + return `${title} [${num + 1}]`; + }, + /** * Is supplied user authorized to view this board? */