cards.js 22 KB

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