From 7a08f42edb13a72bd7e50ea290bfdad8f06fd012 Mon Sep 17 00:00:00 2001 From: Daniel Eder <1525711+daniel-eder@users.noreply.github.com> Date: Wed, 9 Dec 2020 14:01:33 +0200 Subject: [PATCH 1/4] Fix missing custom fields when cloning board --- models/wekanCreator.js | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/models/wekanCreator.js b/models/wekanCreator.js index 9914f8171..0dbc2dfcf 100644 --- a/models/wekanCreator.js +++ b/models/wekanCreator.js @@ -15,6 +15,7 @@ export class WekanCreator { cards: {}, lists: {}, swimlanes: {}, + customFields: {}, }; // The object creator Wekan Id, indexed by the object Wekan id // (so we only parse actions once!) @@ -30,6 +31,8 @@ export class WekanCreator { this.lists = {}; // Map of cards Wekan ID => Wekan ID this.cards = {}; + // Map of custom fields Wekan ID => Wekan ID + this.customFields = {}; // Map of comments Wekan ID => Wekan ID this.commentIds = {}; // Map of attachments Wekan ID => Wekan ID @@ -356,6 +359,17 @@ export class WekanCreator { if (card.color) { cardToCreate.color = card.color; } + + // add custom fields + if (card.customFields) { + cardToCreate.customFields = card.customFields.map(field => { + return { + _id: this.customFields[field._id], + value: field.value, + }; + }); + } + // insert card const cardId = Cards.direct.insert(cardToCreate); // keep track of Wekan id => Wekan id @@ -481,6 +495,39 @@ export class WekanCreator { return result; } + /** + * Create the Wekan custom fields corresponding to the supplied Wekan + * custom fields. + * @param wekanCustomFields + * @param boardId + */ + createCustomFields(wekanCustomFields, boardId) { + wekanCustomFields.forEach((field, fieldIndex) => { + const fieldToCreate = { + boardIds: [boardId], + name: field.name, + type: field.type, + settings: field.settings, + showOnCard: field.showOnCard, + showLabelOnMiniCard: field.showLabelOnMiniCard, + automaticallyOnCard: field.automaticallyOnCard, + //use date "now" if now created at date is provided (e.g. for very old boards) + createdAt: this._now(this.createdAt.customFields[field._id]), + modifiedAt: field.modifiedAt, + }; + //insert copy of custom field + const fieldId = CustomFields.direct.insert(fieldToCreate); + //set modified date to now + CustomFields.direct.update(fieldId, { + $set: { + modifiedAt: this._now(), + }, + }); + //store mapping of old id to new id + this.customFields[field._id] = fieldId; + }); + } + // Create labels if they do not exist and load this.labels. createLabels(wekanLabels, board) { wekanLabels.forEach(label => { @@ -690,6 +737,11 @@ export class WekanCreator { this.createdAt.swimlanes[swimlaneId] = activity.createdAt; break; } + case 'createCustomField': { + const customFieldId = activity.customFieldId; + this.createdAt.customFields[customFieldId] = activity.createdAt; + break; + } } }); } @@ -840,6 +892,7 @@ export class WekanCreator { const boardId = this.createBoardAndLabels(board); this.createLists(board.lists, boardId); this.createSwimlanes(board.swimlanes, boardId); + this.createCustomFields(board.customFields, boardId); this.createCards(board.cards, boardId); this.createChecklists(board.checklists); this.createChecklistItems(board.checklistItems); From a3cd1b89ff51bb2d20240a450266923da8e6c442 Mon Sep 17 00:00:00 2001 From: Daniel Eder <1525711+daniel-eder@users.noreply.github.com> Date: Wed, 9 Dec 2020 15:02:50 +0200 Subject: [PATCH 2/4] Fix missing member assignments when cloning board --- models/import.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/models/import.js b/models/import.js index ba68bd66c..7c6a30e28 100644 --- a/models/import.js +++ b/models/import.js @@ -42,9 +42,23 @@ Meteor.methods({ check(currentBoardId, Match.Maybe(String)); const exporter = new Exporter(sourceBoardId); const data = exporter.build(); - const addData = {}; - addData.membersMapping = getMembersToMap(data); - const creator = new WekanCreator(addData); + const additionalData = {}; + + //get the members to map + const membersMapping = getMembersToMap(data); + + //now mirror the mapping done in finishImport in client/components/import/import.js: + if (membersMapping) { + const mappingById = {}; + membersMapping.forEach(member => { + if (member.wekanId) { + mappingById[member.id] = member.wekanId; + } + }); + additionalData.membersMapping = mappingById; + } + + const creator = new WekanCreator(additionalData); //data.title = `${data.title } - ${ TAPi18n.__('copy-tag')}`; data.title = `${data.title}`; return creator.create(data, currentBoardId); From a6c48329ef78ee7d7e4ec46874c10684243025c8 Mon Sep 17 00:00:00 2001 From: Daniel Eder <1525711+daniel-eder@users.noreply.github.com> Date: Wed, 9 Dec 2020 15:06:56 +0200 Subject: [PATCH 3/4] Fix missing assignee assignment when cloning or importing board --- models/wekanCreator.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/models/wekanCreator.js b/models/wekanCreator.js index 0dbc2dfcf..24e3cc262 100644 --- a/models/wekanCreator.js +++ b/models/wekanCreator.js @@ -355,6 +355,25 @@ export class WekanCreator { cardToCreate.members = wekanMembers; } } + // add assignees + if (card.assignees) { + const wekanAssignees = []; + // we can't just map, as some members may not have been mapped + card.assignees.forEach(sourceMemberId => { + if (this.members[sourceMemberId]) { + const wekanId = this.members[sourceMemberId]; + // we may map multiple Wekan members to the same wekan user + // in which case we risk adding the same user multiple times + if (!wekanAssignees.find(wId => wId === wekanId)) { + wekanAssignees.push(wekanId); + } + } + return true; + }); + if (wekanAssignees.length > 0) { + cardToCreate.assignees = wekanAssignees; + } + } // set color if (card.color) { cardToCreate.color = card.color; From 0c12c45080362430e9bb392c217bafe9692be06e Mon Sep 17 00:00:00 2001 From: Daniel Eder <1525711+daniel-eder@users.noreply.github.com> Date: Wed, 9 Dec 2020 15:51:45 +0200 Subject: [PATCH 4/4] Fix missing subtasks when cloning board --- models/wekanCreator.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/models/wekanCreator.js b/models/wekanCreator.js index 24e3cc262..77e5fa6d8 100644 --- a/models/wekanCreator.js +++ b/models/wekanCreator.js @@ -247,6 +247,7 @@ export class WekanCreator { swimlaneId: false, }, ], + presentParentTask: boardToImport.presentParentTask, // Standalone Export has modifiedAt missing, adding modifiedAt to fix it modifiedAt: this._now(boardToImport.modifiedAt), permission: boardToImport.permission, @@ -626,6 +627,35 @@ export class WekanCreator { }); } + createSubtasks(wekanCards) { + wekanCards.forEach(card => { + // get new id of card (in created / new board) + const cardIdInNewBoard = this.cards[card._id]; + + //If there is a mapped parent card, use the mapped card + // this means, the card and parent were in the same source board + //If there is no mapped parent card, use the original parent id, + // this should handle cases where source and parent are in different boards + // Note: This can only handle board cloning (within the same wekan instance). + // When importing boards between instances the IDs are definitely + // lost if source and parent are two different boards + // This is not the place to fix it, the entire subtask system needs to be rethought there. + const parentIdInNewBoard = this.cards[card.parentId] + ? this.cards[card.parentId] + : card.parentId; + + //if the parent card exists, proceed + if (Cards.findOne(parentIdInNewBoard)) { + //set parent id of the card in the new board to the new id of the parent + Cards.direct.update(cardIdInNewBoard, { + $set: { + parentId: parentIdInNewBoard, + }, + }); + } + }); + } + createChecklists(wekanChecklists) { const result = []; wekanChecklists.forEach((checklist, checklistIndex) => { @@ -913,6 +943,7 @@ export class WekanCreator { this.createSwimlanes(board.swimlanes, boardId); this.createCustomFields(board.customFields, boardId); this.createCards(board.cards, boardId); + this.createSubtasks(board.cards); this.createChecklists(board.checklists); this.createChecklistItems(board.checklistItems); this.importActivities(board.activities, boardId);