ExporterExcel.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. import { createWorkbook } from './createWorkbook';
  2. // exporter maybe is broken since Gridfs introduced, add fs and path
  3. class ExporterExcel {
  4. constructor(boardId, userLanguage) {
  5. this._boardId = boardId;
  6. this.userLanguage = userLanguage;
  7. }
  8. build(res) {
  9. const fs = Npm.require('fs');
  10. const os = Npm.require('os');
  11. const path = Npm.require('path');
  12. const byBoard = {
  13. boardId: this._boardId,
  14. };
  15. const byBoardNoLinked = {
  16. boardId: this._boardId,
  17. linkedId: {
  18. $in: ['', null],
  19. },
  20. };
  21. // we do not want to retrieve boardId in related elements
  22. const noBoardId = {
  23. fields: {
  24. boardId: 0,
  25. },
  26. };
  27. const result = {
  28. _format: 'wekan-board-1.0.0',
  29. };
  30. _.extend(
  31. result,
  32. Boards.findOne(this._boardId, {
  33. fields: {
  34. stars: 0,
  35. },
  36. }),
  37. );
  38. result.lists = Lists.find(byBoard, noBoardId).fetch();
  39. result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
  40. result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
  41. result.customFields = CustomFields.find(
  42. {
  43. boardIds: {
  44. $in: [this.boardId],
  45. },
  46. },
  47. {
  48. fields: {
  49. boardId: 0,
  50. },
  51. },
  52. ).fetch();
  53. result.comments = CardComments.find(byBoard, noBoardId).fetch();
  54. result.activities = Activities.find(byBoard, noBoardId).fetch();
  55. result.rules = Rules.find(byBoard, noBoardId).fetch();
  56. result.checklists = [];
  57. result.checklistItems = [];
  58. result.subtaskItems = [];
  59. result.triggers = [];
  60. result.actions = [];
  61. result.cards.forEach((card) => {
  62. result.checklists.push(
  63. ...Checklists.find({
  64. cardId: card._id,
  65. }).fetch(),
  66. );
  67. result.checklistItems.push(
  68. ...ChecklistItems.find({
  69. cardId: card._id,
  70. }).fetch(),
  71. );
  72. result.subtaskItems.push(
  73. ...Cards.find({
  74. parentId: card._id,
  75. }).fetch(),
  76. );
  77. });
  78. result.rules.forEach((rule) => {
  79. result.triggers.push(
  80. ...Triggers.find(
  81. {
  82. _id: rule.triggerId,
  83. },
  84. noBoardId,
  85. ).fetch(),
  86. );
  87. result.actions.push(
  88. ...Actions.find(
  89. {
  90. _id: rule.actionId,
  91. },
  92. noBoardId,
  93. ).fetch(),
  94. );
  95. });
  96. // we also have to export some user data - as the other elements only
  97. // include id but we have to be careful:
  98. // 1- only exports users that are linked somehow to that board
  99. // 2- do not export any sensitive information
  100. const users = {};
  101. result.members.forEach((member) => {
  102. users[member.userId] = true;
  103. });
  104. result.lists.forEach((list) => {
  105. users[list.userId] = true;
  106. });
  107. result.cards.forEach((card) => {
  108. users[card.userId] = true;
  109. if (card.members) {
  110. card.members.forEach((memberId) => {
  111. users[memberId] = true;
  112. });
  113. }
  114. if (card.assignees) {
  115. card.assignees.forEach((memberId) => {
  116. users[memberId] = true;
  117. });
  118. }
  119. });
  120. result.comments.forEach((comment) => {
  121. users[comment.userId] = true;
  122. });
  123. result.activities.forEach((activity) => {
  124. users[activity.userId] = true;
  125. });
  126. result.checklists.forEach((checklist) => {
  127. users[checklist.userId] = true;
  128. });
  129. const byUserIds = {
  130. _id: {
  131. $in: Object.getOwnPropertyNames(users),
  132. },
  133. };
  134. // we use whitelist to be sure we do not expose inadvertently
  135. // some secret fields that gets added to User later.
  136. const userFields = {
  137. fields: {
  138. _id: 1,
  139. username: 1,
  140. 'profile.initials': 1,
  141. 'profile.avatarUrl': 1,
  142. },
  143. };
  144. result.users = Users.find(byUserIds, userFields)
  145. .fetch()
  146. .map((user) => {
  147. // user avatar is stored as a relative url, we export absolute
  148. if ((user.profile || {}).avatarUrl) {
  149. user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
  150. }
  151. return user;
  152. });
  153. //init exceljs workbook
  154. const workbook = createWorkbook();
  155. workbook.creator = TAPi18n.__('export-board','',this.userLanguage);
  156. workbook.lastModifiedBy = TAPi18n.__('export-board','',this.userLanguage);
  157. workbook.created = new Date();
  158. workbook.modified = new Date();
  159. workbook.lastPrinted = new Date();
  160. const filename = `${result.title}.xlsx`;
  161. //init worksheet
  162. const worksheet = workbook.addWorksheet(result.title, {
  163. properties: {
  164. tabColor: {
  165. argb: 'FFC0000',
  166. },
  167. },
  168. pageSetup: {
  169. paperSize: 9,
  170. orientation: 'landscape',
  171. },
  172. });
  173. //get worksheet
  174. const ws = workbook.getWorksheet(result.title);
  175. ws.properties.defaultRowHeight = 20;
  176. //init columns
  177. //Excel font. Western: Arial. zh-CN: 宋体
  178. ws.columns = [
  179. {
  180. key: 'a',
  181. width: 14,
  182. },
  183. {
  184. key: 'b',
  185. width: 40,
  186. },
  187. {
  188. key: 'c',
  189. width: 60,
  190. },
  191. {
  192. key: 'd',
  193. width: 40,
  194. },
  195. {
  196. key: 'e',
  197. width: 20,
  198. },
  199. {
  200. key: 'f',
  201. width: 20,
  202. style: {
  203. font: {
  204. name: TAPi18n.__('excel-font'),
  205. size: '10',
  206. },
  207. numFmt: 'yyyy/mm/dd hh:mm:ss',
  208. },
  209. },
  210. {
  211. key: 'g',
  212. width: 20,
  213. style: {
  214. font: {
  215. name: TAPi18n.__('excel-font'),
  216. size: '10',
  217. },
  218. numFmt: 'yyyy/mm/dd hh:mm:ss',
  219. },
  220. },
  221. {
  222. key: 'h',
  223. width: 20,
  224. style: {
  225. font: {
  226. name: TAPi18n.__('excel-font'),
  227. size: '10',
  228. },
  229. numFmt: 'yyyy/mm/dd hh:mm:ss',
  230. },
  231. },
  232. {
  233. key: 'i',
  234. width: 20,
  235. style: {
  236. font: {
  237. name: TAPi18n.__('excel-font'),
  238. size: '10',
  239. },
  240. numFmt: 'yyyy/mm/dd hh:mm:ss',
  241. },
  242. },
  243. {
  244. key: 'j',
  245. width: 20,
  246. style: {
  247. font: {
  248. name: TAPi18n.__('excel-font'),
  249. size: '10',
  250. },
  251. numFmt: 'yyyy/mm/dd hh:mm:ss',
  252. },
  253. },
  254. {
  255. key: 'k',
  256. width: 20,
  257. style: {
  258. font: {
  259. name: TAPi18n.__('excel-font'),
  260. size: '10',
  261. },
  262. numFmt: 'yyyy/mm/dd hh:mm:ss',
  263. },
  264. },
  265. {
  266. key: 'l',
  267. width: 20,
  268. },
  269. {
  270. key: 'm',
  271. width: 20,
  272. },
  273. {
  274. key: 'n',
  275. width: 20,
  276. },
  277. {
  278. key: 'o',
  279. width: 20,
  280. },
  281. {
  282. key: 'p',
  283. width: 20,
  284. },
  285. {
  286. key: 'q',
  287. width: 20,
  288. },
  289. {
  290. key: 'r',
  291. width: 20,
  292. },
  293. ];
  294. //add title line
  295. ws.mergeCells('A1:H1');
  296. ws.getCell('A1').value = result.title;
  297. ws.getCell('A1').style = {
  298. font: {
  299. name: TAPi18n.__('excel-font'),
  300. size: '20',
  301. },
  302. };
  303. ws.getCell('A1').alignment = {
  304. vertical: 'middle',
  305. horizontal: 'center',
  306. };
  307. ws.getRow(1).height = 40;
  308. //get member and assignee info
  309. let jmem = '';
  310. let jassig = '';
  311. const jmeml = {};
  312. const jassigl = {};
  313. for (const i in result.users) {
  314. jmem = `${jmem + result.users[i].username},`;
  315. jmeml[result.users[i]._id] = result.users[i].username;
  316. }
  317. jmem = jmem.substr(0, jmem.length - 1);
  318. for (const ia in result.users) {
  319. jassig = `${jassig + result.users[ia].username},`;
  320. jassigl[result.users[ia]._id] = result.users[ia].username;
  321. }
  322. jassig = jassig.substr(0, jassig.length - 1);
  323. //get kanban list info
  324. const jlist = {};
  325. for (const klist in result.lists) {
  326. jlist[result.lists[klist]._id] = result.lists[klist].title;
  327. }
  328. //get kanban swimlanes info
  329. const jswimlane = {};
  330. for (const kswimlane in result.swimlanes) {
  331. jswimlane[result.swimlanes[kswimlane]._id] =
  332. result.swimlanes[kswimlane].title;
  333. }
  334. //get kanban label info
  335. const jlabel = {};
  336. var isFirst = 1;
  337. for (const klabel in result.labels) {
  338. // console.log(klabel);
  339. if (isFirst == 0) {
  340. jlabel[result.labels[klabel]._id] = `,${result.labels[klabel].name}`;
  341. } else {
  342. isFirst = 0;
  343. jlabel[result.labels[klabel]._id] = result.labels[klabel].name;
  344. }
  345. }
  346. //add data +8 hours
  347. function addTZhours(jdate) {
  348. const curdate = new Date(jdate);
  349. const checkCorrectDate = moment(curdate);
  350. if (checkCorrectDate.isValid()) {
  351. return curdate;
  352. } else {
  353. return ' ';
  354. }
  355. ////Do not add 8 hours to GMT. Use GMT instead.
  356. ////Could not yet figure out how to get localtime.
  357. //return new Date(curdate.setHours(curdate.getHours() + 8));
  358. //return curdate;
  359. }
  360. //cell center
  361. function cellCenter(cellno) {
  362. ws.getCell(cellno).alignment = {
  363. vertical: 'middle',
  364. horizontal: 'center',
  365. wrapText: true,
  366. };
  367. }
  368. function cellLeft(cellno) {
  369. ws.getCell(cellno).alignment = {
  370. vertical: 'middle',
  371. horizontal: 'left',
  372. wrapText: true,
  373. };
  374. }
  375. //all border
  376. function allBorder(cellno) {
  377. ws.getCell(cellno).border = {
  378. top: {
  379. style: 'thin',
  380. },
  381. left: {
  382. style: 'thin',
  383. },
  384. bottom: {
  385. style: 'thin',
  386. },
  387. right: {
  388. style: 'thin',
  389. },
  390. };
  391. }
  392. //add blank row
  393. ws.addRow().values = ['', '', '', '', '', ''];
  394. //add board description
  395. ws.addRow().values = [
  396. TAPi18n.__('description','',this.userLanguage),
  397. result.description,
  398. ];
  399. ws.mergeCells('B3:H3');
  400. ws.getRow(3).height = 40;
  401. ws.getRow(3).font = {
  402. name: TAPi18n.__('excel-font'),
  403. size: 10,
  404. };
  405. ws.getCell('A3').style = {
  406. font: {
  407. name: TAPi18n.__('excel-font'),
  408. size: '10',
  409. bold: true,
  410. },
  411. };
  412. ws.getCell(`B3`).alignment = {
  413. wrapText: true,
  414. vertical: 'middle',
  415. };
  416. cellCenter('A3');
  417. //add blank row
  418. ws.addRow().values = ['', '', '', '', '', ''];
  419. //add kanban info
  420. ws.addRow().values = [
  421. TAPi18n.__('createdAt','',this.userLanguage),
  422. addTZhours(result.createdAt),
  423. TAPi18n.__('modifiedAt','',this.userLanguage),
  424. addTZhours(result.modifiedAt),
  425. TAPi18n.__('members','',this.userLanguage),
  426. jmem,
  427. ];
  428. ws.getRow(5).font = {
  429. name: TAPi18n.__('excel-font'),
  430. size: 10,
  431. bold: true,
  432. };
  433. ws.mergeCells('F5:R5');
  434. ws.getCell('B5').style = {
  435. font: {
  436. name: TAPi18n.__('excel-font'),
  437. size: '10',
  438. bold: true,
  439. },
  440. numFmt: 'yyyy/mm/dd hh:mm:ss',
  441. };
  442. ws.getCell('D5').style = {
  443. font: {
  444. name: TAPi18n.__('excel-font'),
  445. size: '10',
  446. bold: true,
  447. },
  448. numFmt: 'yyyy/mm/dd hh:mm:ss',
  449. };
  450. cellCenter('A5');
  451. cellCenter('B5');
  452. cellCenter('C5');
  453. cellCenter('D5');
  454. cellCenter('E5');
  455. cellLeft('F5');
  456. ws.getRow(5).height = 20;
  457. allBorder('A5');
  458. allBorder('B5');
  459. allBorder('C5');
  460. allBorder('D5');
  461. allBorder('E5');
  462. allBorder('F5');
  463. //add blank row
  464. ws.addRow().values = [
  465. '',
  466. '',
  467. '',
  468. '',
  469. '',
  470. '',
  471. '',
  472. '',
  473. '',
  474. '',
  475. '',
  476. '',
  477. '',
  478. '',
  479. '',
  480. ];
  481. //add card title
  482. //ws.addRow().values = ['编号', '标题', '创建人', '创建时间', '更新时间', '列表', '成员', '描述', '标签'];
  483. //this is where order in which the excel file generates
  484. ws.addRow().values = [
  485. TAPi18n.__('number','',this.userLanguage),
  486. TAPi18n.__('title','',this.userLanguage),
  487. TAPi18n.__('description','',this.userLanguage),
  488. TAPi18n.__('parent-card','',this.userLanguage),
  489. TAPi18n.__('owner','',this.userLanguage),
  490. TAPi18n.__('createdAt','',this.userLanguage),
  491. TAPi18n.__('last-modified-at','',this.userLanguage),
  492. TAPi18n.__('card-received','',this.userLanguage),
  493. TAPi18n.__('card-start','',this.userLanguage),
  494. TAPi18n.__('card-due','',this.userLanguage),
  495. TAPi18n.__('card-end','',this.userLanguage),
  496. TAPi18n.__('list','',this.userLanguage),
  497. TAPi18n.__('swimlane','',this.userLanguage),
  498. TAPi18n.__('assignee','',this.userLanguage),
  499. TAPi18n.__('members','',this.userLanguage),
  500. TAPi18n.__('labels','',this.userLanguage),
  501. TAPi18n.__('overtime-hours','',this.userLanguage),
  502. TAPi18n.__('spent-time-hours','',this.userLanguage),
  503. ];
  504. ws.getRow(7).height = 20;
  505. allBorder('A7');
  506. allBorder('B7');
  507. allBorder('C7');
  508. allBorder('D7');
  509. allBorder('E7');
  510. allBorder('F7');
  511. allBorder('G7');
  512. allBorder('H7');
  513. allBorder('I7');
  514. allBorder('J7');
  515. allBorder('K7');
  516. allBorder('L7');
  517. allBorder('M7');
  518. allBorder('N7');
  519. allBorder('O7');
  520. allBorder('P7');
  521. allBorder('Q7');
  522. allBorder('R7');
  523. cellCenter('A7');
  524. cellCenter('B7');
  525. cellCenter('C7');
  526. cellCenter('D7');
  527. cellCenter('E7');
  528. cellCenter('F7');
  529. cellCenter('G7');
  530. cellCenter('H7');
  531. cellCenter('I7');
  532. cellCenter('J7');
  533. cellCenter('K7');
  534. cellCenter('L7');
  535. cellCenter('M7');
  536. cellCenter('N7');
  537. cellCenter('O7');
  538. cellCenter('P7');
  539. cellCenter('Q7');
  540. cellCenter('R7');
  541. ws.getRow(7).font = {
  542. name: TAPi18n.__('excel-font'),
  543. size: 10,
  544. bold: true,
  545. };
  546. //add blank row
  547. //add card info
  548. for (const i in result.cards) {
  549. const jcard = result.cards[i];
  550. //get member info
  551. let jcmem = '';
  552. for (const j in jcard.members) {
  553. jcmem += jmeml[jcard.members[j]];
  554. jcmem += ' ';
  555. }
  556. //get assignee info
  557. let jcassig = '';
  558. for (const ja in jcard.assignees) {
  559. jcassig += jassigl[jcard.assignees[ja]];
  560. jcassig += ' ';
  561. }
  562. //get card label info
  563. let jclabel = '';
  564. for (const jl in jcard.labelIds) {
  565. jclabel += jlabel[jcard.labelIds[jl]];
  566. jclabel += ' ';
  567. }
  568. //get parent name
  569. if (jcard.parentId) {
  570. const parentCard = result.cards.find(
  571. (card) => card._id === jcard.parentId,
  572. );
  573. jcard.parentCardTitle = parentCard ? parentCard.title : '';
  574. }
  575. //add card detail
  576. const t = Number(i) + 1;
  577. ws.addRow().values = [
  578. t.toString(),
  579. jcard.title,
  580. jcard.description,
  581. jcard.parentCardTitle,
  582. jmeml[jcard.userId],
  583. addTZhours(jcard.createdAt),
  584. addTZhours(jcard.dateLastActivity),
  585. addTZhours(jcard.receivedAt),
  586. addTZhours(jcard.startAt),
  587. addTZhours(jcard.dueAt),
  588. addTZhours(jcard.endAt),
  589. jlist[jcard.listId],
  590. jswimlane[jcard.swimlaneId],
  591. jcassig,
  592. jcmem,
  593. jclabel,
  594. jcard.isOvertime ? 'true' : 'false',
  595. jcard.spentTime,
  596. ];
  597. const y = Number(i) + 8;
  598. //ws.getRow(y).height = 25;
  599. ws.getRow(y).font = {
  600. name: TAPi18n.__('excel-font'),
  601. size: 10,
  602. };
  603. allBorder(`A${y}`);
  604. allBorder(`B${y}`);
  605. allBorder(`C${y}`);
  606. allBorder(`D${y}`);
  607. allBorder(`E${y}`);
  608. allBorder(`F${y}`);
  609. allBorder(`G${y}`);
  610. allBorder(`H${y}`);
  611. allBorder(`I${y}`);
  612. allBorder(`J${y}`);
  613. allBorder(`K${y}`);
  614. allBorder(`L${y}`);
  615. allBorder(`M${y}`);
  616. allBorder(`N${y}`);
  617. allBorder(`O${y}`);
  618. allBorder(`P${y}`);
  619. allBorder(`Q${y}`);
  620. allBorder(`R${y}`);
  621. cellCenter(`A${y}`);
  622. ws.getCell(`B${y}`).alignment = {
  623. wrapText: true,
  624. };
  625. ws.getCell(`C${y}`).alignment = {
  626. wrapText: true,
  627. };
  628. ws.getCell(`M${y}`).alignment = {
  629. wrapText: true,
  630. };
  631. ws.getCell(`N${y}`).alignment = {
  632. wrapText: true,
  633. };
  634. ws.getCell(`O${y}`).alignment = {
  635. wrapText: true,
  636. };
  637. }
  638. //Activities worksheet
  639. //init worksheet
  640. const worksheet2 = workbook.addWorksheet(TAPi18n.__('activity','',this.userLanguage), {
  641. properties: {
  642. tabColor: {
  643. argb: 'FFC0000',
  644. },
  645. },
  646. pageSetup: {
  647. paperSize: 9,
  648. orientation: 'landscape',
  649. },
  650. });
  651. //get worksheet
  652. const ws2 = workbook.getWorksheet(TAPi18n.__('activity','',this.userLanguage));
  653. ws2.properties.defaultRowHeight = 20;
  654. //init columns
  655. ws2.columns = [
  656. {
  657. key: 'a',
  658. width: 14,
  659. },
  660. {
  661. key: 'b',
  662. width: 60,
  663. },
  664. {
  665. key: 'c',
  666. width: 40,
  667. },
  668. {
  669. key: 'd',
  670. width: 40,
  671. },
  672. {
  673. key: 'e',
  674. width: 30,
  675. style: {
  676. font: {
  677. name: TAPi18n.__('excel-font'),
  678. size: '10',
  679. },
  680. numFmt: 'yyyy/mm/dd hh:mm:ss',
  681. },
  682. },
  683. {
  684. key: 'f',
  685. width: 30,
  686. style: {
  687. font: {
  688. name: TAPi18n.__('excel-font'),
  689. size: '10',
  690. },
  691. numFmt: 'yyyy/mm/dd hh:mm:ss',
  692. },
  693. },
  694. ];
  695. //all border
  696. function allBorderWs2(cellno) {
  697. ws2.getCell(cellno).border = {
  698. top: {
  699. style: 'thin',
  700. },
  701. left: {
  702. style: 'thin',
  703. },
  704. bottom: {
  705. style: 'thin',
  706. },
  707. right: {
  708. style: 'thin',
  709. },
  710. };
  711. }
  712. //add title line
  713. ws2.mergeCells('A1:F1');
  714. ws2.getCell('A1').value = result.title;
  715. ws2.getCell('A1').style = {
  716. font: {
  717. name: TAPi18n.__('excel-font'),
  718. size: '20',
  719. },
  720. };
  721. ws2.getCell('A1').alignment = {
  722. vertical: 'middle',
  723. horizontal: 'center',
  724. wrapText: true,
  725. };
  726. ws2.getRow(1).height = 40;
  727. //add blank row
  728. ws2.addRow().values = ['', '', '', '', '', ''];
  729. //add comment title
  730. ws2.addRow().values = [
  731. TAPi18n.__('number','',this.userLanguage),
  732. TAPi18n.__('activity','',this.userLanguage),
  733. TAPi18n.__('card','',this.userLanguage),
  734. TAPi18n.__('owner','',this.userLanguage),
  735. TAPi18n.__('createdAt','',this.userLanguage),
  736. TAPi18n.__('last-modified-at','',this.userLanguage),
  737. ];
  738. ws2.getRow(3).height = 20;
  739. ws2.getRow(3).font = {
  740. name: TAPi18n.__('excel-font'),
  741. size: 10,
  742. bold: true,
  743. };
  744. ws2.getRow(3).alignment = {
  745. vertical: 'middle',
  746. horizontal: 'center',
  747. wrapText: true,
  748. };
  749. allBorderWs2('A3');
  750. allBorderWs2('B3');
  751. allBorderWs2('C3');
  752. allBorderWs2('D3');
  753. allBorderWs2('E3');
  754. allBorderWs2('F3');
  755. //add comment info
  756. let commentcnt = 0;
  757. for (const i in result.comments) {
  758. const jcomment = result.comments[i];
  759. //card title
  760. const parentCard = result.cards.find(
  761. (card) => card._id === jcomment.cardId,
  762. );
  763. jcomment.cardTitle = parentCard ? parentCard.title : '';
  764. if (jcomment.cardTitle == '') {
  765. continue;
  766. }
  767. //add comment detail
  768. commentcnt++;
  769. ws2.addRow().values = [
  770. commentcnt.toString(),
  771. jcomment.text,
  772. jcomment.cardTitle,
  773. jmeml[jcomment.userId],
  774. addTZhours(jcomment.createdAt),
  775. addTZhours(jcomment.modifiedAt),
  776. ];
  777. const y = commentcnt + 3;
  778. ws2.getRow(y).font = {
  779. name: TAPi18n.__('excel-font'),
  780. size: 10,
  781. };
  782. ws2.getCell(`A${y}`).alignment = {
  783. vertical: 'middle',
  784. horizontal: 'center',
  785. wrapText: true,
  786. };
  787. ws2.getCell(`B${y}`).alignment = {
  788. vertical: 'middle',
  789. wrapText: true,
  790. };
  791. ws2.getCell(`C${y}`).alignment = {
  792. vertical: 'middle',
  793. wrapText: true,
  794. };
  795. ws2.getCell(`D${y}`).alignment = {
  796. vertical: 'middle',
  797. wrapText: true,
  798. };
  799. ws2.getCell(`E${y}`).alignment = {
  800. vertical: 'middle',
  801. wrapText: true,
  802. };
  803. ws2.getCell(`F${y}`).alignment = {
  804. vertical: 'middle',
  805. wrapText: true,
  806. };
  807. allBorderWs2(`A${y}`);
  808. allBorderWs2(`B${y}`);
  809. allBorderWs2(`C${y}`);
  810. allBorderWs2(`D${y}`);
  811. allBorderWs2(`E${y}`);
  812. allBorderWs2(`F${y}`);
  813. }
  814. workbook.xlsx.write(res).then(function () {});
  815. }
  816. canExport(user) {
  817. const board = Boards.findOne(this._boardId);
  818. return board && board.isVisibleBy(user);
  819. }
  820. }
  821. export { ExporterExcel };