mirror of https://github.com/wekan/wekan
The Open Source kanban (built with Meteor). Keep variable/table/field names camelCase. For translations, only add Pull Request changes to wekan/i18n/en.i18n.json , other translations are done at https://transifex.com/wekan/wekan only.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
803 lines
22 KiB
803 lines
22 KiB
import { ReactiveCache } from '/imports/reactiveCache';
|
|
import moment from 'moment/min/moment-with-locales';
|
|
import escapeForRegex from 'escape-string-regexp';
|
|
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";
|
|
|
|
Meteor.publish('card', cardId => {
|
|
check(cardId, String);
|
|
const ret = ReactiveCache.getCards(
|
|
{ _id: cardId },
|
|
{},
|
|
true,
|
|
);
|
|
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(
|
|
ReactiveCache.getCards(
|
|
{ _id: cardId },
|
|
{},
|
|
true,
|
|
),
|
|
function(cardId, card) {
|
|
this.cursor(ReactiveCache.getBoards({_id: card.boardId}, {}, true));
|
|
this.cursor(ReactiveCache.getLists({boardId: card.boardId}, {}, true));
|
|
},
|
|
);
|
|
const ret = this.ready()
|
|
return ret;
|
|
});
|
|
|
|
Meteor.publish('myCards', function(sessionId) {
|
|
check(sessionId, String);
|
|
|
|
const queryParams = new QueryParams();
|
|
queryParams.addPredicate(OPERATOR_USER, ReactiveCache.getCurrentUser().username);
|
|
queryParams.setPredicate(OPERATOR_LIMIT, 200);
|
|
|
|
const query = buildQuery(queryParams);
|
|
query.projection.sort = {
|
|
boardId: 1,
|
|
swimlaneId: 1,
|
|
listId: 1,
|
|
};
|
|
|
|
const ret = findCards(sessionId, query);
|
|
return ret;
|
|
});
|
|
|
|
// 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 = [ReactiveCache.getCurrentUser().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);
|
|
|
|
const ret = findCards(sessionId, buildQuery(new QueryParams(params, text)));
|
|
return ret;
|
|
});
|
|
|
|
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 = ReactiveCache.getOrg({
|
|
$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 = ReactiveCache.getTeam({
|
|
$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.length) {
|
|
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 = ReactiveCache.getSwimlanes({
|
|
title: new RegExp(escapeForRegex(query), 'i'),
|
|
});
|
|
if (swimlanes.length) {
|
|
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 = ReactiveCache.getLists({
|
|
title: new RegExp(escapeForRegex(query), 'i'),
|
|
});
|
|
if (lists.length) {
|
|
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 = ReactiveCache.getUser({ 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 = ReactiveCache.getUser({ 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.length) {
|
|
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.length) {
|
|
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: ReactiveCache.getAttachments({}, { fields: { cardId: 1 } }).map(
|
|
a => a.cardId,
|
|
),
|
|
},
|
|
});
|
|
break;
|
|
case PREDICATE_CHECKLIST:
|
|
selector.$and.push({
|
|
_id: {
|
|
$in: ReactiveCache.getChecklists({}, { 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 = ReactiveCache.getChecklists(
|
|
{
|
|
$or: [
|
|
{ title: regex },
|
|
{ _id: { $in: items.map(item => item.checklistId) } },
|
|
],
|
|
},
|
|
{ fields: { cardId: 1 } },
|
|
);
|
|
|
|
const attachments = ReactiveCache.getAttachments({ 'original.name': regex });
|
|
|
|
const comments = ReactiveCache.getCardComments(
|
|
{ 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,
|
|
};
|
|
if (limit > 0) {
|
|
projection.limit = 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);
|
|
|
|
const ret = findCards(sessionId, query);
|
|
return ret;
|
|
});
|
|
|
|
Meteor.publish('nextPage', function(sessionId) {
|
|
check(sessionId, String);
|
|
|
|
const session = ReactiveCache.getSessionData({ sessionId });
|
|
const projection = session.getProjection();
|
|
projection.skip = session.lastHit;
|
|
|
|
const ret = findCards(sessionId, new Query(session.getSelector(), projection));
|
|
return ret;
|
|
});
|
|
|
|
Meteor.publish('previousPage', function(sessionId) {
|
|
check(sessionId, String);
|
|
|
|
const session = ReactiveCache.getSessionData({ sessionId });
|
|
const projection = session.getProjection();
|
|
projection.skip = session.lastHit - session.resultsCount - projection.limit;
|
|
|
|
const ret = findCards(sessionId, new Query(session.getSelector(), projection));
|
|
return ret;
|
|
});
|
|
|
|
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 = ReactiveCache.getCards(query.selector, query.projection, true);
|
|
// 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,
|
|
ReactiveCache.getBoards(
|
|
{ _id: { $in: boards } },
|
|
{ fields: { ...fields, labels: 1, color: 1 } },
|
|
true,
|
|
),
|
|
ReactiveCache.getSwimlanes(
|
|
{ _id: { $in: swimlanes } },
|
|
{ fields: { ...fields, color: 1 } },
|
|
true,
|
|
),
|
|
ReactiveCache.getLists({ _id: { $in: lists } }, { fields }, true),
|
|
CustomFields.find({ _id: { $in: customFieldIds } }),
|
|
ReactiveCache.getUsers({ _id: { $in: users } }, { fields: Users.safeFields }, true),
|
|
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 })];
|
|
}
|
|
|