cards.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. import moment from 'moment';
  2. import Users from '../../models/users';
  3. import Boards from '../../models/boards';
  4. import Lists from '../../models/lists';
  5. import Swimlanes from '../../models/swimlanes';
  6. import Cards from '../../models/cards';
  7. import CardComments from '../../models/cardComments';
  8. import Attachments from '../../models/attachments';
  9. import Checklists from '../../models/checklists';
  10. import ChecklistItems from '../../models/checklistItems';
  11. import SessionData from '../../models/usersessiondata';
  12. import CustomFields from '../../models/customFields';
  13. import {
  14. DEFAULT_LIMIT,
  15. OPERATOR_ASSIGNEE,
  16. OPERATOR_BOARD,
  17. OPERATOR_COMMENT,
  18. OPERATOR_CREATED_AT,
  19. OPERATOR_CREATOR,
  20. OPERATOR_DEBUG,
  21. OPERATOR_DUE,
  22. OPERATOR_HAS,
  23. OPERATOR_LABEL,
  24. OPERATOR_LIMIT,
  25. OPERATOR_LIST,
  26. OPERATOR_MEMBER,
  27. OPERATOR_MODIFIED_AT, OPERATOR_ORG,
  28. OPERATOR_SORT,
  29. OPERATOR_STATUS,
  30. OPERATOR_SWIMLANE, OPERATOR_TEAM,
  31. OPERATOR_USER,
  32. ORDER_ASCENDING,
  33. PREDICATE_ALL,
  34. PREDICATE_ARCHIVED,
  35. PREDICATE_ASSIGNEES,
  36. PREDICATE_ATTACHMENT,
  37. PREDICATE_CHECKLIST,
  38. PREDICATE_CREATED_AT,
  39. PREDICATE_DESCRIPTION,
  40. PREDICATE_DUE_AT,
  41. PREDICATE_END_AT,
  42. PREDICATE_ENDED,
  43. PREDICATE_MEMBERS,
  44. PREDICATE_MODIFIED_AT,
  45. PREDICATE_PRIVATE,
  46. PREDICATE_PUBLIC,
  47. PREDICATE_START_AT,
  48. PREDICATE_SYSTEM,
  49. } from '/config/search-const';
  50. import { QueryErrors, QueryParams, Query } from '/config/query-classes';
  51. import { CARD_TYPES } from '../../config/const';
  52. import Org from "../../models/org";
  53. import Team from "../../models/team";
  54. const escapeForRegex = require('escape-string-regexp');
  55. Meteor.publish('card', cardId => {
  56. check(cardId, String);
  57. const ret = Cards.find({ _id: cardId });
  58. return ret;
  59. });
  60. /** publish all data which is necessary to display card details as popup
  61. * @returns array of cursors
  62. */
  63. Meteor.publishRelations('popupCardData', function(cardId) {
  64. check(cardId, String);
  65. this.cursor(
  66. Cards.find({_id: cardId}),
  67. function(cardId, card) {
  68. this.cursor(Boards.find({_id: card.boardId}));
  69. },
  70. );
  71. return this.ready()
  72. });
  73. Meteor.publish('myCards', function(sessionId) {
  74. check(sessionId, String);
  75. const queryParams = new QueryParams();
  76. queryParams.addPredicate(OPERATOR_USER, Meteor.user().username);
  77. queryParams.setPredicate(OPERATOR_LIMIT, 200);
  78. const query = buildQuery(queryParams);
  79. query.projection.sort = {
  80. boardId: 1,
  81. swimlaneId: 1,
  82. listId: 1,
  83. };
  84. return findCards(sessionId, query);
  85. });
  86. // Meteor.publish('dueCards', function(sessionId, allUsers = false) {
  87. // check(sessionId, String);
  88. // check(allUsers, Boolean);
  89. //
  90. // // eslint-disable-next-line no-console
  91. // // console.log('all users:', allUsers);
  92. //
  93. // const queryParams = {
  94. // has: [{ field: 'dueAt', exists: true }],
  95. // limit: 25,
  96. // skip: 0,
  97. // sort: { name: 'dueAt', order: 'des' },
  98. // };
  99. //
  100. // if (!allUsers) {
  101. // queryParams.users = [Meteor.user().username];
  102. // }
  103. //
  104. // return buildQuery(sessionId, queryParams);
  105. // });
  106. Meteor.publish('globalSearch', function(sessionId, params, text) {
  107. check(sessionId, String);
  108. check(params, Object);
  109. check(text, String);
  110. // eslint-disable-next-line no-console
  111. // console.log('queryParams:', params);
  112. return findCards(sessionId, buildQuery(new QueryParams(params, text)));
  113. });
  114. function buildSelector(queryParams) {
  115. const userId = Meteor.userId();
  116. const errors = new QueryErrors();
  117. let selector = {};
  118. // eslint-disable-next-line no-console
  119. // console.log('queryParams:', queryParams);
  120. if (queryParams.selector) {
  121. selector = queryParams.selector;
  122. } else {
  123. const boardsSelector = {};
  124. let archived = false;
  125. let endAt = null;
  126. if (queryParams.hasOperator(OPERATOR_STATUS)) {
  127. queryParams.getPredicates(OPERATOR_STATUS).forEach(status => {
  128. if (status === PREDICATE_ARCHIVED) {
  129. archived = true;
  130. } else if (status === PREDICATE_ALL) {
  131. archived = null;
  132. } else if (status === PREDICATE_ENDED) {
  133. endAt = { $nin: [null, ''] };
  134. } else if ([PREDICATE_PRIVATE, PREDICATE_PUBLIC].includes(status)) {
  135. boardsSelector.permission = status;
  136. }
  137. });
  138. }
  139. if (queryParams.hasOperator(OPERATOR_ORG)) {
  140. const orgs = [];
  141. queryParams.getPredicates(OPERATOR_ORG).forEach(name => {
  142. const org = Org.findOne({
  143. $or: [
  144. { orgDisplayName: name },
  145. { orgShortName: name }
  146. ]
  147. });
  148. if (org) {
  149. orgs.push(org._id);
  150. } else {
  151. errors.addNotFound(OPERATOR_ORG, name);
  152. }
  153. });
  154. if (orgs.length) {
  155. boardsSelector.orgs = {
  156. $elemMatch: { orgId: { $in: orgs }, isActive: true }
  157. };
  158. }
  159. }
  160. if (queryParams.hasOperator(OPERATOR_TEAM)) {
  161. const teams = [];
  162. queryParams.getPredicates(OPERATOR_TEAM).forEach(name => {
  163. const team = Team.findOne({
  164. $or: [
  165. { teamDisplayName: name },
  166. { teamShortName: name }
  167. ]
  168. });
  169. if (team) {
  170. teams.push(team._id);
  171. } else {
  172. errors.addNotFound(OPERATOR_TEAM, name);
  173. }
  174. });
  175. if (teams.length) {
  176. boardsSelector.teams = {
  177. $elemMatch: { teamId: { $in: teams }, isActive: true }
  178. };
  179. }
  180. }
  181. selector = {
  182. type: 'cardType-card',
  183. // boardId: { $in: Boards.userBoardIds(userId) },
  184. $and: [],
  185. };
  186. if (archived !== null) {
  187. if (archived) {
  188. selector.boardId = {
  189. $in: Boards.userBoardIds(userId, null, boardsSelector),
  190. };
  191. selector.$and.push({
  192. $or: [
  193. {
  194. boardId: {
  195. $in: Boards.userBoardIds(userId, archived, boardsSelector),
  196. },
  197. },
  198. { swimlaneId: { $in: Swimlanes.userArchivedSwimlaneIds(userId) } },
  199. { listId: { $in: Lists.userArchivedListIds(userId) } },
  200. { archived: true },
  201. ],
  202. });
  203. } else {
  204. selector.boardId = {
  205. $in: Boards.userBoardIds(userId, false, boardsSelector),
  206. };
  207. selector.swimlaneId = { $nin: Swimlanes.archivedSwimlaneIds() };
  208. selector.listId = { $nin: Lists.archivedListIds() };
  209. selector.archived = false;
  210. }
  211. } else {
  212. selector.boardId = {
  213. $in: Boards.userBoardIds(userId, null, boardsSelector),
  214. };
  215. }
  216. if (endAt !== null) {
  217. selector.endAt = endAt;
  218. }
  219. if (queryParams.hasOperator(OPERATOR_BOARD)) {
  220. const queryBoards = [];
  221. queryParams.getPredicates(OPERATOR_BOARD).forEach(query => {
  222. const boards = Boards.userSearch(userId, {
  223. title: new RegExp(escapeForRegex(query), 'i'),
  224. });
  225. if (boards.count()) {
  226. boards.forEach(board => {
  227. queryBoards.push(board._id);
  228. });
  229. } else {
  230. errors.addNotFound(OPERATOR_BOARD, query);
  231. }
  232. });
  233. selector.boardId.$in = queryBoards;
  234. }
  235. if (queryParams.hasOperator(OPERATOR_SWIMLANE)) {
  236. const querySwimlanes = [];
  237. queryParams.getPredicates(OPERATOR_SWIMLANE).forEach(query => {
  238. const swimlanes = Swimlanes.find({
  239. title: new RegExp(escapeForRegex(query), 'i'),
  240. });
  241. if (swimlanes.count()) {
  242. swimlanes.forEach(swim => {
  243. querySwimlanes.push(swim._id);
  244. });
  245. } else {
  246. errors.addNotFound(OPERATOR_SWIMLANE, query);
  247. }
  248. });
  249. // eslint-disable-next-line no-prototype-builtins
  250. if (!selector.swimlaneId.hasOwnProperty('swimlaneId')) {
  251. selector.swimlaneId = { $in: [] };
  252. }
  253. selector.swimlaneId.$in = querySwimlanes;
  254. }
  255. if (queryParams.hasOperator(OPERATOR_LIST)) {
  256. const queryLists = [];
  257. queryParams.getPredicates(OPERATOR_LIST).forEach(query => {
  258. const lists = Lists.find({
  259. title: new RegExp(escapeForRegex(query), 'i'),
  260. });
  261. if (lists.count()) {
  262. lists.forEach(list => {
  263. queryLists.push(list._id);
  264. });
  265. } else {
  266. errors.addNotFound(OPERATOR_LIST, query);
  267. }
  268. });
  269. // eslint-disable-next-line no-prototype-builtins
  270. if (!selector.hasOwnProperty('listId')) {
  271. selector.listId = { $in: [] };
  272. }
  273. selector.listId.$in = queryLists;
  274. }
  275. if (queryParams.hasOperator(OPERATOR_COMMENT)) {
  276. const cardIds = CardComments.textSearch(
  277. userId,
  278. queryParams.getPredicates(OPERATOR_COMMENT),
  279. com => {
  280. return com.cardId;
  281. },
  282. );
  283. if (cardIds.length) {
  284. selector._id = { $in: cardIds };
  285. } else {
  286. queryParams.getPredicates(OPERATOR_COMMENT).forEach(comment => {
  287. errors.addNotFound(OPERATOR_COMMENT, comment);
  288. });
  289. }
  290. }
  291. [OPERATOR_DUE, OPERATOR_CREATED_AT, OPERATOR_MODIFIED_AT].forEach(field => {
  292. if (queryParams.hasOperator(field)) {
  293. selector[field] = {};
  294. const predicate = queryParams.getPredicate(field);
  295. selector[field][predicate.operator] = new Date(predicate.value);
  296. }
  297. });
  298. const queryUsers = {};
  299. queryUsers[OPERATOR_ASSIGNEE] = [];
  300. queryUsers[OPERATOR_MEMBER] = [];
  301. queryUsers[OPERATOR_CREATOR] = [];
  302. if (queryParams.hasOperator(OPERATOR_USER)) {
  303. const users = [];
  304. queryParams.getPredicates(OPERATOR_USER).forEach(username => {
  305. const user = Users.findOne({ username });
  306. if (user) {
  307. users.push(user._id);
  308. } else {
  309. errors.addNotFound(OPERATOR_USER, username);
  310. }
  311. });
  312. if (users.length) {
  313. selector.$and.push({
  314. $or: [{ members: { $in: users } }, { assignees: { $in: users } }],
  315. });
  316. }
  317. }
  318. [OPERATOR_MEMBER, OPERATOR_ASSIGNEE, OPERATOR_CREATOR].forEach(key => {
  319. if (queryParams.hasOperator(key)) {
  320. const users = [];
  321. queryParams.getPredicates(key).forEach(username => {
  322. const user = Users.findOne({ username });
  323. if (user) {
  324. users.push(user._id);
  325. } else {
  326. errors.addNotFound(key, username);
  327. }
  328. });
  329. if (users.length) {
  330. selector[key] = { $in: users };
  331. }
  332. }
  333. });
  334. if (queryParams.hasOperator(OPERATOR_LABEL)) {
  335. const queryLabels = [];
  336. queryParams.getPredicates(OPERATOR_LABEL).forEach(label => {
  337. let boards = Boards.userBoards(userId, null, {
  338. labels: { $elemMatch: { color: label.toLowerCase() } },
  339. });
  340. if (boards.count()) {
  341. boards.forEach(board => {
  342. // eslint-disable-next-line no-console
  343. // console.log('board:', board);
  344. // eslint-disable-next-line no-console
  345. // console.log('board.labels:', board.labels);
  346. board.labels
  347. .filter(boardLabel => {
  348. return boardLabel.color === label.toLowerCase();
  349. })
  350. .forEach(boardLabel => {
  351. queryLabels.push(boardLabel._id);
  352. });
  353. });
  354. } else {
  355. // eslint-disable-next-line no-console
  356. // console.log('label:', label);
  357. const reLabel = new RegExp(escapeForRegex(label), 'i');
  358. // eslint-disable-next-line no-console
  359. // console.log('reLabel:', reLabel);
  360. boards = Boards.userBoards(userId, null, {
  361. labels: { $elemMatch: { name: reLabel } },
  362. });
  363. if (boards.count()) {
  364. boards.forEach(board => {
  365. board.labels
  366. .filter(boardLabel => {
  367. if (!boardLabel.name) {
  368. return false;
  369. }
  370. return boardLabel.name.match(reLabel);
  371. })
  372. .forEach(boardLabel => {
  373. queryLabels.push(boardLabel._id);
  374. });
  375. });
  376. } else {
  377. errors.addNotFound(OPERATOR_LABEL, label);
  378. }
  379. }
  380. });
  381. if (queryLabels.length) {
  382. // eslint-disable-next-line no-console
  383. // console.log('queryLabels:', queryLabels);
  384. selector.labelIds = { $in: _.uniq(queryLabels) };
  385. }
  386. }
  387. if (queryParams.hasOperator(OPERATOR_HAS)) {
  388. queryParams.getPredicates(OPERATOR_HAS).forEach(has => {
  389. switch (has.field) {
  390. case PREDICATE_ATTACHMENT:
  391. selector.$and.push({
  392. _id: {
  393. $in: Attachments.find({}, { fields: { cardId: 1 } }).map(
  394. a => a.cardId,
  395. ),
  396. },
  397. });
  398. break;
  399. case PREDICATE_CHECKLIST:
  400. selector.$and.push({
  401. _id: {
  402. $in: Checklists.find({}, { fields: { cardId: 1 } }).map(
  403. a => a.cardId,
  404. ),
  405. },
  406. });
  407. break;
  408. case PREDICATE_DESCRIPTION:
  409. case PREDICATE_START_AT:
  410. case PREDICATE_DUE_AT:
  411. case PREDICATE_END_AT:
  412. if (has.exists) {
  413. selector[has.field] = { $exists: true, $nin: [null, ''] };
  414. } else {
  415. selector[has.field] = { $in: [null, ''] };
  416. }
  417. break;
  418. case PREDICATE_ASSIGNEES:
  419. case PREDICATE_MEMBERS:
  420. if (has.exists) {
  421. selector[has.field] = { $exists: true, $nin: [null, []] };
  422. } else {
  423. selector[has.field] = { $in: [null, []] };
  424. }
  425. break;
  426. }
  427. });
  428. }
  429. if (queryParams.text) {
  430. const regex = new RegExp(escapeForRegex(queryParams.text), 'i');
  431. const items = ChecklistItems.find(
  432. { title: regex },
  433. { fields: { cardId: 1, checklistId: 1 } },
  434. );
  435. const checklists = Checklists.find(
  436. {
  437. $or: [
  438. { title: regex },
  439. { _id: { $in: items.map(item => item.checklistId) } },
  440. ],
  441. },
  442. { fields: { cardId: 1 } },
  443. );
  444. const attachments = Attachments.find({ 'original.name': regex });
  445. const comments = CardComments.find(
  446. { text: regex },
  447. { fields: { cardId: 1 } },
  448. );
  449. let cardsSelector = [
  450. { title: regex },
  451. { description: regex },
  452. { customFields: { $elemMatch: { value: regex } } },
  453. { _id: { $in: checklists.map(list => list.cardId) } },
  454. { _id: { $in: attachments.map(attach => attach.cardId) } },
  455. { _id: { $in: comments.map(com => com.cardId) } },
  456. ];
  457. if (queryParams.text === "false" || queryParams.text === "true") {
  458. cardsSelector.push({ customFields: { $elemMatch: { value: queryParams.text === "true" } } } );
  459. }
  460. selector.$and.push({ $or: cardsSelector });
  461. }
  462. if (selector.$and.length === 0) {
  463. delete selector.$and;
  464. }
  465. }
  466. // eslint-disable-next-line no-console
  467. // console.log('cards selector:', JSON.stringify(selector, null, 2));
  468. const query = new Query();
  469. query.selector = selector;
  470. query.setQueryParams(queryParams);
  471. query._errors = errors;
  472. return query;
  473. }
  474. function buildProjection(query) {
  475. // eslint-disable-next-line no-console
  476. // console.log('query:', query);
  477. let skip = 0;
  478. if (query.getQueryParams().skip) {
  479. skip = query.getQueryParams().skip;
  480. }
  481. let limit = DEFAULT_LIMIT;
  482. const configLimit = parseInt(process.env.RESULTS_PER_PAGE, 10);
  483. if (!isNaN(configLimit) && configLimit > 0) {
  484. limit = configLimit;
  485. }
  486. if (query.getQueryParams().hasOperator(OPERATOR_LIMIT)) {
  487. limit = query.getQueryParams().getPredicate(OPERATOR_LIMIT);
  488. }
  489. const projection = {
  490. fields: {
  491. _id: 1,
  492. archived: 1,
  493. boardId: 1,
  494. swimlaneId: 1,
  495. listId: 1,
  496. title: 1,
  497. type: 1,
  498. sort: 1,
  499. members: 1,
  500. assignees: 1,
  501. colors: 1,
  502. dueAt: 1,
  503. createdAt: 1,
  504. modifiedAt: 1,
  505. labelIds: 1,
  506. customFields: 1,
  507. userId: 1,
  508. description: 1,
  509. },
  510. sort: {
  511. boardId: 1,
  512. swimlaneId: 1,
  513. listId: 1,
  514. sort: 1,
  515. },
  516. skip,
  517. limit,
  518. };
  519. if (query.getQueryParams().hasOperator(OPERATOR_SORT)) {
  520. const order =
  521. query.getQueryParams().getPredicate(OPERATOR_SORT).order ===
  522. ORDER_ASCENDING
  523. ? 1
  524. : -1;
  525. switch (query.getQueryParams().getPredicate(OPERATOR_SORT).name) {
  526. case PREDICATE_DUE_AT:
  527. projection.sort = {
  528. dueAt: order,
  529. boardId: 1,
  530. swimlaneId: 1,
  531. listId: 1,
  532. sort: 1,
  533. };
  534. break;
  535. case PREDICATE_MODIFIED_AT:
  536. projection.sort = {
  537. modifiedAt: order,
  538. boardId: 1,
  539. swimlaneId: 1,
  540. listId: 1,
  541. sort: 1,
  542. };
  543. break;
  544. case PREDICATE_CREATED_AT:
  545. projection.sort = {
  546. createdAt: order,
  547. boardId: 1,
  548. swimlaneId: 1,
  549. listId: 1,
  550. sort: 1,
  551. };
  552. break;
  553. case PREDICATE_SYSTEM:
  554. projection.sort = {
  555. boardId: order,
  556. swimlaneId: order,
  557. listId: order,
  558. modifiedAt: order,
  559. sort: order,
  560. };
  561. break;
  562. }
  563. }
  564. // eslint-disable-next-line no-console
  565. // console.log('projection:', projection);
  566. query.projection = projection;
  567. return query;
  568. }
  569. function buildQuery(queryParams) {
  570. const query = buildSelector(queryParams);
  571. return buildProjection(query);
  572. }
  573. Meteor.publish('brokenCards', function(sessionId) {
  574. check(sessionId, String);
  575. const params = new QueryParams();
  576. params.addPredicate(OPERATOR_STATUS, PREDICATE_ALL);
  577. const query = buildQuery(params);
  578. query.selector.$or = [
  579. { boardId: { $in: [null, ''] } },
  580. { swimlaneId: { $in: [null, ''] } },
  581. { listId: { $in: [null, ''] } },
  582. { type: { $nin: CARD_TYPES } },
  583. ];
  584. // console.log('brokenCards selector:', query.selector);
  585. return findCards(sessionId, query);
  586. });
  587. Meteor.publish('nextPage', function(sessionId) {
  588. check(sessionId, String);
  589. const session = SessionData.findOne({ sessionId });
  590. const projection = session.getProjection();
  591. projection.skip = session.lastHit;
  592. return findCards(sessionId, new Query(session.getSelector(), projection));
  593. });
  594. Meteor.publish('previousPage', function(sessionId) {
  595. check(sessionId, String);
  596. const session = SessionData.findOne({ sessionId });
  597. const projection = session.getProjection();
  598. projection.skip = session.lastHit - session.resultsCount - projection.limit;
  599. return findCards(sessionId, new Query(session.getSelector(), projection));
  600. });
  601. function findCards(sessionId, query) {
  602. const userId = Meteor.userId();
  603. // eslint-disable-next-line no-console
  604. // console.log('selector:', query.selector);
  605. // console.log('selector.$and:', query.selector.$and);
  606. // eslint-disable-next-line no-console
  607. // console.log('projection:', query.projection);
  608. const cards = Cards.find(query.selector, query.projection);
  609. // eslint-disable-next-line no-console
  610. // console.log('count:', cards.count());
  611. const update = {
  612. $set: {
  613. totalHits: 0,
  614. lastHit: 0,
  615. resultsCount: 0,
  616. cards: [],
  617. selector: SessionData.pickle(query.selector),
  618. projection: SessionData.pickle(query.projection),
  619. errors: query.errors(),
  620. debug: query.getQueryParams().getPredicate(OPERATOR_DEBUG)
  621. },
  622. };
  623. if (cards) {
  624. update.$set.totalHits = cards.count();
  625. update.$set.lastHit =
  626. query.projection.skip + query.projection.limit < cards.count()
  627. ? query.projection.skip + query.projection.limit
  628. : cards.count();
  629. update.$set.cards = cards.map(card => {
  630. return card._id;
  631. });
  632. update.$set.resultsCount = update.$set.cards.length;
  633. }
  634. // eslint-disable-next-line no-console
  635. // console.log('sessionId:', sessionId);
  636. // eslint-disable-next-line no-console
  637. // console.log('userId:', userId);
  638. // eslint-disable-next-line no-console
  639. // console.log('update:', update);
  640. SessionData.upsert({ userId, sessionId }, update);
  641. // remove old session data
  642. SessionData.remove({
  643. userId,
  644. modifiedAt: {
  645. $lt: new Date(
  646. moment()
  647. .subtract(1, 'day')
  648. .format(),
  649. ),
  650. },
  651. });
  652. if (cards) {
  653. const boards = [];
  654. const swimlanes = [];
  655. const lists = [];
  656. const customFieldIds = [];
  657. const users = [this.userId];
  658. cards.forEach(card => {
  659. if (card.boardId) boards.push(card.boardId);
  660. if (card.swimlaneId) swimlanes.push(card.swimlaneId);
  661. if (card.listId) lists.push(card.listId);
  662. if (card.userId) {
  663. users.push(card.userId);
  664. }
  665. if (card.members) {
  666. card.members.forEach(userId => {
  667. users.push(userId);
  668. });
  669. }
  670. if (card.assignees) {
  671. card.assignees.forEach(userId => {
  672. users.push(userId);
  673. });
  674. }
  675. if (card.customFields) {
  676. card.customFields.forEach(field => {
  677. customFieldIds.push(field._id);
  678. });
  679. }
  680. });
  681. const fields = {
  682. _id: 1,
  683. title: 1,
  684. archived: 1,
  685. sort: 1,
  686. type: 1,
  687. };
  688. return [
  689. cards,
  690. Boards.find(
  691. { _id: { $in: boards } },
  692. { fields: { ...fields, labels: 1, color: 1 } },
  693. ),
  694. Swimlanes.find(
  695. { _id: { $in: swimlanes } },
  696. { fields: { ...fields, color: 1 } },
  697. ),
  698. Lists.find({ _id: { $in: lists } }, { fields }),
  699. CustomFields.find({ _id: { $in: customFieldIds } }),
  700. Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
  701. Checklists.find({ cardId: { $in: cards.map(c => c._id) } }),
  702. ChecklistItems.find({ cardId: { $in: cards.map(c => c._id) } }),
  703. Attachments.find({ cardId: { $in: cards.map(c => c._id) } }),
  704. CardComments.find({ cardId: { $in: cards.map(c => c._id) } }),
  705. SessionData.find({ userId, sessionId }),
  706. ];
  707. }
  708. return [SessionData.find({ userId, sessionId })];
  709. }