| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629 | import { ReactiveCache } from '/imports/reactiveCache';// exporter maybe is broken since Gridfs introduced, add fs and pathimport { createWorkbook } from './createWorkbook';class ExporterCardPDF {  constructor(boardId) {    this._boardId = boardId;  }  build(res) {    /*        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,          ReactiveCache.getBoard(this._boardId, {            fields: {              stars: 0,            },          }),        );        result.lists = ReactiveCache.getLists(byBoard, noBoardId);        result.cards = ReactiveCache.getCards(byBoardNoLinked, noBoardId);        result.swimlanes = ReactiveCache.getSwimlanes(byBoard, noBoardId);        result.customFields = ReactiveCache.getCustomFields(          {            boardIds: {              $in: [this.boardId],            },          },          {            fields: {              boardId: 0,            },          },        );        result.comments = ReactiveCache.getCardComments(byBoard, noBoardId);        result.activities = ReactiveCache.getActivities(byBoard, noBoardId);        result.rules = ReactiveCache.getRules(byBoard, noBoardId);        result.checklists = [];        result.checklistItems = [];        result.subtaskItems = [];        result.triggers = [];        result.actions = [];        result.cards.forEach((card) => {          result.checklists.push(            ...ReactiveCache.getChecklists({              cardId: card._id,            }),          );          result.checklistItems.push(            ...ReactiveCache.getChecklistItems({              cardId: card._id,            }),          );          result.subtaskItems.push(            ...ReactiveCache.getCards({              parentId: card._id,            }),          );        });        result.rules.forEach((rule) => {          result.triggers.push(            ...ReactiveCache.getTriggers(              {                _id: rule.triggerId,              },              noBoardId,            ),          );          result.actions.push(            ...ReactiveCache.getActions(              {                _id: rule.actionId,              },              noBoardId,            ),          );        });        // 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;            });          }          if (card.assignees) {            card.assignees.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.initials': 1,            'profile.avatarUrl': 1,          },        };        result.users = ReactiveCache.getUsers(byUserIds, userFields)          .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;          });        //init exceljs workbook        const workbook = createWorkbook();        workbook.creator = TAPi18n.__('export-board');        workbook.lastModifiedBy = TAPi18n.__('export-board');        workbook.created = new Date();        workbook.modified = new Date();        workbook.lastPrinted = new Date();        const filename = `${result.title}.xlsx`;        //init worksheet        const worksheet = workbook.addWorksheet(result.title, {          properties: {            tabColor: {              argb: 'FFC0000',            },          },          pageSetup: {            paperSize: 9,            orientation: 'landscape',          },        });        //get worksheet        const ws = workbook.getWorksheet(result.title);        ws.properties.defaultRowHeight = 20;        //init columns        //Excel font. Western: Arial. zh-CN: 宋体        ws.columns = [          {            key: 'a',            width: 14,          },          {            key: 'b',            width: 40,          },          {            key: 'c',            width: 60,          },          {            key: 'd',            width: 40,          },          {            key: 'e',            width: 20,          },          {            key: 'f',            width: 20,            style: {              font: {                name: TAPi18n.__('excel-font'),                size: '10',              },              numFmt: 'yyyy/mm/dd hh:mm:ss',            },          },          {            key: 'g',            width: 20,            style: {              font: {                name: TAPi18n.__('excel-font'),                size: '10',              },              numFmt: 'yyyy/mm/dd hh:mm:ss',            },          },          {            key: 'h',            width: 20,            style: {              font: {                name: TAPi18n.__('excel-font'),                size: '10',              },              numFmt: 'yyyy/mm/dd hh:mm:ss',            },          },          {            key: 'i',            width: 20,            style: {              font: {                name: TAPi18n.__('excel-font'),                size: '10',              },              numFmt: 'yyyy/mm/dd hh:mm:ss',            },          },          {            key: 'j',            width: 20,            style: {              font: {                name: TAPi18n.__('excel-font'),                size: '10',              },              numFmt: 'yyyy/mm/dd hh:mm:ss',            },          },          {            key: 'k',            width: 20,            style: {              font: {                name: TAPi18n.__('excel-font'),                size: '10',              },              numFmt: 'yyyy/mm/dd hh:mm:ss',            },          },          {            key: 'l',            width: 20,          },          {            key: 'm',            width: 20,          },          {            key: 'n',            width: 20,          },          {            key: 'o',            width: 20,          },          {            key: 'p',            width: 20,          },          {            key: 'q',            width: 20,          },          {            key: 'r',            width: 20,          },        ];        //add title line        ws.mergeCells('A1:H1');        ws.getCell('A1').value = result.title;        ws.getCell('A1').style = {          font: {            name: TAPi18n.__('excel-font'),            size: '20',          },        };        ws.getCell('A1').alignment = {          vertical: 'middle',          horizontal: 'center',        };        ws.getRow(1).height = 40;        //get member and assignee info        let jmem = '';        let jassig = '';        const jmeml = {};        const jassigl = {};        for (const i in result.users) {          jmem = `${jmem + result.users[i].username},`;          jmeml[result.users[i]._id] = result.users[i].username;        }        jmem = jmem.substr(0, jmem.length - 1);        for (const ia in result.users) {          jassig = `${jassig + result.users[ia].username},`;          jassigl[result.users[ia]._id] = result.users[ia].username;        }        jassig = jassig.substr(0, jassig.length - 1);        //get kanban list info        const jlist = {};        for (const klist in result.lists) {          jlist[result.lists[klist]._id] = result.lists[klist].title;        }        //get kanban swimlanes info        const jswimlane = {};        for (const kswimlane in result.swimlanes) {          jswimlane[result.swimlanes[kswimlane]._id] =            result.swimlanes[kswimlane].title;        }        //get kanban label info        const jlabel = {};        var isFirst = 1;        for (const klabel in result.labels) {          // console.log(klabel);          if (isFirst == 0) {            jlabel[result.labels[klabel]._id] = `,${result.labels[klabel].name}`;          } else {            isFirst = 0;            jlabel[result.labels[klabel]._id] = result.labels[klabel].name;          }        }        //add data +8 hours        function addTZhours(jdate) {          const curdate = new Date(jdate);          const checkCorrectDate = moment(curdate);          if (checkCorrectDate.isValid()) {            return curdate;          } else {            return ' ';          }          ////Do not add 8 hours to GMT. Use GMT instead.          ////Could not yet figure out how to get localtime.          //return new Date(curdate.setHours(curdate.getHours() + 8));          //return curdate;        }        //add blank row        ws.addRow().values = ['', '', '', '', '', ''];        //add kanban info        ws.addRow().values = [          TAPi18n.__('createdAt'),          addTZhours(result.createdAt),          TAPi18n.__('modifiedAt'),          addTZhours(result.modifiedAt),          TAPi18n.__('members'),          jmem,        ];        ws.getRow(3).font = {          name: TAPi18n.__('excel-font'),          size: 10,          bold: true,        };        ws.mergeCells('F3:R3');        ws.getCell('B3').style = {          font: {            name: TAPi18n.__('excel-font'),            size: '10',            bold: true,          },          numFmt: 'yyyy/mm/dd hh:mm:ss',        };        //cell center        function cellCenter(cellno) {          ws.getCell(cellno).alignment = {            vertical: 'middle',            horizontal: 'center',            wrapText: true,          };        }        function cellLeft(cellno) {          ws.getCell(cellno).alignment = {            vertical: 'middle',            horizontal: 'left',            wrapText: true,          };        }        cellCenter('A3');        cellCenter('B3');        cellCenter('C3');        cellCenter('D3');        cellCenter('E3');        cellLeft('F3');        ws.getRow(3).height = 20;        //all border        function allBorder(cellno) {          ws.getCell(cellno).border = {            top: {              style: 'thin',            },            left: {              style: 'thin',            },            bottom: {              style: 'thin',            },            right: {              style: 'thin',            },          };        }        allBorder('A3');        allBorder('B3');        allBorder('C3');        allBorder('D3');        allBorder('E3');        allBorder('F3');        //add blank row        ws.addRow().values = [          '',          '',          '',          '',          '',          '',          '',          '',          '',          '',          '',          '',          '',          '',          '',        ];        //add card title        //ws.addRow().values = ['编号', '标题', '创建人', '创建时间', '更新时间', '列表', '成员', '描述', '标签'];        //this is where order in which the excel file generates        ws.addRow().values = [          TAPi18n.__('number'),          TAPi18n.__('title'),          TAPi18n.__('description'),          TAPi18n.__('parent-card'),          TAPi18n.__('owner'),          TAPi18n.__('createdAt'),          TAPi18n.__('last-modified-at'),          TAPi18n.__('card-received'),          TAPi18n.__('card-start'),          TAPi18n.__('card-due'),          TAPi18n.__('card-end'),          TAPi18n.__('list'),          TAPi18n.__('swimlane'),          TAPi18n.__('assignee'),          TAPi18n.__('members'),          TAPi18n.__('labels'),          TAPi18n.__('overtime-hours'),          TAPi18n.__('spent-time-hours'),        ];        ws.getRow(5).height = 20;        allBorder('A5');        allBorder('B5');        allBorder('C5');        allBorder('D5');        allBorder('E5');        allBorder('F5');        allBorder('G5');        allBorder('H5');        allBorder('I5');        allBorder('J5');        allBorder('K5');        allBorder('L5');        allBorder('M5');        allBorder('N5');        allBorder('O5');        allBorder('P5');        allBorder('Q5');        allBorder('R5');        cellCenter('A5');        cellCenter('B5');        cellCenter('C5');        cellCenter('D5');        cellCenter('E5');        cellCenter('F5');        cellCenter('G5');        cellCenter('H5');        cellCenter('I5');        cellCenter('J5');        cellCenter('K5');        cellCenter('L5');        cellCenter('M5');        cellCenter('N5');        cellCenter('O5');        cellCenter('P5');        cellCenter('Q5');        cellCenter('R5');        ws.getRow(5).font = {          name: TAPi18n.__('excel-font'),          size: 12,          bold: true,        };        //add blank row        //add card info        for (const i in result.cards) {          const jcard = result.cards[i];          //get member info          let jcmem = '';          for (const j in jcard.members) {            jcmem += jmeml[jcard.members[j]];            jcmem += ' ';          }          //get assignee info          let jcassig = '';          for (const ja in jcard.assignees) {            jcassig += jassigl[jcard.assignees[ja]];            jcassig += ' ';          }          //get card label info          let jclabel = '';          for (const jl in jcard.labelIds) {            jclabel += jlabel[jcard.labelIds[jl]];            jclabel += ' ';          }          //get parent name          if (jcard.parentId) {            const parentCard = result.cards.find(              (card) => card._id === jcard.parentId,            );            jcard.parentCardTitle = parentCard ? parentCard.title : '';          }          //add card detail          const t = Number(i) + 1;          ws.addRow().values = [            t.toString(),            jcard.title,            jcard.description,            jcard.parentCardTitle,            jmeml[jcard.userId],            addTZhours(jcard.createdAt),            addTZhours(jcard.dateLastActivity),            addTZhours(jcard.receivedAt),            addTZhours(jcard.startAt),            addTZhours(jcard.dueAt),            addTZhours(jcard.endAt),            jlist[jcard.listId],            jswimlane[jcard.swimlaneId],            jcassig,            jcmem,            jclabel,            jcard.isOvertime ? 'true' : 'false',            jcard.spentTime,          ];          const y = Number(i) + 6;          //ws.getRow(y).height = 25;          allBorder(`A${y}`);          allBorder(`B${y}`);          allBorder(`C${y}`);          allBorder(`D${y}`);          allBorder(`E${y}`);          allBorder(`F${y}`);          allBorder(`G${y}`);          allBorder(`H${y}`);          allBorder(`I${y}`);          allBorder(`J${y}`);          allBorder(`K${y}`);          allBorder(`L${y}`);          allBorder(`M${y}`);          allBorder(`N${y}`);          allBorder(`O${y}`);          allBorder(`P${y}`);          allBorder(`Q${y}`);          allBorder(`R${y}`);          cellCenter(`A${y}`);          ws.getCell(`B${y}`).alignment = {            wrapText: true,          };          ws.getCell(`C${y}`).alignment = {            wrapText: true,          };          ws.getCell(`M${y}`).alignment = {            wrapText: true,          };          ws.getCell(`N${y}`).alignment = {            wrapText: true,          };          ws.getCell(`O${y}`).alignment = {            wrapText: true,          };        }        workbook.xlsx.write(res).then(function () {});        */    var doc = new PDFDocument({size: 'A4', margin: 50});    doc.fontSize(12);    doc.text('Some test text', 10, 30, {align: 'center', width: 200});    this.response.writeHead(200, {      'Content-type': 'application/pdf',      'Content-Disposition': "attachment; filename=test.pdf"    });    this.response.end( doc.outputSync() );  }  canExport(user) {    const board = ReactiveCache.getBoard(this._boardId);    return board && board.isVisibleBy(user);  }}export { ExporterCardPDF };
 |