mirror of
https://github.com/wekan/wekan.git
synced 2025-12-17 16:00:13 +01:00
Import attachments
This commit is contained in:
parent
475bc70621
commit
ad27a59e57
3 changed files with 121 additions and 63 deletions
|
|
@ -86,7 +86,8 @@ BlazeComponent.extendComponent({
|
||||||
|
|
||||||
attachmentLink() {
|
attachmentLink() {
|
||||||
const attachment = this.currentData().attachment();
|
const attachment = this.currentData().attachment();
|
||||||
return attachment && Blaze.toHTML(HTML.A({
|
// trying to display url before file is stored generates js errors
|
||||||
|
return attachment && attachment.url({ download: true }) && Blaze.toHTML(HTML.A({
|
||||||
href: FlowRouter.path(attachment.url({ download: true })),
|
href: FlowRouter.path(attachment.url({ download: true })),
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
}, attachment.name()));
|
}, attachment.name()));
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,10 @@ Cards.helpers({
|
||||||
},
|
},
|
||||||
|
|
||||||
cover() {
|
cover() {
|
||||||
return Attachments.findOne(this.coverId);
|
const cover = Attachments.findOne(this.coverId);
|
||||||
|
// if we return a cover before it is fully stored, we will get errors when we try to display it
|
||||||
|
// todo XXX we could return a default "upload pending" image in the meantime?
|
||||||
|
return cover && cover.url() && cover;
|
||||||
},
|
},
|
||||||
|
|
||||||
absoluteUrl() {
|
absoluteUrl() {
|
||||||
|
|
|
||||||
176
models/import.js
176
models/import.js
|
|
@ -20,6 +20,9 @@ class TrelloCreator {
|
||||||
this.comments = {};
|
this.comments = {};
|
||||||
// the members, indexed by Trello member id => Wekan user ID
|
// the members, indexed by Trello member id => Wekan user ID
|
||||||
this.members = data.membersMapping ? data.membersMapping : {};
|
this.members = data.membersMapping ? data.membersMapping : {};
|
||||||
|
|
||||||
|
// maps a trelloCardId to an array of trelloAttachments
|
||||||
|
this.attachments = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
checkActions(trelloActions) {
|
checkActions(trelloActions) {
|
||||||
|
|
@ -141,56 +144,14 @@ class TrelloCreator {
|
||||||
return boardId;
|
return boardId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create labels if they do not exist and load this.labels.
|
/**
|
||||||
createLabels(trelloLabels, board) {
|
* Create the Wekan cards corresponding to the supplied Trello cards,
|
||||||
trelloLabels.forEach((label) => {
|
* as well as all linked data: activities, comments, and attachments
|
||||||
const color = label.color;
|
* @param trelloCards
|
||||||
const name = label.name;
|
* @param boardId
|
||||||
const existingLabel = board.getLabel(name, color);
|
* @returns {Array}
|
||||||
if (existingLabel) {
|
*/
|
||||||
this.labels[label.id] = existingLabel._id;
|
createCards(trelloCards, boardId) {
|
||||||
} 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) {
|
|
||||||
const result = [];
|
const result = [];
|
||||||
trelloCards.forEach((card) => {
|
trelloCards.forEach((card) => {
|
||||||
const cardToCreate = {
|
const cardToCreate = {
|
||||||
|
|
@ -273,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);
|
result.push(cardId);
|
||||||
});
|
});
|
||||||
return result;
|
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) {
|
getColor(trelloColorCode) {
|
||||||
// trello color name => wekan color
|
// trello color name => wekan color
|
||||||
const mapColors = {
|
const mapColors = {
|
||||||
|
|
@ -308,6 +347,29 @@ class TrelloCreator {
|
||||||
parseActions(trelloActions) {
|
parseActions(trelloActions) {
|
||||||
trelloActions.forEach((action) => {
|
trelloActions.forEach((action) => {
|
||||||
switch (action.type) {
|
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':
|
case 'createBoard':
|
||||||
this.createdAt.board = action.date;
|
this.createdAt.board = action.date;
|
||||||
break;
|
break;
|
||||||
|
|
@ -319,14 +381,6 @@ class TrelloCreator {
|
||||||
const listId = action.data.list.id;
|
const listId = action.data.list.id;
|
||||||
this.createdAt.lists[listId] = action.date;
|
this.createdAt.lists[listId] = action.date;
|
||||||
break;
|
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:
|
default:
|
||||||
// do nothing
|
// do nothing
|
||||||
break;
|
break;
|
||||||
|
|
@ -360,7 +414,7 @@ Meteor.methods({
|
||||||
trelloCreator.parseActions(trelloBoard.actions);
|
trelloCreator.parseActions(trelloBoard.actions);
|
||||||
const boardId = trelloCreator.createBoardAndLabels(trelloBoard);
|
const boardId = trelloCreator.createBoardAndLabels(trelloBoard);
|
||||||
trelloCreator.createLists(trelloBoard.lists, boardId);
|
trelloCreator.createLists(trelloBoard.lists, boardId);
|
||||||
trelloCreator.createCardsAndComments(trelloBoard.cards, boardId);
|
trelloCreator.createCards(trelloBoard.cards, boardId);
|
||||||
// XXX add members
|
// XXX add members
|
||||||
return boardId;
|
return boardId;
|
||||||
},
|
},
|
||||||
|
|
@ -399,7 +453,7 @@ Meteor.methods({
|
||||||
trelloCreator.parseActions(trelloCard.actions);
|
trelloCreator.parseActions(trelloCard.actions);
|
||||||
const board = list.board();
|
const board = list.board();
|
||||||
trelloCreator.createLabels(trelloCard.labels, board);
|
trelloCreator.createLabels(trelloCard.labels, board);
|
||||||
const cardIds = trelloCreator.createCardsAndComments([trelloCard], board._id);
|
const cardIds = trelloCreator.createCards([trelloCard], board._id);
|
||||||
return cardIds[0];
|
return cardIds[0];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue