| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784 | import moment from 'moment';import Users from '../../models/users';import Boards from '../../models/boards';import Lists from '../../models/lists';import Swimlanes from '../../models/swimlanes';import Cards from '../../models/cards';import CardComments from '../../models/cardComments';import Attachments from '../../models/attachments';import Checklists from '../../models/checklists';import ChecklistItems from '../../models/checklistItems';import SessionData from '../../models/usersessiondata';import CustomFields from '../../models/customFields';import {  DEFAULT_LIMIT,  OPERATOR_ASSIGNEE,  OPERATOR_BOARD,  OPERATOR_COMMENT,  OPERATOR_CREATED_AT,  OPERATOR_CREATOR,  OPERATOR_DEBUG,  OPERATOR_DUE,  OPERATOR_HAS,  OPERATOR_LABEL,  OPERATOR_LIMIT,  OPERATOR_LIST,  OPERATOR_MEMBER,  OPERATOR_MODIFIED_AT, OPERATOR_ORG,  OPERATOR_SORT,  OPERATOR_STATUS,  OPERATOR_SWIMLANE, OPERATOR_TEAM,  OPERATOR_USER,  ORDER_ASCENDING,  PREDICATE_ALL,  PREDICATE_ARCHIVED,  PREDICATE_ASSIGNEES,  PREDICATE_ATTACHMENT,  PREDICATE_CHECKLIST,  PREDICATE_CREATED_AT,  PREDICATE_DESCRIPTION,  PREDICATE_DUE_AT,  PREDICATE_END_AT,  PREDICATE_ENDED,  PREDICATE_MEMBERS,  PREDICATE_MODIFIED_AT,  PREDICATE_PRIVATE,  PREDICATE_PUBLIC,  PREDICATE_START_AT,  PREDICATE_SYSTEM,} from '/config/search-const';import { QueryErrors, QueryParams, Query } from '/config/query-classes';import { CARD_TYPES } from '../../config/const';import Org from "../../models/org";import Team from "../../models/team";const escapeForRegex = require('escape-string-regexp');Meteor.publish('card', cardId => {  check(cardId, String);  const ret = Cards.find({ _id: cardId });  return ret;});/** publish all data which is necessary to display card details as popup * @returns array of cursors */Meteor.publishRelations('popupCardData', function(cardId) {  check(cardId, String);  this.cursor(    Cards.find({_id: cardId}),    function(cardId, card) {      this.cursor(Boards.find({_id: card.boardId}));    },  );  return this.ready()});Meteor.publish('myCards', function(sessionId) {  check(sessionId, String);  const queryParams = new QueryParams();  queryParams.addPredicate(OPERATOR_USER, Meteor.user().username);  queryParams.setPredicate(OPERATOR_LIMIT, 200);  const query = buildQuery(queryParams);  query.projection.sort = {    boardId: 1,    swimlaneId: 1,    listId: 1,  };  return findCards(sessionId, query);});// Meteor.publish('dueCards', function(sessionId, allUsers = false) {//   check(sessionId, String);//   check(allUsers, Boolean);////   // eslint-disable-next-line no-console//   // console.log('all users:', allUsers);////   const queryParams = {//     has: [{ field: 'dueAt', exists: true }],//     limit: 25,//     skip: 0,//     sort: { name: 'dueAt', order: 'des' },//   };////   if (!allUsers) {//     queryParams.users = [Meteor.user().username];//   }////   return buildQuery(sessionId, queryParams);// });Meteor.publish('globalSearch', function(sessionId, params, text) {  check(sessionId, String);  check(params, Object);  check(text, String);  // eslint-disable-next-line no-console  // console.log('queryParams:', params);  return findCards(sessionId, buildQuery(new QueryParams(params, text)));});function buildSelector(queryParams) {  const userId = Meteor.userId();  const errors = new QueryErrors();  let selector = {};  // eslint-disable-next-line no-console  // console.log('queryParams:', queryParams);  if (queryParams.selector) {    selector = queryParams.selector;  } else {    const boardsSelector = {};    let archived = false;    let endAt = null;    if (queryParams.hasOperator(OPERATOR_STATUS)) {      queryParams.getPredicates(OPERATOR_STATUS).forEach(status => {        if (status === PREDICATE_ARCHIVED) {          archived = true;        } else if (status === PREDICATE_ALL) {          archived = null;        } else if (status === PREDICATE_ENDED) {          endAt = { $nin: [null, ''] };        } else if ([PREDICATE_PRIVATE, PREDICATE_PUBLIC].includes(status)) {          boardsSelector.permission = status;        }      });    }    if (queryParams.hasOperator(OPERATOR_ORG)) {      const orgs = [];      queryParams.getPredicates(OPERATOR_ORG).forEach(name => {        const org = Org.findOne({          $or: [            { orgDisplayName: name },            { orgShortName: name }          ]        });        if (org) {          orgs.push(org._id);        } else {          errors.addNotFound(OPERATOR_ORG, name);        }      });      if (orgs.length) {        boardsSelector.orgs = {          $elemMatch: { orgId: { $in: orgs }, isActive: true }        };      }    }    if (queryParams.hasOperator(OPERATOR_TEAM)) {      const teams = [];      queryParams.getPredicates(OPERATOR_TEAM).forEach(name => {        const team = Team.findOne({          $or: [            { teamDisplayName: name },            { teamShortName: name }          ]        });        if (team) {          teams.push(team._id);        } else {          errors.addNotFound(OPERATOR_TEAM, name);        }      });      if (teams.length) {        boardsSelector.teams = {          $elemMatch: { teamId: { $in: teams }, isActive: true }        };      }    }    selector = {      type: 'cardType-card',      // boardId: { $in: Boards.userBoardIds(userId) },      $and: [],    };    if (archived !== null) {      if (archived) {        selector.boardId = {          $in: Boards.userBoardIds(userId, null, boardsSelector),        };        selector.$and.push({          $or: [            {              boardId: {                $in: Boards.userBoardIds(userId, archived, boardsSelector),              },            },            { swimlaneId: { $in: Swimlanes.userArchivedSwimlaneIds(userId) } },            { listId: { $in: Lists.userArchivedListIds(userId) } },            { archived: true },          ],        });      } else {        selector.boardId = {          $in: Boards.userBoardIds(userId, false, boardsSelector),        };        selector.swimlaneId = { $nin: Swimlanes.archivedSwimlaneIds() };        selector.listId = { $nin: Lists.archivedListIds() };        selector.archived = false;      }    } else {      selector.boardId = {        $in: Boards.userBoardIds(userId, null, boardsSelector),      };    }    if (endAt !== null) {      selector.endAt = endAt;    }    if (queryParams.hasOperator(OPERATOR_BOARD)) {      const queryBoards = [];      queryParams.getPredicates(OPERATOR_BOARD).forEach(query => {        const boards = Boards.userSearch(userId, {          title: new RegExp(escapeForRegex(query), 'i'),        });        if (boards.count()) {          boards.forEach(board => {            queryBoards.push(board._id);          });        } else {          errors.addNotFound(OPERATOR_BOARD, query);        }      });      selector.boardId.$in = queryBoards;    }    if (queryParams.hasOperator(OPERATOR_SWIMLANE)) {      const querySwimlanes = [];      queryParams.getPredicates(OPERATOR_SWIMLANE).forEach(query => {        const swimlanes = Swimlanes.find({          title: new RegExp(escapeForRegex(query), 'i'),        });        if (swimlanes.count()) {          swimlanes.forEach(swim => {            querySwimlanes.push(swim._id);          });        } else {          errors.addNotFound(OPERATOR_SWIMLANE, query);        }      });      // eslint-disable-next-line no-prototype-builtins      if (!selector.swimlaneId.hasOwnProperty('swimlaneId')) {        selector.swimlaneId = { $in: [] };      }      selector.swimlaneId.$in = querySwimlanes;    }    if (queryParams.hasOperator(OPERATOR_LIST)) {      const queryLists = [];      queryParams.getPredicates(OPERATOR_LIST).forEach(query => {        const lists = Lists.find({          title: new RegExp(escapeForRegex(query), 'i'),        });        if (lists.count()) {          lists.forEach(list => {            queryLists.push(list._id);          });        } else {          errors.addNotFound(OPERATOR_LIST, query);        }      });      // eslint-disable-next-line no-prototype-builtins      if (!selector.hasOwnProperty('listId')) {        selector.listId = { $in: [] };      }      selector.listId.$in = queryLists;    }    if (queryParams.hasOperator(OPERATOR_COMMENT)) {      const cardIds = CardComments.textSearch(        userId,        queryParams.getPredicates(OPERATOR_COMMENT),        com => {          return com.cardId;        },      );      if (cardIds.length) {        selector._id = { $in: cardIds };      } else {        queryParams.getPredicates(OPERATOR_COMMENT).forEach(comment => {          errors.addNotFound(OPERATOR_COMMENT, comment);        });      }    }    [OPERATOR_DUE, OPERATOR_CREATED_AT, OPERATOR_MODIFIED_AT].forEach(field => {      if (queryParams.hasOperator(field)) {        selector[field] = {};        const predicate = queryParams.getPredicate(field);        selector[field][predicate.operator] = new Date(predicate.value);      }    });    const queryUsers = {};    queryUsers[OPERATOR_ASSIGNEE] = [];    queryUsers[OPERATOR_MEMBER] = [];    queryUsers[OPERATOR_CREATOR] = [];    if (queryParams.hasOperator(OPERATOR_USER)) {      const users = [];      queryParams.getPredicates(OPERATOR_USER).forEach(username => {        const user = Users.findOne({ username });        if (user) {          users.push(user._id);        } else {          errors.addNotFound(OPERATOR_USER, username);        }      });      if (users.length) {        selector.$and.push({          $or: [{ members: { $in: users } }, { assignees: { $in: users } }],        });      }    }    [OPERATOR_MEMBER, OPERATOR_ASSIGNEE, OPERATOR_CREATOR].forEach(key => {      if (queryParams.hasOperator(key)) {        const users = [];        queryParams.getPredicates(key).forEach(username => {          const user = Users.findOne({ username });          if (user) {            users.push(user._id);          } else {            errors.addNotFound(key, username);          }        });        if (users.length) {          selector[key] = { $in: users };        }      }    });    if (queryParams.hasOperator(OPERATOR_LABEL)) {      const queryLabels = [];      queryParams.getPredicates(OPERATOR_LABEL).forEach(label => {        let boards = Boards.userBoards(userId, null, {          labels: { $elemMatch: { color: label.toLowerCase() } },        });        if (boards.count()) {          boards.forEach(board => {            // eslint-disable-next-line no-console            // console.log('board:', board);            // eslint-disable-next-line no-console            // console.log('board.labels:', board.labels);            board.labels              .filter(boardLabel => {                return boardLabel.color === label.toLowerCase();              })              .forEach(boardLabel => {                queryLabels.push(boardLabel._id);              });          });        } else {          // eslint-disable-next-line no-console          // console.log('label:', label);          const reLabel = new RegExp(escapeForRegex(label), 'i');          // eslint-disable-next-line no-console          // console.log('reLabel:', reLabel);          boards = Boards.userBoards(userId, null, {            labels: { $elemMatch: { name: reLabel } },          });          if (boards.count()) {            boards.forEach(board => {              board.labels                .filter(boardLabel => {                  if (!boardLabel.name) {                    return false;                  }                  return boardLabel.name.match(reLabel);                })                .forEach(boardLabel => {                  queryLabels.push(boardLabel._id);                });            });          } else {            errors.addNotFound(OPERATOR_LABEL, label);          }        }      });      if (queryLabels.length) {        // eslint-disable-next-line no-console        // console.log('queryLabels:', queryLabels);        selector.labelIds = { $in: _.uniq(queryLabels) };      }    }    if (queryParams.hasOperator(OPERATOR_HAS)) {      queryParams.getPredicates(OPERATOR_HAS).forEach(has => {        switch (has.field) {          case PREDICATE_ATTACHMENT:            selector.$and.push({              _id: {                $in: Attachments.find({}, { fields: { cardId: 1 } }).map(                  a => a.cardId,                ),              },            });            break;          case PREDICATE_CHECKLIST:            selector.$and.push({              _id: {                $in: Checklists.find({}, { fields: { cardId: 1 } }).map(                  a => a.cardId,                ),              },            });            break;          case PREDICATE_DESCRIPTION:          case PREDICATE_START_AT:          case PREDICATE_DUE_AT:          case PREDICATE_END_AT:            if (has.exists) {              selector[has.field] = { $exists: true, $nin: [null, ''] };            } else {              selector[has.field] = { $in: [null, ''] };            }            break;          case PREDICATE_ASSIGNEES:          case PREDICATE_MEMBERS:            if (has.exists) {              selector[has.field] = { $exists: true, $nin: [null, []] };            } else {              selector[has.field] = { $in: [null, []] };            }            break;        }      });    }    if (queryParams.text) {      const regex = new RegExp(escapeForRegex(queryParams.text), 'i');      const items = ChecklistItems.find(        { title: regex },        { fields: { cardId: 1, checklistId: 1 } },      );      const checklists = Checklists.find(        {          $or: [            { title: regex },            { _id: { $in: items.map(item => item.checklistId) } },          ],        },        { fields: { cardId: 1 } },      );      const attachments = Attachments.find({ 'original.name': regex });      const comments = CardComments.find(        { text: regex },        { fields: { cardId: 1 } },      );      let cardsSelector = [          { title: regex },          { description: regex },          { customFields: { $elemMatch: { value: regex } } },          { _id: { $in: checklists.map(list => list.cardId) } },          { _id: { $in: attachments.map(attach => attach.cardId) } },          { _id: { $in: comments.map(com => com.cardId) } },        ];      if (queryParams.text === "false" || queryParams.text === "true") {        cardsSelector.push({ customFields: { $elemMatch: { value: queryParams.text === "true" } } } );      }      selector.$and.push({ $or: cardsSelector });    }    if (selector.$and.length === 0) {      delete selector.$and;    }  }  // eslint-disable-next-line no-console  // console.log('cards selector:', JSON.stringify(selector, null, 2));  const query = new Query();  query.selector = selector;  query.setQueryParams(queryParams);  query._errors = errors;  return query;}function buildProjection(query) {  // eslint-disable-next-line no-console  // console.log('query:', query);  let skip = 0;  if (query.getQueryParams().skip) {    skip = query.getQueryParams().skip;  }  let limit = DEFAULT_LIMIT;  const configLimit = parseInt(process.env.RESULTS_PER_PAGE, 10);  if (!isNaN(configLimit) && configLimit > 0) {    limit = configLimit;  }  if (query.getQueryParams().hasOperator(OPERATOR_LIMIT)) {    limit = query.getQueryParams().getPredicate(OPERATOR_LIMIT);  }  const projection = {    fields: {      _id: 1,      archived: 1,      boardId: 1,      swimlaneId: 1,      listId: 1,      title: 1,      type: 1,      sort: 1,      members: 1,      assignees: 1,      colors: 1,      dueAt: 1,      createdAt: 1,      modifiedAt: 1,      labelIds: 1,      customFields: 1,      userId: 1,      description: 1,    },    sort: {      boardId: 1,      swimlaneId: 1,      listId: 1,      sort: 1,    },    skip,    limit,  };  if (query.getQueryParams().hasOperator(OPERATOR_SORT)) {    const order =      query.getQueryParams().getPredicate(OPERATOR_SORT).order ===      ORDER_ASCENDING        ? 1        : -1;    switch (query.getQueryParams().getPredicate(OPERATOR_SORT).name) {      case PREDICATE_DUE_AT:        projection.sort = {          dueAt: order,          boardId: 1,          swimlaneId: 1,          listId: 1,          sort: 1,        };        break;      case PREDICATE_MODIFIED_AT:        projection.sort = {          modifiedAt: order,          boardId: 1,          swimlaneId: 1,          listId: 1,          sort: 1,        };        break;      case PREDICATE_CREATED_AT:        projection.sort = {          createdAt: order,          boardId: 1,          swimlaneId: 1,          listId: 1,          sort: 1,        };        break;      case PREDICATE_SYSTEM:        projection.sort = {          boardId: order,          swimlaneId: order,          listId: order,          modifiedAt: order,          sort: order,        };        break;    }  }  // eslint-disable-next-line no-console  // console.log('projection:', projection);  query.projection = projection;  return query;}function buildQuery(queryParams) {  const query = buildSelector(queryParams);  return buildProjection(query);}Meteor.publish('brokenCards', function(sessionId) {  check(sessionId, String);  const params = new QueryParams();  params.addPredicate(OPERATOR_STATUS, PREDICATE_ALL);  const query = buildQuery(params);  query.selector.$or = [    { boardId: { $in: [null, ''] } },    { swimlaneId: { $in: [null, ''] } },    { listId: { $in: [null, ''] } },    { type: { $nin: CARD_TYPES } },  ];  // console.log('brokenCards selector:', query.selector);  return findCards(sessionId, query);});Meteor.publish('nextPage', function(sessionId) {  check(sessionId, String);  const session = SessionData.findOne({ sessionId });  const projection = session.getProjection();  projection.skip = session.lastHit;  return findCards(sessionId, new Query(session.getSelector(), projection));});Meteor.publish('previousPage', function(sessionId) {  check(sessionId, String);  const session = SessionData.findOne({ sessionId });  const projection = session.getProjection();  projection.skip = session.lastHit - session.resultsCount - projection.limit;  return findCards(sessionId, new Query(session.getSelector(), projection));});function findCards(sessionId, query) {  const userId = Meteor.userId();  // eslint-disable-next-line no-console  // console.log('selector:', query.selector);  // console.log('selector.$and:', query.selector.$and);  // eslint-disable-next-line no-console  // console.log('projection:', query.projection);  const cards = Cards.find(query.selector, query.projection);  // eslint-disable-next-line no-console  // console.log('count:', cards.count());  const update = {    $set: {      totalHits: 0,      lastHit: 0,      resultsCount: 0,      cards: [],      selector: SessionData.pickle(query.selector),      projection: SessionData.pickle(query.projection),      errors: query.errors(),      debug: query.getQueryParams().getPredicate(OPERATOR_DEBUG)    },  };  if (cards) {    update.$set.totalHits = cards.count();    update.$set.lastHit =      query.projection.skip + query.projection.limit < cards.count()        ? query.projection.skip + query.projection.limit        : cards.count();    update.$set.cards = cards.map(card => {      return card._id;    });    update.$set.resultsCount = update.$set.cards.length;  }  // eslint-disable-next-line no-console  // console.log('sessionId:', sessionId);  // eslint-disable-next-line no-console  // console.log('userId:', userId);  // eslint-disable-next-line no-console  // console.log('update:', update);  SessionData.upsert({ userId, sessionId }, update);  // remove old session data  SessionData.remove({    userId,    modifiedAt: {      $lt: new Date(        moment()          .subtract(1, 'day')          .format(),      ),    },  });  if (cards) {    const boards = [];    const swimlanes = [];    const lists = [];    const customFieldIds = [];    const users = [this.userId];    cards.forEach(card => {      if (card.boardId) boards.push(card.boardId);      if (card.swimlaneId) swimlanes.push(card.swimlaneId);      if (card.listId) lists.push(card.listId);      if (card.userId) {        users.push(card.userId);      }      if (card.members) {        card.members.forEach(userId => {          users.push(userId);        });      }      if (card.assignees) {        card.assignees.forEach(userId => {          users.push(userId);        });      }      if (card.customFields) {        card.customFields.forEach(field => {          customFieldIds.push(field._id);        });      }    });    const fields = {      _id: 1,      title: 1,      archived: 1,      sort: 1,      type: 1,    };    return [      cards,      Boards.find(        { _id: { $in: boards } },        { fields: { ...fields, labels: 1, color: 1 } },      ),      Swimlanes.find(        { _id: { $in: swimlanes } },        { fields: { ...fields, color: 1 } },      ),      Lists.find({ _id: { $in: lists } }, { fields }),      CustomFields.find({ _id: { $in: customFieldIds } }),      Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),      Checklists.find({ cardId: { $in: cards.map(c => c._id) } }),      ChecklistItems.find({ cardId: { $in: cards.map(c => c._id) } }),      Attachments.find({ 'meta.cardId': { $in: cards.map(c => c._id) } }).cursor,      CardComments.find({ cardId: { $in: cards.map(c => c._id) } }),      SessionData.find({ userId, sessionId }),    ];  }  return [SessionData.find({ userId, sessionId })];}
 |