mirror of
https://github.com/wekan/wekan.git
synced 2025-12-21 18:00:12 +01:00
Import board: map team permission, and refactor code to share with card import
This commit is contained in:
parent
ec304de811
commit
8e0ad91191
2 changed files with 83 additions and 135 deletions
|
|
@ -111,6 +111,14 @@ Boards.helpers({
|
||||||
colorClass() {
|
colorClass() {
|
||||||
return `board-color-${this.color}`;
|
return `board-color-${this.color}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// XXX currently mutations return no value so we have an issue when using addLabel in import
|
||||||
|
// XXX waiting on https://github.com/mquandalle/meteor-collection-mutations/issues/1 to remove...
|
||||||
|
pushLabel(name, color) {
|
||||||
|
const _id = Random.id(6);
|
||||||
|
Boards.direct.update(this._id, { $push: {labels: { _id, name, color }}});
|
||||||
|
return _id;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Boards.mutations({
|
Boards.mutations({
|
||||||
|
|
|
||||||
210
models/import.js
210
models/import.js
|
|
@ -11,9 +11,9 @@ class TrelloCreator {
|
||||||
cards: {},
|
cards: {},
|
||||||
lists: {},
|
lists: {},
|
||||||
};
|
};
|
||||||
// the labels we created, indexed by Trello id (to map when importing cards)
|
// map of labels Trello ID => Wekan ID
|
||||||
this.labels = {};
|
this.labels = {};
|
||||||
// the lists we created, indexed by Trello id (to map when importing cards)
|
// map of lists Trello ID => Wekan ID
|
||||||
this.lists = {};
|
this.lists = {};
|
||||||
// the comments, indexed by Trello card id (to map when importing cards)
|
// the comments, indexed by Trello card id (to map when importing cards)
|
||||||
this.comments = {};
|
this.comments = {};
|
||||||
|
|
@ -25,27 +25,41 @@ class TrelloCreator {
|
||||||
date: DateString,
|
date: DateString,
|
||||||
type: String,
|
type: String,
|
||||||
})]);
|
})]);
|
||||||
// XXX perform deeper checks based on type
|
// XXX we could perform more thorough checks based on action type
|
||||||
}
|
}
|
||||||
|
|
||||||
checkBoard(trelloBoard) {
|
checkBoard(trelloBoard) {
|
||||||
check(trelloBoard, Match.ObjectIncluding({
|
check(trelloBoard, Match.ObjectIncluding({
|
||||||
closed: Boolean,
|
closed: Boolean,
|
||||||
labels: [Match.ObjectIncluding({
|
|
||||||
// XXX check versus list
|
|
||||||
color: String,
|
|
||||||
name: String,
|
|
||||||
})],
|
|
||||||
name: String,
|
name: String,
|
||||||
prefs: Match.ObjectIncluding({
|
prefs: Match.ObjectIncluding({
|
||||||
// XXX check versus list
|
// XXX refine control by validating 'background' against a list of allowed values (is it worth the maintenance?)
|
||||||
background: String,
|
background: String,
|
||||||
// XXX check versus list
|
permissionLevel: Match.Where((value) => {return ['org', 'private', 'public'].indexOf(value)>= 0;}),
|
||||||
permissionLevel: String,
|
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkCards(trelloCards) {
|
||||||
|
check(trelloCards, [Match.ObjectIncluding({
|
||||||
|
closed: Boolean,
|
||||||
|
dateLastActivity: DateString,
|
||||||
|
desc: String,
|
||||||
|
idLabels: [String],
|
||||||
|
idMembers: [String],
|
||||||
|
name: String,
|
||||||
|
pos: Number,
|
||||||
|
})]);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLabels(trelloLabels) {
|
||||||
|
check(trelloLabels, [Match.ObjectIncluding({
|
||||||
|
// XXX refine control by validating 'color' against a list of allowed values (is it worth the maintenance?)
|
||||||
|
color: String,
|
||||||
|
name: String,
|
||||||
|
})]);
|
||||||
|
}
|
||||||
|
|
||||||
checkLists(trelloLists) {
|
checkLists(trelloLists) {
|
||||||
check(trelloLists, [Match.ObjectIncluding({
|
check(trelloLists, [Match.ObjectIncluding({
|
||||||
closed: Boolean,
|
closed: Boolean,
|
||||||
|
|
@ -53,16 +67,6 @@ class TrelloCreator {
|
||||||
})]);
|
})]);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkCards(trelloCards) {
|
|
||||||
check(trelloCards, [Match.ObjectIncluding({
|
|
||||||
closed: Boolean,
|
|
||||||
desc: String,
|
|
||||||
// XXX check idLabels
|
|
||||||
name: String,
|
|
||||||
pos: Number,
|
|
||||||
})]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* must call parseActions before calling this one
|
* must call parseActions before calling this one
|
||||||
*/
|
*/
|
||||||
|
|
@ -78,8 +82,7 @@ class TrelloCreator {
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
}],
|
}],
|
||||||
// current mapping is easy as trello and wekan use same keys: 'private' and 'public'
|
permission: this.getPermission(trelloBoard.prefs.permissionLevel),
|
||||||
permission: trelloBoard.prefs.permissionLevel,
|
|
||||||
slug: getSlug(trelloBoard.name) || 'board',
|
slug: getSlug(trelloBoard.name) || 'board',
|
||||||
stars: 0,
|
stars: 0,
|
||||||
title: trelloBoard.name,
|
title: trelloBoard.name,
|
||||||
|
|
@ -91,7 +94,7 @@ class TrelloCreator {
|
||||||
name: label.name,
|
name: label.name,
|
||||||
};
|
};
|
||||||
// we need to remember them by Trello ID, as this is the only ref we have when importing cards
|
// we need to remember them by Trello ID, as this is the only ref we have when importing cards
|
||||||
this.labels[label.id] = labelToCreate;
|
this.labels[label.id] = labelToCreate._id;
|
||||||
boardToCreate.labels.push(labelToCreate);
|
boardToCreate.labels.push(labelToCreate);
|
||||||
});
|
});
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
@ -113,6 +116,23 @@ class TrelloCreator {
|
||||||
return boardId;
|
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) {
|
createLists(trelloLists, boardId) {
|
||||||
trelloLists.forEach((list) => {
|
trelloLists.forEach((list) => {
|
||||||
const listToCreate = {
|
const listToCreate = {
|
||||||
|
|
@ -125,8 +145,7 @@ class TrelloCreator {
|
||||||
const listId = Lists.direct.insert(listToCreate);
|
const listId = Lists.direct.insert(listToCreate);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
Lists.direct.update(listId, {$set: {'updatedAt': now}});
|
Lists.direct.update(listId, {$set: {'updatedAt': now}});
|
||||||
listToCreate._id = listId;
|
this.lists[list.id] = listId;
|
||||||
this.lists[list.id] = listToCreate;
|
|
||||||
// log activity
|
// log activity
|
||||||
Activities.direct.insert({
|
Activities.direct.insert({
|
||||||
activityType: 'importList',
|
activityType: 'importList',
|
||||||
|
|
@ -144,6 +163,7 @@ class TrelloCreator {
|
||||||
}
|
}
|
||||||
|
|
||||||
createCardsAndComments(trelloCards, boardId) {
|
createCardsAndComments(trelloCards, boardId) {
|
||||||
|
const result = [];
|
||||||
trelloCards.forEach((card) => {
|
trelloCards.forEach((card) => {
|
||||||
const cardToCreate = {
|
const cardToCreate = {
|
||||||
archived: card.closed,
|
archived: card.closed,
|
||||||
|
|
@ -151,7 +171,7 @@ class TrelloCreator {
|
||||||
createdAt: this.createdAt.cards[card.id],
|
createdAt: this.createdAt.cards[card.id],
|
||||||
dateLastActivity: new Date(),
|
dateLastActivity: new Date(),
|
||||||
description: card.desc,
|
description: card.desc,
|
||||||
listId: this.lists[card.idList]._id,
|
listId: this.lists[card.idList],
|
||||||
sort: card.pos,
|
sort: card.pos,
|
||||||
title: card.name,
|
title: card.name,
|
||||||
// XXX use the original user?
|
// XXX use the original user?
|
||||||
|
|
@ -160,7 +180,7 @@ class TrelloCreator {
|
||||||
// add labels
|
// add labels
|
||||||
if(card.idLabels) {
|
if(card.idLabels) {
|
||||||
cardToCreate.labelIds = card.idLabels.map((trelloId) => {
|
cardToCreate.labelIds = card.idLabels.map((trelloId) => {
|
||||||
return this.labels[trelloId]._id;
|
return this.labels[trelloId];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// insert card
|
// insert card
|
||||||
|
|
@ -205,7 +225,9 @@ class TrelloCreator {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// XXX add attachments
|
// XXX add attachments
|
||||||
|
result.push(cardId);
|
||||||
});
|
});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
getColor(trelloColorCode) {
|
getColor(trelloColorCode) {
|
||||||
|
|
@ -225,6 +247,14 @@ class TrelloCreator {
|
||||||
return wekanColor || Boards.simpleSchema()._schema.color.allowedValues[0];
|
return wekanColor || Boards.simpleSchema()._schema.color.allowedValues[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPermission(trelloPermissionCode) {
|
||||||
|
if(trelloPermissionCode === 'public') {
|
||||||
|
return 'public';
|
||||||
|
}
|
||||||
|
// Wekan does NOT have organization level, so we default both 'private' and 'org' to private.
|
||||||
|
return 'private';
|
||||||
|
}
|
||||||
|
|
||||||
parseActions(trelloActions) {
|
parseActions(trelloActions) {
|
||||||
trelloActions.forEach((action) => {
|
trelloActions.forEach((action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
@ -258,19 +288,23 @@ class TrelloCreator {
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
importTrelloBoard(trelloBoard, data) {
|
importTrelloBoard(trelloBoard, data) {
|
||||||
const trelloCreator = new TrelloCreator();
|
const trelloCreator = new TrelloCreator();
|
||||||
|
|
||||||
// 1. check all parameters are ok from a syntax point of view
|
// 1. check all parameters are ok from a syntax point of view
|
||||||
try {
|
try {
|
||||||
// we don't use additional data - this should be an empty object
|
// we don't use additional data - this should be an empty object
|
||||||
check(data, {});
|
check(data, {});
|
||||||
trelloCreator.checkActions(trelloBoard.actions);
|
trelloCreator.checkActions(trelloBoard.actions);
|
||||||
trelloCreator.checkBoard(trelloBoard);
|
trelloCreator.checkBoard(trelloBoard);
|
||||||
|
trelloCreator.checkLabels(trelloBoard.labels);
|
||||||
trelloCreator.checkLists(trelloBoard.lists);
|
trelloCreator.checkLists(trelloBoard.lists);
|
||||||
trelloCreator.checkCards(trelloBoard.cards);
|
trelloCreator.checkCards(trelloBoard.cards);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
throw new Meteor.Error('error-json-schema');
|
throw new Meteor.Error('error-json-schema');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. check parameters are ok from a business point of view (exist & authorized)
|
// 2. check parameters are ok from a business point of view (exist & authorized)
|
||||||
// nothing to check, everyone can import boards in their account
|
// nothing to check, everyone can import boards in their account
|
||||||
|
|
||||||
// 3. create all elements
|
// 3. create all elements
|
||||||
trelloCreator.parseActions(trelloBoard.actions);
|
trelloCreator.parseActions(trelloBoard.actions);
|
||||||
const boardId = trelloCreator.createBoardAndLabels(trelloBoard);
|
const boardId = trelloCreator.createBoardAndLabels(trelloBoard);
|
||||||
|
|
@ -279,33 +313,19 @@ Meteor.methods({
|
||||||
// XXX add members
|
// XXX add members
|
||||||
return boardId;
|
return boardId;
|
||||||
},
|
},
|
||||||
|
|
||||||
importTrelloCard(trelloCard, data) {
|
importTrelloCard(trelloCard, data) {
|
||||||
|
const trelloCreator = new TrelloCreator();
|
||||||
|
|
||||||
// 1. check parameters are ok from a syntax point of view
|
// 1. check parameters are ok from a syntax point of view
|
||||||
const DateString = Match.Where(function (dateAsString) {
|
|
||||||
check(dateAsString, String);
|
|
||||||
return moment(dateAsString, moment.ISO_8601).isValid();
|
|
||||||
});
|
|
||||||
try {
|
try {
|
||||||
check(trelloCard, Match.ObjectIncluding({
|
|
||||||
name: String,
|
|
||||||
desc: String,
|
|
||||||
closed: Boolean,
|
|
||||||
dateLastActivity: DateString,
|
|
||||||
labels: [Match.ObjectIncluding({
|
|
||||||
name: String,
|
|
||||||
color: String,
|
|
||||||
})],
|
|
||||||
actions: [Match.ObjectIncluding({
|
|
||||||
type: String,
|
|
||||||
date: DateString,
|
|
||||||
data: Object,
|
|
||||||
})],
|
|
||||||
members: [Object],
|
|
||||||
}));
|
|
||||||
check(data, {
|
check(data, {
|
||||||
listId: String,
|
listId: String,
|
||||||
sortIndex: Number,
|
sortIndex: Number,
|
||||||
});
|
});
|
||||||
|
trelloCreator.checkCards([trelloCard]);
|
||||||
|
trelloCreator.checkLabels(trelloCard.labels);
|
||||||
|
trelloCreator.checkActions(trelloCard.actions);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
throw new Meteor.Error('error-json-schema');
|
throw new Meteor.Error('error-json-schema');
|
||||||
}
|
}
|
||||||
|
|
@ -321,92 +341,12 @@ Meteor.methods({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. map all fields for the card to create
|
// 3. create all elements
|
||||||
const dateOfImport = new Date();
|
trelloCreator.lists[trelloCard.idList] = data.listId;
|
||||||
const cardToCreate = {
|
trelloCreator.parseActions(trelloCard.actions);
|
||||||
archived: trelloCard.closed,
|
const board = list.board();
|
||||||
boardId: list.boardId,
|
trelloCreator.createLabels(trelloCard.labels, board);
|
||||||
// this is a default date, we'll fetch the actual one from the actions array
|
const cardIds = trelloCreator.createCardsAndComments([trelloCard], board._id);
|
||||||
createdAt: dateOfImport,
|
return cardIds[0];
|
||||||
dateLastActivity: dateOfImport,
|
|
||||||
description: trelloCard.desc,
|
|
||||||
listId: list._id,
|
|
||||||
sort: data.sortIndex,
|
|
||||||
title: trelloCard.name,
|
|
||||||
// XXX use the original user?
|
|
||||||
userId: Meteor.userId(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// 4. find actual creation date
|
|
||||||
const creationAction = trelloCard.actions.find((action) => {
|
|
||||||
return action.type === 'createCard';
|
|
||||||
});
|
|
||||||
if(creationAction) {
|
|
||||||
cardToCreate.createdAt = creationAction.date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. map labels - create missing ones
|
|
||||||
trelloCard.labels.forEach((currentLabel) => {
|
|
||||||
const color = currentLabel.color;
|
|
||||||
const name = currentLabel.name;
|
|
||||||
const existingLabel = list.board().getLabel(name, color);
|
|
||||||
let labelId = undefined;
|
|
||||||
if (existingLabel) {
|
|
||||||
labelId = existingLabel._id;
|
|
||||||
} else {
|
|
||||||
let labelCreated = list.board().addLabel(name, color);
|
|
||||||
// XXX currently mutations return no value so we have to fetch the label we just created
|
|
||||||
// waiting on https://github.com/mquandalle/meteor-collection-mutations/issues/1 to remove...
|
|
||||||
labelCreated = list.board().getLabel(name, color);
|
|
||||||
labelId = labelCreated._id;
|
|
||||||
}
|
|
||||||
if(labelId) {
|
|
||||||
if (!cardToCreate.labelIds) {
|
|
||||||
cardToCreate.labelIds = [];
|
|
||||||
}
|
|
||||||
cardToCreate.labelIds.push(labelId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 6. insert new card into list
|
|
||||||
const cardId = Cards.direct.insert(cardToCreate);
|
|
||||||
Activities.direct.insert({
|
|
||||||
activityType: 'importCard',
|
|
||||||
boardId: cardToCreate.boardId,
|
|
||||||
cardId,
|
|
||||||
createdAt: dateOfImport,
|
|
||||||
listId: cardToCreate.listId,
|
|
||||||
source: {
|
|
||||||
id: trelloCard.id,
|
|
||||||
system: 'Trello',
|
|
||||||
url: trelloCard.url,
|
|
||||||
},
|
|
||||||
// we attribute the import to current user, not the one from the original card
|
|
||||||
userId: Meteor.userId(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// 7. parse actions and add comments
|
|
||||||
trelloCard.actions.forEach((currentAction) => {
|
|
||||||
if(currentAction.type === 'commentCard') {
|
|
||||||
const commentToCreate = {
|
|
||||||
boardId: list.boardId,
|
|
||||||
cardId,
|
|
||||||
createdAt: currentAction.date,
|
|
||||||
text: currentAction.data.text,
|
|
||||||
// XXX use the original comment user instead
|
|
||||||
userId: Meteor.userId(),
|
|
||||||
};
|
|
||||||
const commentId = CardComments.direct.insert(commentToCreate);
|
|
||||||
Activities.direct.insert({
|
|
||||||
activityType: 'addComment',
|
|
||||||
boardId: commentToCreate.boardId,
|
|
||||||
cardId: commentToCreate.cardId,
|
|
||||||
commentId,
|
|
||||||
createdAt: commentToCreate.createdAt,
|
|
||||||
userId: commentToCreate.userId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return cardId;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue