diff options
author | Maxime Quandalle <maxime@quandalle.com> | 2015-11-25 09:41:34 -0800 |
---|---|---|
committer | Maxime Quandalle <maxime@quandalle.com> | 2015-11-25 09:46:30 -0800 |
commit | f565aed2596baaf9cd3da63110068177f7461e8e (patch) | |
tree | 6cfd70ebf5091925f7e9662029a0ca2135103120 /models/import.js | |
parent | 5763714d22c4533f173d1d34a1210acf548c4d58 (diff) | |
parent | db90771d9b1330eb8f7183c472545277b8ed9449 (diff) | |
download | wekan-f565aed2596baaf9cd3da63110068177f7461e8e.tar.gz wekan-f565aed2596baaf9cd3da63110068177f7461e8e.tar.bz2 wekan-f565aed2596baaf9cd3da63110068177f7461e8e.zip |
Merge GitHub PR #401
I also completed the release notes related to the import feature.
Closes #401
Diffstat (limited to 'models/import.js')
-rw-r--r-- | models/import.js | 227 |
1 files changed, 161 insertions, 66 deletions
diff --git a/models/import.js b/models/import.js index a6e9f3d5..33f56d4b 100644 --- a/models/import.js +++ b/models/import.js @@ -4,7 +4,7 @@ const DateString = Match.Where(function (dateAsString) { }); class TrelloCreator { - constructor() { + constructor(data) { // The object creation dates, indexed by Trello id (so we only parse actions // once!) this.createdAt = { @@ -18,6 +18,11 @@ class TrelloCreator { this.lists = {}; // The comments, indexed by Trello card id (to map when importing cards) this.comments = {}; + // the members, indexed by Trello member id => Wekan user ID + this.members = data.membersMapping ? data.membersMapping : {}; + + // maps a trelloCardId to an array of trelloAttachments + this.attachments = {}; } checkActions(trelloActions) { @@ -90,6 +95,24 @@ class TrelloCreator { stars: 0, title: trelloBoard.name, }; + // now add other members + if(trelloBoard.memberships) { + trelloBoard.memberships.forEach((trelloMembership) => { + const trelloId = trelloMembership.idMember; + // do we have a mapping? + if(this.members[trelloId]) { + const wekanId = this.members[trelloId]; + // do we already have it in our list? + if(!boardToCreate.members.find((wekanMember) => wekanMember.userId === wekanId)) { + boardToCreate.members.push({ + userId: wekanId, + isAdmin: false, + isActive: true, + }); + } + } + }); + } trelloBoard.labels.forEach((label) => { const labelToCreate = { _id: Random.id(6), @@ -121,56 +144,14 @@ class TrelloCreator { return boardId; } - // Create labels if they do not exist and load this.labels. - createLabels(trelloLabels, board) { - trelloLabels.forEach((label) => { - const color = label.color; - const name = label.name; - const existingLabel = board.getLabel(name, color); - if (existingLabel) { - this.labels[label.id] = existingLabel._id; - } else { - const idLabelCreated = board.pushLabel(name, color); - this.labels[label.id] = idLabelCreated; - } - }); - } - - createLists(trelloLists, boardId) { - trelloLists.forEach((list) => { - const listToCreate = { - archived: list.closed, - boardId, - // We are being defensing here by providing a default date (now) if the - // creation date wasn't found on the action log. This happen on old - // Trello boards (eg from 2013) that didn't log the 'createList' action - // we require. - createdAt: new Date(this.createdAt.lists[list.id] || Date.now()), - title: list.name, - userId: Meteor.userId(), - }; - const listId = Lists.direct.insert(listToCreate); - const now = new Date(); - Lists.direct.update(listId, {$set: {'updatedAt': now}}); - this.lists[list.id] = listId; - // log activity - Activities.direct.insert({ - activityType: 'importList', - boardId, - createdAt: now, - listId, - source: { - id: list.id, - system: 'Trello', - }, - // We attribute the import to current user, not the one from the - // original object - userId: Meteor.userId(), - }); - }); - } - - createCardsAndComments(trelloCards, boardId) { + /** + * Create the Wekan cards corresponding to the supplied Trello cards, + * as well as all linked data: activities, comments, and attachments + * @param trelloCards + * @param boardId + * @returns {Array} + */ + createCards(trelloCards, boardId) { const result = []; trelloCards.forEach((card) => { const cardToCreate = { @@ -191,6 +172,25 @@ class TrelloCreator { return this.labels[trelloId]; }); } + // add members { + if(card.idMembers) { + const wekanMembers = []; + // we can't just map, as some members may not have been mapped + card.idMembers.forEach((trelloId) => { + if(this.members[trelloId]) { + const wekanId = this.members[trelloId]; + // we may map multiple Trello members to the same wekan user + // in which case we risk adding the same user multiple times + if(!wekanMembers.find((wId) => wId === wekanId)){ + wekanMembers.push(wekanId); + } + } + return true; + }); + if(wekanMembers.length>0) { + cardToCreate.members = wekanMembers; + } + } // insert card const cardId = Cards.direct.insert(cardToCreate); // log activity @@ -234,12 +234,90 @@ class TrelloCreator { }); }); } - // XXX add attachments + const attachments = this.attachments[card.id]; + const trelloCoverId = card.idAttachmentCover; + if (attachments) { + attachments.forEach((att) => { + const file = new FS.File(); + // Simulating file.attachData on the client generates multiple errors + // - HEAD returns null, which causes exception down the line + // - the template then tries to display the url to the attachment which causes other errors + // so we make it server only, and let UI catch up once it is done, forget about latency comp. + if(Meteor.isServer) { + file.attachData(att.url, function (error) { + file.boardId = boardId; + file.cardId = cardId; + if (error) { + throw(error); + } else { + const wekanAtt = Attachments.insert(file, () => { + // we do nothing + }); + // + if(trelloCoverId === att.id) { + Cards.direct.update(cardId, { $set: {coverId: wekanAtt._id}}); + } + } + }); + } + // todo XXX set cover - if need be + }); + } result.push(cardId); }); return result; } + // Create labels if they do not exist and load this.labels. + createLabels(trelloLabels, board) { + trelloLabels.forEach((label) => { + const color = label.color; + const name = label.name; + const existingLabel = board.getLabel(name, color); + if (existingLabel) { + this.labels[label.id] = existingLabel._id; + } else { + const idLabelCreated = board.pushLabel(name, color); + this.labels[label.id] = idLabelCreated; + } + }); + } + + createLists(trelloLists, boardId) { + trelloLists.forEach((list) => { + const listToCreate = { + archived: list.closed, + boardId, + // We are being defensing here by providing a default date (now) if the + // creation date wasn't found on the action log. This happen on old + // Trello boards (eg from 2013) that didn't log the 'createList' action + // we require. + createdAt: new Date(this.createdAt.lists[list.id] || Date.now()), + title: list.name, + userId: Meteor.userId(), + }; + const listId = Lists.direct.insert(listToCreate); + const now = new Date(); + Lists.direct.update(listId, {$set: {'updatedAt': now}}); + this.lists[list.id] = listId; + // log activity + Activities.direct.insert({ + activityType: 'importList', + boardId, + createdAt: now, + listId, + source: { + id: list.id, + system: 'Trello', + }, + // We attribute the import to current user, not the one from the + // original object + userId: Meteor.userId(), + }); + }); + } + + getColor(trelloColorCode) { // trello color name => wekan color const mapColors = { @@ -269,6 +347,29 @@ class TrelloCreator { parseActions(trelloActions) { trelloActions.forEach((action) => { switch (action.type) { + case 'addAttachmentToCard': + // We have to be cautious, because the attachment could have been removed later. + // In that case Trello still reports its addition, but removes its 'url' field. + // So we test for that + const trelloAttachment = action.data.attachment; + if(trelloAttachment.url) { + // we cannot actually create the Wekan attachment, because we don't yet + // have the cards to attach it to, so we store it in the instance variable. + const trelloCardId = action.data.card.id; + if(!this.attachments[trelloCardId]) { + this.attachments[trelloCardId] = []; + } + this.attachments[trelloCardId].push(trelloAttachment); + } + break; + case 'commentCard': + const id = action.data.card.id; + if (this.comments[id]) { + this.comments[id].push(action); + } else { + this.comments[id] = [action]; + } + break; case 'createBoard': this.createdAt.board = action.date; break; @@ -280,14 +381,6 @@ class TrelloCreator { const listId = action.data.list.id; this.createdAt.lists[listId] = action.date; break; - case 'commentCard': - const id = action.data.card.id; - if (this.comments[id]) { - this.comments[id].push(action); - } else { - this.comments[id] = [action]; - } - break; default: // do nothing break; @@ -298,12 +391,13 @@ class TrelloCreator { Meteor.methods({ importTrelloBoard(trelloBoard, data) { - const trelloCreator = new TrelloCreator(); + const trelloCreator = new TrelloCreator(data); // 1. check all parameters are ok from a syntax point of view try { - // we don't use additional data - this should be an empty object - check(data, {}); + check(data, { + membersMapping: Match.Optional(Object), + }); trelloCreator.checkActions(trelloBoard.actions); trelloCreator.checkBoard(trelloBoard); trelloCreator.checkLabels(trelloBoard.labels); @@ -320,19 +414,20 @@ Meteor.methods({ trelloCreator.parseActions(trelloBoard.actions); const boardId = trelloCreator.createBoardAndLabels(trelloBoard); trelloCreator.createLists(trelloBoard.lists, boardId); - trelloCreator.createCardsAndComments(trelloBoard.cards, boardId); + trelloCreator.createCards(trelloBoard.cards, boardId); // XXX add members return boardId; }, importTrelloCard(trelloCard, data) { - const trelloCreator = new TrelloCreator(); + const trelloCreator = new TrelloCreator(data); // 1. check parameters are ok from a syntax point of view try { check(data, { listId: String, sortIndex: Number, + membersMapping: Match.Optional(Object), }); trelloCreator.checkCards([trelloCard]); trelloCreator.checkLabels(trelloCard.labels); @@ -358,7 +453,7 @@ Meteor.methods({ trelloCreator.parseActions(trelloCard.actions); const board = list.board(); trelloCreator.createLabels(trelloCard.labels, board); - const cardIds = trelloCreator.createCardsAndComments([trelloCard], board._id); + const cardIds = trelloCreator.createCards([trelloCard], board._id); return cardIds[0]; }, }); |