|  | @@ -1,36 +1,349 @@
 | 
	
		
			
				|  |  | -Meteor.methods({
 | 
	
		
			
				|  |  | -  importTrelloCard(trelloCard, listId, sortIndex) {
 | 
	
		
			
				|  |  | -    // 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();
 | 
	
		
			
				|  |  | +const DateString = Match.Where(function (dateAsString) {
 | 
	
		
			
				|  |  | +  check(dateAsString, String);
 | 
	
		
			
				|  |  | +  return moment(dateAsString, moment.ISO_8601).isValid();
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class TrelloCreator {
 | 
	
		
			
				|  |  | +  constructor() {
 | 
	
		
			
				|  |  | +    // The object creation dates, indexed by Trello id (so we only parse actions
 | 
	
		
			
				|  |  | +    // once!)
 | 
	
		
			
				|  |  | +    this.createdAt = {
 | 
	
		
			
				|  |  | +      board: null,
 | 
	
		
			
				|  |  | +      cards: {},
 | 
	
		
			
				|  |  | +      lists: {},
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    // Map of labels Trello ID => Wekan ID
 | 
	
		
			
				|  |  | +    this.labels = {};
 | 
	
		
			
				|  |  | +    // Map of lists Trello ID => Wekan ID
 | 
	
		
			
				|  |  | +    this.lists = {};
 | 
	
		
			
				|  |  | +    // The comments, indexed by Trello card id (to map when importing cards)
 | 
	
		
			
				|  |  | +    this.comments = {};
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  checkActions(trelloActions) {
 | 
	
		
			
				|  |  | +    check(trelloActions, [Match.ObjectIncluding({
 | 
	
		
			
				|  |  | +      data: Object,
 | 
	
		
			
				|  |  | +      date: DateString,
 | 
	
		
			
				|  |  | +      type: String,
 | 
	
		
			
				|  |  | +    })]);
 | 
	
		
			
				|  |  | +    // XXX we could perform more thorough checks based on action type
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  checkBoard(trelloBoard) {
 | 
	
		
			
				|  |  | +    check(trelloBoard, Match.ObjectIncluding({
 | 
	
		
			
				|  |  | +      closed: Boolean,
 | 
	
		
			
				|  |  | +      name: String,
 | 
	
		
			
				|  |  | +      prefs: Match.ObjectIncluding({
 | 
	
		
			
				|  |  | +        // XXX refine control by validating 'background' against a list of
 | 
	
		
			
				|  |  | +        // allowed values (is it worth the maintenance?)
 | 
	
		
			
				|  |  | +        background: String,
 | 
	
		
			
				|  |  | +        permissionLevel: Match.Where((value) => {
 | 
	
		
			
				|  |  | +          return ['org', 'private', 'public'].indexOf(value)>= 0;
 | 
	
		
			
				|  |  | +        }),
 | 
	
		
			
				|  |  | +      }),
 | 
	
		
			
				|  |  | +    }));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  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) {
 | 
	
		
			
				|  |  | +    check(trelloLists, [Match.ObjectIncluding({
 | 
	
		
			
				|  |  | +      closed: Boolean,
 | 
	
		
			
				|  |  | +      name: String,
 | 
	
		
			
				|  |  | +    })]);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // You must call parseActions before calling this one.
 | 
	
		
			
				|  |  | +  createBoardAndLabels(trelloBoard) {
 | 
	
		
			
				|  |  | +    const createdAt = this.createdAt.board;
 | 
	
		
			
				|  |  | +    const boardToCreate = {
 | 
	
		
			
				|  |  | +      archived: trelloBoard.closed,
 | 
	
		
			
				|  |  | +      color: this.getColor(trelloBoard.prefs.background),
 | 
	
		
			
				|  |  | +      createdAt,
 | 
	
		
			
				|  |  | +      labels: [],
 | 
	
		
			
				|  |  | +      members: [{
 | 
	
		
			
				|  |  | +        userId: Meteor.userId(),
 | 
	
		
			
				|  |  | +        isAdmin: true,
 | 
	
		
			
				|  |  | +        isActive: true,
 | 
	
		
			
				|  |  | +      }],
 | 
	
		
			
				|  |  | +      permission: this.getPermission(trelloBoard.prefs.permissionLevel),
 | 
	
		
			
				|  |  | +      slug: getSlug(trelloBoard.name) || 'board',
 | 
	
		
			
				|  |  | +      stars: 0,
 | 
	
		
			
				|  |  | +      title: trelloBoard.name,
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    trelloBoard.labels.forEach((label) => {
 | 
	
		
			
				|  |  | +      const labelToCreate = {
 | 
	
		
			
				|  |  | +        _id: Random.id(6),
 | 
	
		
			
				|  |  | +        color: label.color,
 | 
	
		
			
				|  |  | +        name: label.name,
 | 
	
		
			
				|  |  | +      };
 | 
	
		
			
				|  |  | +      // We need to remember them by Trello ID, as this is the only ref we have
 | 
	
		
			
				|  |  | +      // when importing cards.
 | 
	
		
			
				|  |  | +      this.labels[label.id] = labelToCreate._id;
 | 
	
		
			
				|  |  | +      boardToCreate.labels.push(labelToCreate);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    const now = new Date();
 | 
	
		
			
				|  |  | +    const boardId = Boards.direct.insert(boardToCreate);
 | 
	
		
			
				|  |  | +    Boards.direct.update(boardId, {$set: {modifiedAt: now}});
 | 
	
		
			
				|  |  | +    // log activity
 | 
	
		
			
				|  |  | +    Activities.direct.insert({
 | 
	
		
			
				|  |  | +      activityType: 'importBoard',
 | 
	
		
			
				|  |  | +      boardId,
 | 
	
		
			
				|  |  | +      createdAt: now,
 | 
	
		
			
				|  |  | +      source: {
 | 
	
		
			
				|  |  | +        id: trelloBoard.id,
 | 
	
		
			
				|  |  | +        system: 'Trello',
 | 
	
		
			
				|  |  | +        url: trelloBoard.url,
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      // We attribute the import to current user, not the one from the original
 | 
	
		
			
				|  |  | +      // object.
 | 
	
		
			
				|  |  | +      userId: Meteor.userId(),
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    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) {
 | 
	
		
			
				|  |  | +    const result = [];
 | 
	
		
			
				|  |  | +    trelloCards.forEach((card) => {
 | 
	
		
			
				|  |  | +      const cardToCreate = {
 | 
	
		
			
				|  |  | +        archived: card.closed,
 | 
	
		
			
				|  |  | +        boardId,
 | 
	
		
			
				|  |  | +        createdAt: new Date(this.createdAt.cards[card.id]  || Date.now()),
 | 
	
		
			
				|  |  | +        dateLastActivity: new Date(),
 | 
	
		
			
				|  |  | +        description: card.desc,
 | 
	
		
			
				|  |  | +        listId: this.lists[card.idList],
 | 
	
		
			
				|  |  | +        sort: card.pos,
 | 
	
		
			
				|  |  | +        title: card.name,
 | 
	
		
			
				|  |  | +        // XXX use the original user?
 | 
	
		
			
				|  |  | +        userId: Meteor.userId(),
 | 
	
		
			
				|  |  | +      };
 | 
	
		
			
				|  |  | +      // add labels
 | 
	
		
			
				|  |  | +      if (card.idLabels) {
 | 
	
		
			
				|  |  | +        cardToCreate.labelIds = card.idLabels.map((trelloId) => {
 | 
	
		
			
				|  |  | +          return this.labels[trelloId];
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      // insert card
 | 
	
		
			
				|  |  | +      const cardId = Cards.direct.insert(cardToCreate);
 | 
	
		
			
				|  |  | +      // log activity
 | 
	
		
			
				|  |  | +      Activities.direct.insert({
 | 
	
		
			
				|  |  | +        activityType: 'importCard',
 | 
	
		
			
				|  |  | +        boardId,
 | 
	
		
			
				|  |  | +        cardId,
 | 
	
		
			
				|  |  | +        createdAt: new Date(),
 | 
	
		
			
				|  |  | +        listId: cardToCreate.listId,
 | 
	
		
			
				|  |  | +        source: {
 | 
	
		
			
				|  |  | +          id: card.id,
 | 
	
		
			
				|  |  | +          system: 'Trello',
 | 
	
		
			
				|  |  | +          url: card.url,
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        // we attribute the import to current user, not the one from the
 | 
	
		
			
				|  |  | +        // original card
 | 
	
		
			
				|  |  | +        userId: Meteor.userId(),
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      // add comments
 | 
	
		
			
				|  |  | +      const comments = this.comments[card.id];
 | 
	
		
			
				|  |  | +      if (comments) {
 | 
	
		
			
				|  |  | +        comments.forEach((comment) => {
 | 
	
		
			
				|  |  | +          const commentToCreate = {
 | 
	
		
			
				|  |  | +            boardId,
 | 
	
		
			
				|  |  | +            cardId,
 | 
	
		
			
				|  |  | +            createdAt: comment.date,
 | 
	
		
			
				|  |  | +            text: comment.data.text,
 | 
	
		
			
				|  |  | +            // XXX use the original comment user instead
 | 
	
		
			
				|  |  | +            userId: Meteor.userId(),
 | 
	
		
			
				|  |  | +          };
 | 
	
		
			
				|  |  | +          // dateLastActivity will be set from activity insert, no need to
 | 
	
		
			
				|  |  | +          // update it ourselves
 | 
	
		
			
				|  |  | +          const commentId = CardComments.direct.insert(commentToCreate);
 | 
	
		
			
				|  |  | +          Activities.direct.insert({
 | 
	
		
			
				|  |  | +            activityType: 'addComment',
 | 
	
		
			
				|  |  | +            boardId: commentToCreate.boardId,
 | 
	
		
			
				|  |  | +            cardId: commentToCreate.cardId,
 | 
	
		
			
				|  |  | +            commentId,
 | 
	
		
			
				|  |  | +            createdAt: commentToCreate.createdAt,
 | 
	
		
			
				|  |  | +            userId: commentToCreate.userId,
 | 
	
		
			
				|  |  | +          });
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      // XXX add attachments
 | 
	
		
			
				|  |  | +      result.push(cardId);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    return result;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  getColor(trelloColorCode) {
 | 
	
		
			
				|  |  | +    // trello color name => wekan color
 | 
	
		
			
				|  |  | +    const mapColors = {
 | 
	
		
			
				|  |  | +      'blue': 'belize',
 | 
	
		
			
				|  |  | +      'orange': 'pumpkin',
 | 
	
		
			
				|  |  | +      'green': 'nephritis',
 | 
	
		
			
				|  |  | +      'red': 'pomegranate',
 | 
	
		
			
				|  |  | +      'purple': 'wisteria',
 | 
	
		
			
				|  |  | +      'pink': 'pomegranate',
 | 
	
		
			
				|  |  | +      'lime': 'nephritis',
 | 
	
		
			
				|  |  | +      'sky': 'belize',
 | 
	
		
			
				|  |  | +      'grey': 'midnight',
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const wekanColor = mapColors[trelloColorCode];
 | 
	
		
			
				|  |  | +    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) {
 | 
	
		
			
				|  |  | +    trelloActions.forEach((action) => {
 | 
	
		
			
				|  |  | +      switch (action.type) {
 | 
	
		
			
				|  |  | +      case 'createBoard':
 | 
	
		
			
				|  |  | +        this.createdAt.board = action.date;
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case 'createCard':
 | 
	
		
			
				|  |  | +        const cardId = action.data.card.id;
 | 
	
		
			
				|  |  | +        this.createdAt.cards[cardId] = action.date;
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case 'createList':
 | 
	
		
			
				|  |  | +        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;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Meteor.methods({
 | 
	
		
			
				|  |  | +  importTrelloBoard(trelloBoard, data) {
 | 
	
		
			
				|  |  | +    const trelloCreator = new TrelloCreator();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 1. check all parameters are ok from a syntax point of view
 | 
	
		
			
				|  |  |      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(listId, String);
 | 
	
		
			
				|  |  | -      check(sortIndex, Number);
 | 
	
		
			
				|  |  | +      // we don't use additional data - this should be an empty object
 | 
	
		
			
				|  |  | +      check(data, {});
 | 
	
		
			
				|  |  | +      trelloCreator.checkActions(trelloBoard.actions);
 | 
	
		
			
				|  |  | +      trelloCreator.checkBoard(trelloBoard);
 | 
	
		
			
				|  |  | +      trelloCreator.checkLabels(trelloBoard.labels);
 | 
	
		
			
				|  |  | +      trelloCreator.checkLists(trelloBoard.lists);
 | 
	
		
			
				|  |  | +      trelloCreator.checkCards(trelloBoard.cards);
 | 
	
		
			
				|  |  |      } catch (e) {
 | 
	
		
			
				|  |  |        throw new Meteor.Error('error-json-schema');
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    // 2. check parameters are ok from a business point of view (exist &
 | 
	
		
			
				|  |  | +    // authorized) nothing to check, everyone can import boards in their account
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 3. create all elements
 | 
	
		
			
				|  |  | +    trelloCreator.parseActions(trelloBoard.actions);
 | 
	
		
			
				|  |  | +    const boardId = trelloCreator.createBoardAndLabels(trelloBoard);
 | 
	
		
			
				|  |  | +    trelloCreator.createLists(trelloBoard.lists, boardId);
 | 
	
		
			
				|  |  | +    trelloCreator.createCardsAndComments(trelloBoard.cards, boardId);
 | 
	
		
			
				|  |  | +    // XXX add members
 | 
	
		
			
				|  |  | +    return boardId;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  importTrelloCard(trelloCard, data) {
 | 
	
		
			
				|  |  | +    const trelloCreator = new TrelloCreator();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 1. check parameters are ok from a syntax point of view
 | 
	
		
			
				|  |  | +    try {
 | 
	
		
			
				|  |  | +      check(data, {
 | 
	
		
			
				|  |  | +        listId: String,
 | 
	
		
			
				|  |  | +        sortIndex: Number,
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      trelloCreator.checkCards([trelloCard]);
 | 
	
		
			
				|  |  | +      trelloCreator.checkLabels(trelloCard.labels);
 | 
	
		
			
				|  |  | +      trelloCreator.checkActions(trelloCard.actions);
 | 
	
		
			
				|  |  | +    } catch(e) {
 | 
	
		
			
				|  |  | +      throw new Meteor.Error('error-json-schema');
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      // 2. check parameters are ok from a business point of view (exist &
 | 
	
		
			
				|  |  |      // authorized)
 | 
	
		
			
				|  |  | -    const list = Lists.findOne(listId);
 | 
	
		
			
				|  |  | +    const list = Lists.findOne(data.listId);
 | 
	
		
			
				|  |  |      if (!list) {
 | 
	
		
			
				|  |  |        throw new Meteor.Error('error-list-doesNotExist');
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -40,83 +353,12 @@ Meteor.methods({
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // 3. map all fields for the card to create
 | 
	
		
			
				|  |  | -    const dateOfImport = new Date();
 | 
	
		
			
				|  |  | -    const cardToCreate = {
 | 
	
		
			
				|  |  | -      archived: trelloCard.closed,
 | 
	
		
			
				|  |  | -      boardId: list.boardId,
 | 
	
		
			
				|  |  | -      // this is a default date, we'll fetch the actual one from the actions array
 | 
	
		
			
				|  |  | -      createdAt: dateOfImport,
 | 
	
		
			
				|  |  | -      dateLastActivity: dateOfImport,
 | 
	
		
			
				|  |  | -      description: trelloCard.desc,
 | 
	
		
			
				|  |  | -      labelIds: [],
 | 
	
		
			
				|  |  | -      listId: list._id,
 | 
	
		
			
				|  |  | -      sort: 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 { name, color } = currentLabel;
 | 
	
		
			
				|  |  | -      // `addLabel` won't create dublicate labels (ie labels with the same name
 | 
	
		
			
				|  |  | -      // and color) so here it is used more in a "enforceLabelExistence" way.
 | 
	
		
			
				|  |  | -      list.board().addLabel(name, color);
 | 
	
		
			
				|  |  | -      const { _id: labelId } = list.board().getLabel(name, color);
 | 
	
		
			
				|  |  | -      if (labelId) {
 | 
	
		
			
				|  |  | -        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;
 | 
	
		
			
				|  |  | +    // 3. create all elements
 | 
	
		
			
				|  |  | +    trelloCreator.lists[trelloCard.idList] = data.listId;
 | 
	
		
			
				|  |  | +    trelloCreator.parseActions(trelloCard.actions);
 | 
	
		
			
				|  |  | +    const board = list.board();
 | 
	
		
			
				|  |  | +    trelloCreator.createLabels(trelloCard.labels, board);
 | 
	
		
			
				|  |  | +    const cardIds = trelloCreator.createCardsAndComments([trelloCard], board._id);
 | 
	
		
			
				|  |  | +    return cardIds[0];
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  });
 |