ExporterExcel.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  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. let worksheetTitle = result.title;
  163. if (worksheetTitle.length > 31) {
  164. // MS Excel doesn't allow worksheet name longer than 31 chars
  165. // Exceljs truncate names to 31 chars
  166. let words = worksheetTitle.split(' ');
  167. let tmpTitle = '';
  168. for (let i=0;i<words.length; i++) {
  169. if (words[0].length > 27) {
  170. // title has no spaces
  171. tmpTitle = words[0].substr(0,27) + ' ';
  172. break;
  173. }
  174. if(tmpTitle.length + words[i].length < 27) {
  175. tmpTitle += words[i] + ' ';
  176. }
  177. else {
  178. break;
  179. }
  180. }
  181. worksheetTitle = tmpTitle + '...';
  182. }
  183. const worksheet = workbook.addWorksheet(worksheetTitle, {
  184. properties: {
  185. tabColor: {
  186. argb: 'FFC0000',
  187. },
  188. },
  189. pageSetup: {
  190. paperSize: 9,
  191. orientation: 'landscape',
  192. },
  193. });
  194. //get worksheet
  195. const ws = workbook.getWorksheet(worksheetTitle);
  196. ws.properties.defaultRowHeight = 20;
  197. //init columns
  198. //Excel font. Western: Arial. zh-CN: 宋体
  199. ws.columns = [
  200. {
  201. key: 'a',
  202. width: 14,
  203. },
  204. {
  205. key: 'b',
  206. width: 40,
  207. },
  208. {
  209. key: 'c',
  210. width: 60,
  211. },
  212. {
  213. key: 'd',
  214. width: 40,
  215. },
  216. {
  217. key: 'e',
  218. width: 20,
  219. },
  220. {
  221. key: 'f',
  222. width: 20,
  223. style: {
  224. font: {
  225. name: TAPi18n.__('excel-font'),
  226. size: '10',
  227. },
  228. numFmt: 'yyyy/mm/dd hh:mm:ss',
  229. },
  230. },
  231. {
  232. key: 'g',
  233. width: 20,
  234. style: {
  235. font: {
  236. name: TAPi18n.__('excel-font'),
  237. size: '10',
  238. },
  239. numFmt: 'yyyy/mm/dd hh:mm:ss',
  240. },
  241. },
  242. {
  243. key: 'h',
  244. width: 20,
  245. style: {
  246. font: {
  247. name: TAPi18n.__('excel-font'),
  248. size: '10',
  249. },
  250. numFmt: 'yyyy/mm/dd hh:mm:ss',
  251. },
  252. },
  253. {
  254. key: 'i',
  255. width: 20,
  256. style: {
  257. font: {
  258. name: TAPi18n.__('excel-font'),
  259. size: '10',
  260. },
  261. numFmt: 'yyyy/mm/dd hh:mm:ss',
  262. },
  263. },
  264. {
  265. key: 'j',
  266. width: 20,
  267. style: {
  268. font: {
  269. name: TAPi18n.__('excel-font'),
  270. size: '10',
  271. },
  272. numFmt: 'yyyy/mm/dd hh:mm:ss',
  273. },
  274. },
  275. {
  276. key: 'k',
  277. width: 20,
  278. style: {
  279. font: {
  280. name: TAPi18n.__('excel-font'),
  281. size: '10',
  282. },
  283. numFmt: 'yyyy/mm/dd hh:mm:ss',
  284. },
  285. },
  286. {
  287. key: 'l',
  288. width: 20,
  289. },
  290. {
  291. key: 'm',
  292. width: 20,
  293. },
  294. {
  295. key: 'n',
  296. width: 20,
  297. },
  298. {
  299. key: 'o',
  300. width: 20,
  301. },
  302. {
  303. key: 'p',
  304. width: 20,
  305. },
  306. {
  307. key: 'q',
  308. width: 20,
  309. },
  310. {
  311. key: 'r',
  312. width: 20,
  313. },
  314. ];
  315. //add title line
  316. ws.mergeCells('A1:H1');
  317. ws.getCell('A1').value = result.title;
  318. ws.getCell('A1').style = {
  319. font: {
  320. name: TAPi18n.__('excel-font'),
  321. size: '20',
  322. },
  323. };
  324. ws.getCell('A1').alignment = {
  325. vertical: 'middle',
  326. horizontal: 'left',
  327. };
  328. ws.getRow(1).height = 40;
  329. //get member and assignee info
  330. let jmem = '';
  331. let jassig = '';
  332. const jmeml = {};
  333. const jassigl = {};
  334. for (const i in result.users) {
  335. jmem = `${jmem + result.users[i].username},`;
  336. jmeml[result.users[i]._id] = result.users[i].username;
  337. }
  338. jmem = jmem.substr(0, jmem.length - 1);
  339. for (const ia in result.users) {
  340. jassig = `${jassig + result.users[ia].username},`;
  341. jassigl[result.users[ia]._id] = result.users[ia].username;
  342. }
  343. jassig = jassig.substr(0, jassig.length - 1);
  344. //get kanban list info
  345. const jlist = {};
  346. for (const klist in result.lists) {
  347. jlist[result.lists[klist]._id] = result.lists[klist].title;
  348. }
  349. //get kanban swimlanes info
  350. const jswimlane = {};
  351. for (const kswimlane in result.swimlanes) {
  352. jswimlane[result.swimlanes[kswimlane]._id] =
  353. result.swimlanes[kswimlane].title;
  354. }
  355. //get kanban label info
  356. const jlabel = {};
  357. var isFirst = 1;
  358. for (const klabel in result.labels) {
  359. // console.log(klabel);
  360. if (isFirst == 0) {
  361. jlabel[result.labels[klabel]._id] = `,${result.labels[klabel].name}`;
  362. } else {
  363. isFirst = 0;
  364. jlabel[result.labels[klabel]._id] = result.labels[klabel].name;
  365. }
  366. }
  367. //add data +8 hours
  368. function addTZhours(jdate) {
  369. if (!jdate) { return ' '; }
  370. const curdate = new Date(jdate);
  371. const checkCorrectDate = moment(curdate);
  372. if (checkCorrectDate.isValid()) {
  373. return curdate;
  374. } else {
  375. return ' ';
  376. }
  377. ////Do not add 8 hours to GMT. Use GMT instead.
  378. ////Could not yet figure out how to get localtime.
  379. //return new Date(curdate.setHours(curdate.getHours() + 8));
  380. //return curdate;
  381. }
  382. //cell center
  383. function cellCenter(cellno) {
  384. ws.getCell(cellno).alignment = {
  385. vertical: 'middle',
  386. horizontal: 'center',
  387. wrapText: true,
  388. };
  389. }
  390. function cellLeft(cellno) {
  391. ws.getCell(cellno).alignment = {
  392. vertical: 'middle',
  393. horizontal: 'left',
  394. wrapText: true,
  395. };
  396. }
  397. // cell Card alignment
  398. function cellCardAlignment(cellno) {
  399. ws.getCell(cellno).alignment = {
  400. vertical: 'top',
  401. horizontal: 'left',
  402. wrapText: true,
  403. };
  404. }
  405. //all border
  406. function allBorder(cellno) {
  407. ws.getCell(cellno).border = {
  408. top: {
  409. style: 'thin',
  410. },
  411. left: {
  412. style: 'thin',
  413. },
  414. bottom: {
  415. style: 'thin',
  416. },
  417. right: {
  418. style: 'thin',
  419. },
  420. };
  421. }
  422. //add blank row
  423. ws.addRow().values = ['', '', '', '', '', ''];
  424. //add board description
  425. ws.addRow().values = [
  426. TAPi18n.__('description','',this.userLanguage),
  427. result.description,
  428. ];
  429. ws.mergeCells('B3:H3');
  430. ws.getRow(3).height = 40;
  431. // In MS Excel, we can't use the AutoFit feature on a column that contains a cell merged with cells in other columns.
  432. // Likewise, we can't use AutoFit on a row that contains a cell merged with cells in other rows.
  433. ws.getRow(3).font = {
  434. name: TAPi18n.__('excel-font'),
  435. size: 10,
  436. };
  437. ws.getCell('A3').style = {
  438. font: {
  439. name: TAPi18n.__('excel-font'),
  440. size: '10',
  441. bold: true,
  442. },
  443. };
  444. ws.getCell(`B3`).alignment = {
  445. wrapText: true,
  446. vertical: 'middle',
  447. };
  448. cellCenter('A3');
  449. //add blank row
  450. ws.addRow().values = ['', '', '', '', '', ''];
  451. //add kanban info
  452. ws.addRow().values = [
  453. TAPi18n.__('createdAt','',this.userLanguage),
  454. addTZhours(result.createdAt),
  455. TAPi18n.__('modifiedAt','',this.userLanguage),
  456. addTZhours(result.modifiedAt),
  457. TAPi18n.__('members','',this.userLanguage),
  458. jmem,
  459. ];
  460. ws.getRow(5).font = {
  461. name: TAPi18n.__('excel-font'),
  462. size: 10,
  463. bold: true,
  464. };
  465. ws.mergeCells('F5:R5');
  466. ws.getCell('B5').style = {
  467. font: {
  468. name: TAPi18n.__('excel-font'),
  469. size: '10',
  470. bold: true,
  471. },
  472. numFmt: 'yyyy/mm/dd hh:mm:ss',
  473. };
  474. ws.getCell('D5').style = {
  475. font: {
  476. name: TAPi18n.__('excel-font'),
  477. size: '10',
  478. bold: true,
  479. },
  480. numFmt: 'yyyy/mm/dd hh:mm:ss',
  481. };
  482. cellCenter('A5');
  483. cellCenter('B5');
  484. cellCenter('C5');
  485. cellCenter('D5');
  486. cellCenter('E5');
  487. cellLeft('F5');
  488. ws.getRow(5).height = 20;
  489. allBorder('A5');
  490. allBorder('B5');
  491. allBorder('C5');
  492. allBorder('D5');
  493. allBorder('E5');
  494. allBorder('F5');
  495. //add blank row
  496. ws.addRow().values = [
  497. '',
  498. '',
  499. '',
  500. '',
  501. '',
  502. '',
  503. '',
  504. '',
  505. '',
  506. '',
  507. '',
  508. '',
  509. '',
  510. '',
  511. '',
  512. ];
  513. //add card title
  514. //ws.addRow().values = ['编号', '标题', '创建人', '创建时间', '更新时间', '列表', '成员', '描述', '标签'];
  515. //this is where order in which the excel file generates
  516. ws.addRow().values = [
  517. TAPi18n.__('number','',this.userLanguage),
  518. TAPi18n.__('title','',this.userLanguage),
  519. TAPi18n.__('description','',this.userLanguage),
  520. TAPi18n.__('parent-card','',this.userLanguage),
  521. TAPi18n.__('owner','',this.userLanguage),
  522. TAPi18n.__('createdAt','',this.userLanguage),
  523. TAPi18n.__('last-modified-at','',this.userLanguage),
  524. TAPi18n.__('card-received','',this.userLanguage),
  525. TAPi18n.__('card-start','',this.userLanguage),
  526. TAPi18n.__('card-due','',this.userLanguage),
  527. TAPi18n.__('card-end','',this.userLanguage),
  528. TAPi18n.__('list','',this.userLanguage),
  529. TAPi18n.__('swimlane','',this.userLanguage),
  530. TAPi18n.__('assignee','',this.userLanguage),
  531. TAPi18n.__('members','',this.userLanguage),
  532. TAPi18n.__('labels','',this.userLanguage),
  533. TAPi18n.__('overtime-hours','',this.userLanguage),
  534. TAPi18n.__('spent-time-hours','',this.userLanguage),
  535. ];
  536. ws.getRow(7).height = 20;
  537. allBorder('A7');
  538. allBorder('B7');
  539. allBorder('C7');
  540. allBorder('D7');
  541. allBorder('E7');
  542. allBorder('F7');
  543. allBorder('G7');
  544. allBorder('H7');
  545. allBorder('I7');
  546. allBorder('J7');
  547. allBorder('K7');
  548. allBorder('L7');
  549. allBorder('M7');
  550. allBorder('N7');
  551. allBorder('O7');
  552. allBorder('P7');
  553. allBorder('Q7');
  554. allBorder('R7');
  555. cellCenter('A7');
  556. cellCenter('B7');
  557. cellCenter('C7');
  558. cellCenter('D7');
  559. cellCenter('E7');
  560. cellCenter('F7');
  561. cellCenter('G7');
  562. cellCenter('H7');
  563. cellCenter('I7');
  564. cellCenter('J7');
  565. cellCenter('K7');
  566. cellCenter('L7');
  567. cellCenter('M7');
  568. cellCenter('N7');
  569. cellCenter('O7');
  570. cellCenter('P7');
  571. cellCenter('Q7');
  572. cellCenter('R7');
  573. ws.getRow(7).font = {
  574. name: TAPi18n.__('excel-font'),
  575. size: 10,
  576. bold: true,
  577. };
  578. //add blank row
  579. //add card info
  580. for (const i in result.cards) {
  581. const jcard = result.cards[i];
  582. //get member info
  583. let jcmem = '';
  584. for (const j in jcard.members) {
  585. jcmem += jmeml[jcard.members[j]];
  586. jcmem += ' ';
  587. }
  588. //get assignee info
  589. let jcassig = '';
  590. for (const ja in jcard.assignees) {
  591. jcassig += jassigl[jcard.assignees[ja]];
  592. jcassig += ' ';
  593. }
  594. //get card label info
  595. let jclabel = '';
  596. for (const jl in jcard.labelIds) {
  597. jclabel += jlabel[jcard.labelIds[jl]];
  598. jclabel += ' ';
  599. }
  600. //get parent name
  601. if (jcard.parentId) {
  602. const parentCard = result.cards.find(
  603. (card) => card._id === jcard.parentId,
  604. );
  605. jcard.parentCardTitle = parentCard ? parentCard.title : '';
  606. }
  607. //add card detail
  608. const t = Number(i) + 1;
  609. ws.addRow().values = [
  610. t.toString(),
  611. jcard.title,
  612. jcard.description,
  613. jcard.parentCardTitle,
  614. jmeml[jcard.userId],
  615. addTZhours(jcard.createdAt),
  616. addTZhours(jcard.dateLastActivity),
  617. addTZhours(jcard.receivedAt),
  618. addTZhours(jcard.startAt),
  619. addTZhours(jcard.dueAt),
  620. addTZhours(jcard.endAt),
  621. jlist[jcard.listId],
  622. jswimlane[jcard.swimlaneId],
  623. jcassig,
  624. jcmem,
  625. jclabel,
  626. jcard.isOvertime ? 'true' : 'false',
  627. jcard.spentTime,
  628. ];
  629. const y = Number(i) + 8;
  630. //ws.getRow(y).height = 25;
  631. ws.getRow(y).font = {
  632. name: TAPi18n.__('excel-font'),
  633. size: 10,
  634. };
  635. // Border
  636. allBorder(`A${y}`);
  637. allBorder(`B${y}`);
  638. allBorder(`C${y}`);
  639. allBorder(`D${y}`);
  640. allBorder(`E${y}`);
  641. allBorder(`F${y}`);
  642. allBorder(`G${y}`);
  643. allBorder(`H${y}`);
  644. allBorder(`I${y}`);
  645. allBorder(`J${y}`);
  646. allBorder(`K${y}`);
  647. allBorder(`L${y}`);
  648. allBorder(`M${y}`);
  649. allBorder(`N${y}`);
  650. allBorder(`O${y}`);
  651. allBorder(`P${y}`);
  652. allBorder(`Q${y}`);
  653. allBorder(`R${y}`);
  654. // Alignment
  655. ws.getCell(`A${y}`).alignment = {
  656. vertical: 'top',
  657. horizontal: 'right',
  658. wrapText: true,
  659. };
  660. cellCardAlignment(`B${y}`);
  661. cellCardAlignment(`C${y}`);
  662. cellCardAlignment(`D${y}`);
  663. cellCardAlignment(`E${y}`);
  664. cellCardAlignment(`F${y}`);
  665. cellCardAlignment(`G${y}`);
  666. cellCardAlignment(`H${y}`);
  667. cellCardAlignment(`I${y}`);
  668. cellCardAlignment(`J${y}`);
  669. cellCardAlignment(`K${y}`);
  670. cellCardAlignment(`L${y}`);
  671. cellCardAlignment(`M${y}`);
  672. cellCardAlignment(`N${y}`);
  673. cellCardAlignment(`O${y}`);
  674. cellCardAlignment(`P${y}`);
  675. ws.getCell(`Q${y}`).alignment = {
  676. vertical: 'top',
  677. horizontal: 'center',
  678. wrapText: true,
  679. };
  680. ws.getCell(`R${y}`).alignment = {
  681. vertical: 'top',
  682. horizontal: 'center',
  683. wrapText: true,
  684. };
  685. }
  686. //Activities worksheet
  687. //init worksheet
  688. const worksheet2 = workbook.addWorksheet(TAPi18n.__('activity','',this.userLanguage), {
  689. properties: {
  690. tabColor: {
  691. argb: 'FFC0000',
  692. },
  693. },
  694. pageSetup: {
  695. paperSize: 9,
  696. orientation: 'landscape',
  697. },
  698. });
  699. //get worksheet
  700. const ws2 = workbook.getWorksheet(TAPi18n.__('activity','',this.userLanguage));
  701. ws2.properties.defaultRowHeight = 20;
  702. //init columns
  703. ws2.columns = [
  704. {
  705. key: 'a',
  706. width: 14,
  707. },
  708. {
  709. key: 'b',
  710. width: 60,
  711. },
  712. {
  713. key: 'c',
  714. width: 40,
  715. },
  716. {
  717. key: 'd',
  718. width: 40,
  719. },
  720. {
  721. key: 'e',
  722. width: 30,
  723. style: {
  724. font: {
  725. name: TAPi18n.__('excel-font'),
  726. size: '10',
  727. },
  728. numFmt: 'yyyy/mm/dd hh:mm:ss',
  729. },
  730. },
  731. {
  732. key: 'f',
  733. width: 30,
  734. style: {
  735. font: {
  736. name: TAPi18n.__('excel-font'),
  737. size: '10',
  738. },
  739. numFmt: 'yyyy/mm/dd hh:mm:ss',
  740. },
  741. },
  742. ];
  743. // cell Card alignment
  744. function cellCardAlignmentWs2(cellno) {
  745. ws2.getCell(cellno).alignment = {
  746. vertical: 'top',
  747. horizontal: 'left',
  748. wrapText: true,
  749. };
  750. }
  751. //all border
  752. function allBorderWs2(cellno) {
  753. ws2.getCell(cellno).border = {
  754. top: {
  755. style: 'thin',
  756. },
  757. left: {
  758. style: 'thin',
  759. },
  760. bottom: {
  761. style: 'thin',
  762. },
  763. right: {
  764. style: 'thin',
  765. },
  766. };
  767. }
  768. //add title line
  769. ws2.mergeCells('A1:F1');
  770. ws2.getCell('A1').value = result.title;
  771. ws2.getCell('A1').style = {
  772. font: {
  773. name: TAPi18n.__('excel-font'),
  774. size: '20',
  775. },
  776. };
  777. ws2.getCell('A1').alignment = {
  778. vertical: 'middle',
  779. horizontal: 'center',
  780. wrapText: true,
  781. };
  782. ws2.getRow(1).height = 40;
  783. //add blank row
  784. ws2.addRow().values = ['', '', '', '', '', ''];
  785. //add comment title
  786. ws2.addRow().values = [
  787. TAPi18n.__('number','',this.userLanguage),
  788. TAPi18n.__('activity','',this.userLanguage),
  789. TAPi18n.__('card','',this.userLanguage),
  790. TAPi18n.__('owner','',this.userLanguage),
  791. TAPi18n.__('createdAt','',this.userLanguage),
  792. TAPi18n.__('last-modified-at','',this.userLanguage),
  793. ];
  794. ws2.getRow(3).height = 20;
  795. ws2.getRow(3).font = {
  796. name: TAPi18n.__('excel-font'),
  797. size: 10,
  798. bold: true,
  799. };
  800. ws2.getRow(3).alignment = {
  801. vertical: 'middle',
  802. horizontal: 'center',
  803. wrapText: true,
  804. };
  805. allBorderWs2('A3');
  806. allBorderWs2('B3');
  807. allBorderWs2('C3');
  808. allBorderWs2('D3');
  809. allBorderWs2('E3');
  810. allBorderWs2('F3');
  811. //add comment info
  812. let commentcnt = 0;
  813. for (const i in result.comments) {
  814. const jcomment = result.comments[i];
  815. //card title
  816. const parentCard = result.cards.find(
  817. (card) => card._id === jcomment.cardId,
  818. );
  819. jcomment.cardTitle = parentCard ? parentCard.title : '';
  820. if (jcomment.cardTitle == '') {
  821. continue;
  822. }
  823. //add comment detail
  824. commentcnt++;
  825. ws2.addRow().values = [
  826. commentcnt.toString(),
  827. jcomment.text,
  828. jcomment.cardTitle,
  829. jmeml[jcomment.userId],
  830. addTZhours(jcomment.createdAt),
  831. addTZhours(jcomment.modifiedAt),
  832. ];
  833. const y = commentcnt + 3;
  834. ws2.getRow(y).font = {
  835. name: TAPi18n.__('excel-font'),
  836. size: 10,
  837. };
  838. // Border
  839. allBorderWs2(`A${y}`);
  840. allBorderWs2(`B${y}`);
  841. allBorderWs2(`C${y}`);
  842. allBorderWs2(`D${y}`);
  843. allBorderWs2(`E${y}`);
  844. allBorderWs2(`F${y}`);
  845. // Alignment
  846. ws2.getCell(`A${y}`).alignment = {
  847. vertical: 'top',
  848. horizontal: 'right',
  849. wrapText: true,
  850. };
  851. cellCardAlignmentWs2(`B${y}`);
  852. cellCardAlignmentWs2(`C${y}`);
  853. cellCardAlignmentWs2(`D${y}`);
  854. cellCardAlignmentWs2(`E${y}`);
  855. cellCardAlignmentWs2(`F${y}`);
  856. }
  857. workbook.xlsx.write(res).then(function () {});
  858. }
  859. canExport(user) {
  860. const board = Boards.findOne(this._boardId);
  861. return board && board.isVisibleBy(user);
  862. }
  863. }
  864. export { ExporterExcel };