| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 | 
							- import moment from 'moment';
 
- const Papa = require('papaparse');
 
- import { TAPi18n } from '/imports/i18n';
 
- //const stringify = require('csv-stringify');
 
- // exporter maybe is broken since Gridfs introduced, add fs and path
 
- export class Exporter {
 
-   constructor(boardId, attachmentId) {
 
-     this._boardId = boardId;
 
-     this._attachmentId = attachmentId;
 
-   }
 
-   build() {
 
-     const fs = Npm.require('fs');
 
-     const os = Npm.require('os');
 
-     const path = Npm.require('path');
 
-     const byBoard = { boardId: this._boardId };
 
-     const byBoardNoLinked = {
 
-       boardId: this._boardId,
 
-       linkedId: { $in: ['', null] },
 
-     };
 
-     // we do not want to retrieve boardId in related elements
 
-     const noBoardId = {
 
-       fields: {
 
-         boardId: 0,
 
-       },
 
-     };
 
-     const result = {
 
-       _format: 'wekan-board-1.0.0',
 
-     };
 
-     _.extend(
 
-       result,
 
-       Boards.findOne(this._boardId, {
 
-         fields: {
 
-           stars: 0,
 
-         },
 
-       }),
 
-     );
 
-     // [Old] for attachments we only export IDs and absolute url to original doc
 
-     // [New] Encode attachment to base64
 
-     const getBase64Data = function (doc, callback) {
 
-       let buffer = Buffer.allocUnsafe(0);
 
-       buffer.fill(0);
 
-       // callback has the form function (err, res) {}
 
-       const tmpFile = path.join(
 
-         os.tmpdir(),
 
-         `tmpexport${process.pid}${Math.random()}`,
 
-       );
 
-       const tmpWriteable = fs.createWriteStream(tmpFile);
 
-       const readStream = doc.createReadStream();
 
-       readStream.on('data', function (chunk) {
 
-         buffer = Buffer.concat([buffer, chunk]);
 
-       });
 
-       readStream.on('error', function () {
 
-         callback(null, null);
 
-       });
 
-       readStream.on('end', function () {
 
-         // done
 
-         fs.unlink(tmpFile, () => {
 
-           //ignored
 
-         });
 
-         callback(null, buffer.toString('base64'));
 
-       });
 
-       readStream.pipe(tmpWriteable);
 
-     };
 
-     const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
 
-     const byBoardAndAttachment = this._attachmentId
 
-       ? { boardId: this._boardId, _id: this._attachmentId }
 
-       : byBoard;
 
-     result.attachments = Attachments.find(byBoardAndAttachment)
 
-       .fetch()
 
-       .map((attachment) => {
 
-         let filebase64 = null;
 
-         filebase64 = getBase64DataSync(attachment);
 
-         return {
 
-           _id: attachment._id,
 
-           cardId: attachment.meta.cardId,
 
-           //url: FlowRouter.url(attachment.url()),
 
-           file: filebase64,
 
-           name: attachment.name,
 
-           type: attachment.type,
 
-         };
 
-       });
 
-     //When has a especific valid attachment return the single element
 
-     if (this._attachmentId) {
 
-       return result.attachments.length > 0 ? result.attachments[0] : {};
 
-     }
 
-     result.lists = Lists.find(byBoard, noBoardId).fetch();
 
-     result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
 
-     result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
 
-     result.customFields = CustomFields.find(
 
-       { boardIds: this._boardId },
 
-       { fields: { boardIds: 0 } },
 
-     ).fetch();
 
-     result.comments = CardComments.find(byBoard, noBoardId).fetch();
 
-     result.activities = Activities.find(byBoard, noBoardId).fetch();
 
-     result.rules = Rules.find(byBoard, noBoardId).fetch();
 
-     result.checklists = [];
 
-     result.checklistItems = [];
 
-     result.subtaskItems = [];
 
-     result.triggers = [];
 
-     result.actions = [];
 
-     result.cards.forEach((card) => {
 
-       result.checklists.push(
 
-         ...Checklists.find({
 
-           cardId: card._id,
 
-         }).fetch(),
 
-       );
 
-       result.checklistItems.push(
 
-         ...ChecklistItems.find({
 
-           cardId: card._id,
 
-         }).fetch(),
 
-       );
 
-       result.subtaskItems.push(
 
-         ...Cards.find({
 
-           parentId: card._id,
 
-         }).fetch(),
 
-       );
 
-     });
 
-     result.rules.forEach((rule) => {
 
-       result.triggers.push(
 
-         ...Triggers.find(
 
-           {
 
-             _id: rule.triggerId,
 
-           },
 
-           noBoardId,
 
-         ).fetch(),
 
-       );
 
-       result.actions.push(
 
-         ...Actions.find(
 
-           {
 
-             _id: rule.actionId,
 
-           },
 
-           noBoardId,
 
-         ).fetch(),
 
-       );
 
-     });
 
-     // we also have to export some user data - as the other elements only
 
-     // include id but we have to be careful:
 
-     // 1- only exports users that are linked somehow to that board
 
-     // 2- do not export any sensitive information
 
-     const users = {};
 
-     result.members.forEach((member) => {
 
-       users[member.userId] = true;
 
-     });
 
-     result.lists.forEach((list) => {
 
-       users[list.userId] = true;
 
-     });
 
-     result.cards.forEach((card) => {
 
-       users[card.userId] = true;
 
-       if (card.members) {
 
-         card.members.forEach((memberId) => {
 
-           users[memberId] = true;
 
-         });
 
-       }
 
-     });
 
-     result.comments.forEach((comment) => {
 
-       users[comment.userId] = true;
 
-     });
 
-     result.activities.forEach((activity) => {
 
-       users[activity.userId] = true;
 
-     });
 
-     result.checklists.forEach((checklist) => {
 
-       users[checklist.userId] = true;
 
-     });
 
-     const byUserIds = {
 
-       _id: {
 
-         $in: Object.getOwnPropertyNames(users),
 
-       },
 
-     };
 
-     // we use whitelist to be sure we do not expose inadvertently
 
-     // some secret fields that gets added to User later.
 
-     const userFields = {
 
-       fields: {
 
-         _id: 1,
 
-         username: 1,
 
-         'profile.fullname': 1,
 
-         'profile.initials': 1,
 
-         'profile.avatarUrl': 1,
 
-       },
 
-     };
 
-     result.users = Users.find(byUserIds, userFields)
 
-       .fetch()
 
-       .map((user) => {
 
-         // user avatar is stored as a relative url, we export absolute
 
-         if ((user.profile || {}).avatarUrl) {
 
-           user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
 
-         }
 
-         return user;
 
-       });
 
-     return result;
 
-   }
 
-   buildCsv(userDelimiter = ',', userLanguage='en') {
 
-     const result = this.build();
 
-     const columnHeaders = [];
 
-     const cardRows = [];
 
-     const papaconfig = {
 
-       quotes: true,
 
-       quoteChar: '"',
 
-       escapeChar: '"',
 
-       delimiter: userDelimiter,
 
-       header: true,
 
-       newline: "\r\n",
 
-       skipEmptyLines: false,
 
-       escapeFormulae: true,
 
-     };
 
-     columnHeaders.push(
 
-       TAPi18n.__('title','',userLanguage),
 
-       TAPi18n.__('description','',userLanguage),
 
-       TAPi18n.__('list','',userLanguage),
 
-       TAPi18n.__('swimlane','',userLanguage),
 
-       TAPi18n.__('owner','',userLanguage),
 
-       TAPi18n.__('requested-by','',userLanguage),
 
-       TAPi18n.__('assigned-by','',userLanguage),
 
-       TAPi18n.__('members','',userLanguage),
 
-       TAPi18n.__('assignee','',userLanguage),
 
-       TAPi18n.__('labels','',userLanguage),
 
-       TAPi18n.__('card-start','',userLanguage),
 
-       TAPi18n.__('card-due','',userLanguage),
 
-       TAPi18n.__('card-end','',userLanguage),
 
-       TAPi18n.__('overtime-hours','',userLanguage),
 
-       TAPi18n.__('spent-time-hours','',userLanguage),
 
-       TAPi18n.__('createdAt','',userLanguage),
 
-       TAPi18n.__('last-modified-at','',userLanguage),
 
-       TAPi18n.__('last-activity','',userLanguage),
 
-       TAPi18n.__('voting','',userLanguage),
 
-       TAPi18n.__('archived','',userLanguage),
 
-     );
 
-     const customFieldMap = {};
 
-     let i = 0;
 
-     result.customFields.forEach((customField) => {
 
-       customFieldMap[customField._id] = {
 
-         position: i,
 
-         type: customField.type,
 
-       };
 
-       if (customField.type === 'dropdown') {
 
-         let options = '';
 
-         customField.settings.dropdownItems.forEach((item) => {
 
-           options = options === '' ? item.name : `${`${options}/${item.name}`}`;
 
-         });
 
-         columnHeaders.push(
 
-           `CustomField-${customField.name}-${customField.type}-${options}`,
 
-         );
 
-       } else if (customField.type === 'currency') {
 
-         columnHeaders.push(
 
-           `CustomField-${customField.name}-${customField.type}-${customField.settings.currencyCode}`,
 
-         );
 
-       } else {
 
-         columnHeaders.push(
 
-           `CustomField-${customField.name}-${customField.type}`,
 
-         );
 
-       }
 
-       i++;
 
-     });
 
-     //cardRows.push([[columnHeaders]]);
 
-     cardRows.push(columnHeaders);
 
-     result.cards.forEach((card) => {
 
-       const currentRow = [];
 
-       currentRow.push(card.title);
 
-       currentRow.push(card.description);
 
-       currentRow.push(
 
-         result.lists.find(({ _id }) => _id === card.listId).title,
 
-       );
 
-       currentRow.push(
 
-         result.swimlanes.find(({ _id }) => _id === card.swimlaneId).title,
 
-       );
 
-       currentRow.push(
 
-         result.users.find(({ _id }) => _id === card.userId).username,
 
-       );
 
-       currentRow.push(card.requestedBy ? card.requestedBy : ' ');
 
-       currentRow.push(card.assignedBy ? card.assignedBy : ' ');
 
-       let usernames = '';
 
-       card.members.forEach((memberId) => {
 
-         const user = result.users.find(({ _id }) => _id === memberId);
 
-         usernames = `${usernames + user.username} `;
 
-       });
 
-       currentRow.push(usernames.trim());
 
-       let assignees = '';
 
-       card.assignees.forEach((assigneeId) => {
 
-         const user = result.users.find(({ _id }) => _id === assigneeId);
 
-         assignees = `${assignees + user.username} `;
 
-       });
 
-       currentRow.push(assignees.trim());
 
-       let labels = '';
 
-       card.labelIds.forEach((labelId) => {
 
-         const label = result.labels.find(({ _id }) => _id === labelId);
 
-         labels = `${labels + label.name}-${label.color} `;
 
-       });
 
-       currentRow.push(labels.trim());
 
-       currentRow.push(card.startAt ? moment(card.startAt).format() : ' ');
 
-       currentRow.push(card.dueAt ? moment(card.dueAt).format() : ' ');
 
-       currentRow.push(card.endAt ? moment(card.endAt).format() : ' ');
 
-       currentRow.push(card.isOvertime ? 'true' : 'false');
 
-       currentRow.push(card.spentTime);
 
-       currentRow.push(card.createdAt ? moment(card.createdAt).format() : ' ');
 
-       currentRow.push(card.modifiedAt ? moment(card.modifiedAt).format() : ' ');
 
-       currentRow.push(
 
-         card.dateLastActivity ? moment(card.dateLastActivity).format() : ' ',
 
-       );
 
-       if (card.vote && card.vote.question !== '') {
 
-         let positiveVoters = '';
 
-         let negativeVoters = '';
 
-         card.vote.positive.forEach((userId) => {
 
-           const user = result.users.find(({ _id }) => _id === userId);
 
-           positiveVoters = `${positiveVoters + user.username} `;
 
-         });
 
-         card.vote.negative.forEach((userId) => {
 
-           const user = result.users.find(({ _id }) => _id === userId);
 
-           negativeVoters = `${negativeVoters + user.username} `;
 
-         });
 
-         const votingResult = `${
 
-           card.vote.public
 
-             ? `yes-${
 
-                 card.vote.positive.length
 
-               }-${positiveVoters.trimRight()}-no-${
 
-                 card.vote.negative.length
 
-               }-${negativeVoters.trimRight()}`
 
-             : `yes-${card.vote.positive.length}-no-${card.vote.negative.length}`
 
-         }`;
 
-         currentRow.push(`${card.vote.question}-${votingResult}`);
 
-       } else {
 
-         currentRow.push(' ');
 
-       }
 
-       currentRow.push(card.archived ? 'true' : 'false');
 
-       //Custom fields
 
-       const customFieldValuesToPush = new Array(result.customFields.length);
 
-       card.customFields.forEach((field) => {
 
-         if (field.value !== null) {
 
-           if (customFieldMap[field._id].type === 'date') {
 
-             customFieldValuesToPush[customFieldMap[field._id].position] =
 
-               moment(field.value).format();
 
-           } else if (customFieldMap[field._id].type === 'dropdown') {
 
-             const dropdownOptions = result.customFields.find(
 
-               ({ _id }) => _id === field._id,
 
-             ).settings.dropdownItems;
 
-             const fieldValue = dropdownOptions.find(
 
-               ({ _id }) => _id === field.value,
 
-             ).name;
 
-             customFieldValuesToPush[customFieldMap[field._id].position] =
 
-               fieldValue;
 
-           } else {
 
-             customFieldValuesToPush[customFieldMap[field._id].position] =
 
-               field.value;
 
-           }
 
-         }
 
-       });
 
-       for (
 
-         let valueIndex = 0;
 
-         valueIndex < customFieldValuesToPush.length;
 
-         valueIndex++
 
-       ) {
 
-         if (!(valueIndex in customFieldValuesToPush)) {
 
-           currentRow.push(' ');
 
-         } else {
 
-           currentRow.push(customFieldValuesToPush[valueIndex]);
 
-         }
 
-       }
 
-       //cardRows.push([[currentRow]]);
 
-       cardRows.push(currentRow);
 
-     });
 
-     return Papa.unparse(cardRows, papaconfig);
 
-   }
 
-   canExport(user) {
 
-     const board = Boards.findOne(this._boardId);
 
-     return board && board.isVisibleBy(user);
 
-   }
 
- }
 
 
  |