|
@@ -1,3 +1,50 @@
|
|
|
|
+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_DUE,
|
|
|
|
+ OPERATOR_HAS,
|
|
|
|
+ OPERATOR_LABEL,
|
|
|
|
+ OPERATOR_LIMIT,
|
|
|
|
+ OPERATOR_LIST,
|
|
|
|
+ OPERATOR_MEMBER,
|
|
|
|
+ OPERATOR_SORT,
|
|
|
|
+ OPERATOR_STATUS,
|
|
|
|
+ OPERATOR_SWIMLANE,
|
|
|
|
+ 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';
|
|
|
|
+
|
|
const escapeForRegex = require('escape-string-regexp');
|
|
const escapeForRegex = require('escape-string-regexp');
|
|
|
|
|
|
Meteor.publish('card', cardId => {
|
|
Meteor.publish('card', cardId => {
|
|
@@ -5,260 +52,50 @@ Meteor.publish('card', cardId => {
|
|
return Cards.find({ _id: cardId });
|
|
return Cards.find({ _id: cardId });
|
|
});
|
|
});
|
|
|
|
|
|
-Meteor.publish('myCards', function() {
|
|
|
|
- const userId = Meteor.userId();
|
|
|
|
-
|
|
|
|
- const archivedBoards = [];
|
|
|
|
- Boards.find({ archived: true }).forEach(board => {
|
|
|
|
- archivedBoards.push(board._id);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- const archivedSwimlanes = [];
|
|
|
|
- Swimlanes.find({ archived: true }).forEach(swimlane => {
|
|
|
|
- archivedSwimlanes.push(swimlane._id);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- const archivedLists = [];
|
|
|
|
- Lists.find({ archived: true }).forEach(list => {
|
|
|
|
- archivedLists.push(list._id);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- selector = {
|
|
|
|
- archived: false,
|
|
|
|
- boardId: { $nin: archivedBoards },
|
|
|
|
- swimlaneId: { $nin: archivedSwimlanes },
|
|
|
|
- listId: { $nin: archivedLists },
|
|
|
|
- $or: [{ members: userId }, { assignees: userId }],
|
|
|
|
- };
|
|
|
|
|
|
+Meteor.publish('myCards', function(sessionId) {
|
|
|
|
+ const queryParams = new QueryParams();
|
|
|
|
+ queryParams.addPredicate(OPERATOR_USER, Meteor.user().username);
|
|
|
|
|
|
- const cards = Cards.find(selector, {
|
|
|
|
- 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,
|
|
|
|
- },
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- const boards = [];
|
|
|
|
- const swimlanes = [];
|
|
|
|
- const lists = [];
|
|
|
|
- const users = [];
|
|
|
|
-
|
|
|
|
- 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.members) {
|
|
|
|
- card.members.forEach(userId => {
|
|
|
|
- users.push(userId);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- if (card.assignees) {
|
|
|
|
- card.assignees.forEach(userId => {
|
|
|
|
- users.push(userId);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- return [
|
|
|
|
- cards,
|
|
|
|
- Boards.find({ _id: { $in: boards } }),
|
|
|
|
- Swimlanes.find({ _id: { $in: swimlanes } }),
|
|
|
|
- Lists.find({ _id: { $in: lists } }),
|
|
|
|
- Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
|
|
|
- ];
|
|
|
|
|
|
+ return findCards(sessionId, buildQuery(queryParams));
|
|
});
|
|
});
|
|
|
|
|
|
-Meteor.publish('dueCards', function(allUsers = false) {
|
|
|
|
- check(allUsers, Boolean);
|
|
|
|
|
|
+// 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) {
|
|
|
|
+ check(sessionId, String);
|
|
|
|
+ check(params, Object);
|
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
// eslint-disable-next-line no-console
|
|
- // console.log('all users:', allUsers);
|
|
|
|
-
|
|
|
|
- const user = Users.findOne({ _id: this.userId });
|
|
|
|
-
|
|
|
|
- const archivedBoards = [];
|
|
|
|
- Boards.find({ archived: true }).forEach(board => {
|
|
|
|
- archivedBoards.push(board._id);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- const permiitedBoards = [];
|
|
|
|
- let selector = {
|
|
|
|
- archived: false,
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- selector.$or = [
|
|
|
|
- { permission: 'public' },
|
|
|
|
- { members: { $elemMatch: { userId: user._id, isActive: true } } },
|
|
|
|
- ];
|
|
|
|
-
|
|
|
|
- Boards.find(selector).forEach(board => {
|
|
|
|
- permiitedBoards.push(board._id);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- const archivedSwimlanes = [];
|
|
|
|
- Swimlanes.find({ archived: true }).forEach(swimlane => {
|
|
|
|
- archivedSwimlanes.push(swimlane._id);
|
|
|
|
- });
|
|
|
|
|
|
+ // console.log('queryParams:', params);
|
|
|
|
|
|
- const archivedLists = [];
|
|
|
|
- Lists.find({ archived: true }).forEach(list => {
|
|
|
|
- archivedLists.push(list._id);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- selector = {
|
|
|
|
- archived: false,
|
|
|
|
- boardId: { $nin: archivedBoards, $in: permiitedBoards },
|
|
|
|
- swimlaneId: { $nin: archivedSwimlanes },
|
|
|
|
- listId: { $nin: archivedLists },
|
|
|
|
- dueAt: { $ne: null },
|
|
|
|
- endAt: null,
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- if (!allUsers) {
|
|
|
|
- selector.$or = [{ members: user._id }, { assignees: user._id }];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const cards = Cards.find(selector, {
|
|
|
|
- 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,
|
|
|
|
- },
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- const boards = [];
|
|
|
|
- const swimlanes = [];
|
|
|
|
- const lists = [];
|
|
|
|
- const users = [];
|
|
|
|
-
|
|
|
|
- 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.members) {
|
|
|
|
- card.members.forEach(userId => {
|
|
|
|
- users.push(userId);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- if (card.assignees) {
|
|
|
|
- card.assignees.forEach(userId => {
|
|
|
|
- users.push(userId);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- return [
|
|
|
|
- cards,
|
|
|
|
- Boards.find({ _id: { $in: boards } }),
|
|
|
|
- Swimlanes.find({ _id: { $in: swimlanes } }),
|
|
|
|
- Lists.find({ _id: { $in: lists } }),
|
|
|
|
- Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
|
|
|
- ];
|
|
|
|
|
|
+ return findCards(sessionId, buildQuery(new QueryParams(params)));
|
|
});
|
|
});
|
|
|
|
|
|
-Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
|
|
- check(sessionId, String);
|
|
|
|
- check(queryParams, Object);
|
|
|
|
-
|
|
|
|
- // eslint-disable-next-line no-console
|
|
|
|
- // console.log('queryParams:', queryParams);
|
|
|
|
-
|
|
|
|
|
|
+function buildSelector(queryParams) {
|
|
const userId = Meteor.userId();
|
|
const userId = Meteor.userId();
|
|
- // eslint-disable-next-line no-console
|
|
|
|
- // console.log('userId:', userId);
|
|
|
|
-
|
|
|
|
- const errors = new (class {
|
|
|
|
- constructor() {
|
|
|
|
- this.notFound = {
|
|
|
|
- boards: [],
|
|
|
|
- swimlanes: [],
|
|
|
|
- lists: [],
|
|
|
|
- labels: [],
|
|
|
|
- users: [],
|
|
|
|
- members: [],
|
|
|
|
- assignees: [],
|
|
|
|
- status: [],
|
|
|
|
- comments: [],
|
|
|
|
- };
|
|
|
|
|
|
|
|
- this.colorMap = Boards.colorMap();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- hasErrors() {
|
|
|
|
- for (const value of Object.values(this.notFound)) {
|
|
|
|
- if (value.length) {
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- errorMessages() {
|
|
|
|
- const messages = [];
|
|
|
|
-
|
|
|
|
- this.notFound.boards.forEach(board => {
|
|
|
|
- messages.push({ tag: 'board-title-not-found', value: board });
|
|
|
|
- });
|
|
|
|
- this.notFound.swimlanes.forEach(swim => {
|
|
|
|
- messages.push({ tag: 'swimlane-title-not-found', value: swim });
|
|
|
|
- });
|
|
|
|
- this.notFound.lists.forEach(list => {
|
|
|
|
- messages.push({ tag: 'list-title-not-found', value: list });
|
|
|
|
- });
|
|
|
|
- this.notFound.comments.forEach(comments => {
|
|
|
|
- comments.forEach(text => {
|
|
|
|
- messages.push({ tag: 'comment-not-found', value: text });
|
|
|
|
- });
|
|
|
|
- });
|
|
|
|
- this.notFound.labels.forEach(label => {
|
|
|
|
- messages.push({
|
|
|
|
- tag: 'label-not-found',
|
|
|
|
- value: label,
|
|
|
|
- color: Boards.labelColors().includes(label),
|
|
|
|
- });
|
|
|
|
- });
|
|
|
|
- this.notFound.users.forEach(user => {
|
|
|
|
- messages.push({ tag: 'user-username-not-found', value: user });
|
|
|
|
- });
|
|
|
|
- this.notFound.members.forEach(user => {
|
|
|
|
- messages.push({ tag: 'user-username-not-found', value: user });
|
|
|
|
- });
|
|
|
|
- this.notFound.assignees.forEach(user => {
|
|
|
|
- messages.push({ tag: 'user-username-not-found', value: user });
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- return messages;
|
|
|
|
- }
|
|
|
|
- })();
|
|
|
|
|
|
+ const errors = new QueryErrors();
|
|
|
|
|
|
let selector = {};
|
|
let selector = {};
|
|
- let skip = 0;
|
|
|
|
- if (queryParams.skip) {
|
|
|
|
- skip = queryParams.skip;
|
|
|
|
- }
|
|
|
|
- let limit = 25;
|
|
|
|
- if (queryParams.limit) {
|
|
|
|
- limit = queryParams.limit;
|
|
|
|
- }
|
|
|
|
|
|
|
|
if (queryParams.selector) {
|
|
if (queryParams.selector) {
|
|
selector = queryParams.selector;
|
|
selector = queryParams.selector;
|
|
@@ -267,15 +104,15 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
|
|
|
|
let archived = false;
|
|
let archived = false;
|
|
let endAt = null;
|
|
let endAt = null;
|
|
- if (queryParams.status.length) {
|
|
|
|
- queryParams.status.forEach(status => {
|
|
|
|
- if (status === 'archived') {
|
|
|
|
|
|
+ if (queryParams.hasOperator(OPERATOR_STATUS)) {
|
|
|
|
+ queryParams.getPredicates(OPERATOR_STATUS).forEach(status => {
|
|
|
|
+ if (status === PREDICATE_ARCHIVED) {
|
|
archived = true;
|
|
archived = true;
|
|
- } else if (status === 'all') {
|
|
|
|
|
|
+ } else if (status === PREDICATE_ALL) {
|
|
archived = null;
|
|
archived = null;
|
|
- } else if (status === 'ended') {
|
|
|
|
|
|
+ } else if (status === PREDICATE_ENDED) {
|
|
endAt = { $nin: [null, ''] };
|
|
endAt = { $nin: [null, ''] };
|
|
- } else if (['private', 'public'].includes(status)) {
|
|
|
|
|
|
+ } else if ([PREDICATE_PRIVATE, PREDICATE_PUBLIC].includes(status)) {
|
|
boardsSelector.permission = status;
|
|
boardsSelector.permission = status;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
@@ -320,9 +157,9 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
selector.endAt = endAt;
|
|
selector.endAt = endAt;
|
|
}
|
|
}
|
|
|
|
|
|
- if (queryParams.boards.length) {
|
|
|
|
|
|
+ if (queryParams.hasOperator(OPERATOR_BOARD)) {
|
|
const queryBoards = [];
|
|
const queryBoards = [];
|
|
- queryParams.boards.forEach(query => {
|
|
|
|
|
|
+ queryParams.hasOperator(OPERATOR_BOARD).forEach(query => {
|
|
const boards = Boards.userSearch(userId, {
|
|
const boards = Boards.userSearch(userId, {
|
|
title: new RegExp(escapeForRegex(query), 'i'),
|
|
title: new RegExp(escapeForRegex(query), 'i'),
|
|
});
|
|
});
|
|
@@ -331,16 +168,16 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
queryBoards.push(board._id);
|
|
queryBoards.push(board._id);
|
|
});
|
|
});
|
|
} else {
|
|
} else {
|
|
- errors.notFound.boards.push(query);
|
|
|
|
|
|
+ errors.addNotFound(OPERATOR_BOARD, query);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
selector.boardId.$in = queryBoards;
|
|
selector.boardId.$in = queryBoards;
|
|
}
|
|
}
|
|
|
|
|
|
- if (queryParams.swimlanes.length) {
|
|
|
|
|
|
+ if (queryParams.hasOperator(OPERATOR_SWIMLANE)) {
|
|
const querySwimlanes = [];
|
|
const querySwimlanes = [];
|
|
- queryParams.swimlanes.forEach(query => {
|
|
|
|
|
|
+ queryParams.getPredicates(OPERATOR_SWIMLANE).forEach(query => {
|
|
const swimlanes = Swimlanes.find({
|
|
const swimlanes = Swimlanes.find({
|
|
title: new RegExp(escapeForRegex(query), 'i'),
|
|
title: new RegExp(escapeForRegex(query), 'i'),
|
|
});
|
|
});
|
|
@@ -349,7 +186,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
querySwimlanes.push(swim._id);
|
|
querySwimlanes.push(swim._id);
|
|
});
|
|
});
|
|
} else {
|
|
} else {
|
|
- errors.notFound.swimlanes.push(query);
|
|
|
|
|
|
+ errors.addNotFound(OPERATOR_SWIMLANE, query);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
@@ -360,9 +197,9 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
selector.swimlaneId.$in = querySwimlanes;
|
|
selector.swimlaneId.$in = querySwimlanes;
|
|
}
|
|
}
|
|
|
|
|
|
- if (queryParams.lists.length) {
|
|
|
|
|
|
+ if (queryParams.hasOperator(OPERATOR_LIST)) {
|
|
const queryLists = [];
|
|
const queryLists = [];
|
|
- queryParams.lists.forEach(query => {
|
|
|
|
|
|
+ queryParams.getPredicates(OPERATOR_LIST).forEach(query => {
|
|
const lists = Lists.find({
|
|
const lists = Lists.find({
|
|
title: new RegExp(escapeForRegex(query), 'i'),
|
|
title: new RegExp(escapeForRegex(query), 'i'),
|
|
});
|
|
});
|
|
@@ -371,7 +208,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
queryLists.push(list._id);
|
|
queryLists.push(list._id);
|
|
});
|
|
});
|
|
} else {
|
|
} else {
|
|
- errors.notFound.lists.push(query);
|
|
|
|
|
|
+ errors.addNotFound(OPERATOR_LIST, query);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
@@ -382,8 +219,10 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
selector.listId.$in = queryLists;
|
|
selector.listId.$in = queryLists;
|
|
}
|
|
}
|
|
|
|
|
|
- if (queryParams.comments.length) {
|
|
|
|
- const cardIds = CardComments.textSearch(userId, queryParams.comments).map(
|
|
|
|
|
|
+ if (queryParams.hasOperator(OPERATOR_COMMENT)) {
|
|
|
|
+ const cardIds = CardComments.textSearch(
|
|
|
|
+ userId,
|
|
|
|
+ queryParams.getPredicates(OPERATOR_COMMENT),
|
|
com => {
|
|
com => {
|
|
return com.cardId;
|
|
return com.cardId;
|
|
},
|
|
},
|
|
@@ -391,82 +230,75 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
if (cardIds.length) {
|
|
if (cardIds.length) {
|
|
selector._id = { $in: cardIds };
|
|
selector._id = { $in: cardIds };
|
|
} else {
|
|
} else {
|
|
- errors.notFound.comments.push(queryParams.comments);
|
|
|
|
|
|
+ queryParams.getPredicates(OPERATOR_COMMENT).forEach(comment => {
|
|
|
|
+ errors.addNotFound(OPERATOR_COMMENT, comment);
|
|
|
|
+ });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- ['dueAt', 'createdAt', 'modifiedAt'].forEach(field => {
|
|
|
|
- if (queryParams[field]) {
|
|
|
|
|
|
+ [OPERATOR_DUE, 'createdAt', 'modifiedAt'].forEach(field => {
|
|
|
|
+ if (queryParams.hasOperator(field)) {
|
|
selector[field] = {};
|
|
selector[field] = {};
|
|
- selector[field][queryParams[field]['operator']] = new Date(
|
|
|
|
- queryParams[field]['value'],
|
|
|
|
- );
|
|
|
|
|
|
+ const predicate = queryParams.getPredicate(field);
|
|
|
|
+ selector[field][predicate.operator] = new Date(predicate.value);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
- const queryMembers = [];
|
|
|
|
- const queryAssignees = [];
|
|
|
|
- if (queryParams.users.length) {
|
|
|
|
- queryParams.users.forEach(query => {
|
|
|
|
- const users = Users.find({
|
|
|
|
- username: query,
|
|
|
|
- });
|
|
|
|
- if (users.count()) {
|
|
|
|
- users.forEach(user => {
|
|
|
|
- queryMembers.push(user._id);
|
|
|
|
- queryAssignees.push(user._id);
|
|
|
|
- });
|
|
|
|
- } else {
|
|
|
|
- errors.notFound.users.push(query);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
|
|
+ const queryUsers = {};
|
|
|
|
+ queryUsers[OPERATOR_ASSIGNEE] = [];
|
|
|
|
+ queryUsers[OPERATOR_MEMBER] = [];
|
|
|
|
|
|
- if (queryParams.members.length) {
|
|
|
|
- queryParams.members.forEach(query => {
|
|
|
|
|
|
+ if (queryParams.hasOperator(OPERATOR_USER)) {
|
|
|
|
+ queryParams.getPredicates(OPERATOR_USER).forEach(query => {
|
|
const users = Users.find({
|
|
const users = Users.find({
|
|
username: query,
|
|
username: query,
|
|
});
|
|
});
|
|
if (users.count()) {
|
|
if (users.count()) {
|
|
users.forEach(user => {
|
|
users.forEach(user => {
|
|
- queryMembers.push(user._id);
|
|
|
|
|
|
+ queryUsers[OPERATOR_MEMBER].push(user._id);
|
|
|
|
+ queryUsers[OPERATOR_ASSIGNEE].push(user._id);
|
|
});
|
|
});
|
|
} else {
|
|
} else {
|
|
- errors.notFound.members.push(query);
|
|
|
|
|
|
+ errors.addNotFound(OPERATOR_USER, query);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
- if (queryParams.assignees.length) {
|
|
|
|
- queryParams.assignees.forEach(query => {
|
|
|
|
- const users = Users.find({
|
|
|
|
- username: query,
|
|
|
|
- });
|
|
|
|
- if (users.count()) {
|
|
|
|
- users.forEach(user => {
|
|
|
|
- queryAssignees.push(user._id);
|
|
|
|
|
|
+ [OPERATOR_MEMBER, OPERATOR_ASSIGNEE].forEach(key => {
|
|
|
|
+ if (queryParams.hasOperator(key)) {
|
|
|
|
+ queryParams.getPredicates(key).forEach(query => {
|
|
|
|
+ const users = Users.find({
|
|
|
|
+ username: query,
|
|
});
|
|
});
|
|
- } else {
|
|
|
|
- errors.notFound.assignees.push(query);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
|
|
+ if (users.count()) {
|
|
|
|
+ users.forEach(user => {
|
|
|
|
+ queryUsers[key].push(user._id);
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ errors.addNotFound(key, query);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
|
|
- if (queryMembers.length && queryAssignees.length) {
|
|
|
|
|
|
+ if (
|
|
|
|
+ queryUsers[OPERATOR_MEMBER].length &&
|
|
|
|
+ queryUsers[OPERATOR_ASSIGNEE].length
|
|
|
|
+ ) {
|
|
selector.$and.push({
|
|
selector.$and.push({
|
|
$or: [
|
|
$or: [
|
|
- { members: { $in: queryMembers } },
|
|
|
|
- { assignees: { $in: queryAssignees } },
|
|
|
|
|
|
+ { members: { $in: queryUsers[OPERATOR_MEMBER] } },
|
|
|
|
+ { assignees: { $in: queryUsers[OPERATOR_ASSIGNEE] } },
|
|
],
|
|
],
|
|
});
|
|
});
|
|
- } else if (queryMembers.length) {
|
|
|
|
- selector.members = { $in: queryMembers };
|
|
|
|
- } else if (queryAssignees.length) {
|
|
|
|
- selector.assignees = { $in: queryAssignees };
|
|
|
|
|
|
+ } else if (queryUsers[OPERATOR_MEMBER].length) {
|
|
|
|
+ selector.members = { $in: queryUsers[OPERATOR_MEMBER] };
|
|
|
|
+ } else if (queryUsers[OPERATOR_ASSIGNEE].length) {
|
|
|
|
+ selector.assignees = { $in: queryUsers[OPERATOR_ASSIGNEE] };
|
|
}
|
|
}
|
|
|
|
|
|
- if (queryParams.labels.length) {
|
|
|
|
- queryParams.labels.forEach(label => {
|
|
|
|
|
|
+ if (queryParams.hasOperator(OPERATOR_LABEL)) {
|
|
|
|
+ queryParams.getPredicates(OPERATOR_LABEL).forEach(label => {
|
|
const queryLabels = [];
|
|
const queryLabels = [];
|
|
|
|
|
|
let boards = Boards.userSearch(userId, {
|
|
let boards = Boards.userSearch(userId, {
|
|
@@ -511,39 +343,47 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
});
|
|
});
|
|
});
|
|
});
|
|
} else {
|
|
} else {
|
|
- errors.notFound.labels.push(label);
|
|
|
|
|
|
+ errors.addNotFound(OPERATOR_LABEL, label);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- selector.labelIds = { $in: queryLabels };
|
|
|
|
|
|
+ selector.labelIds = { $in: _.uniq(queryLabels) };
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
- if (queryParams.has.length) {
|
|
|
|
- queryParams.has.forEach(has => {
|
|
|
|
|
|
+ if (queryParams.hasOperator(OPERATOR_HAS)) {
|
|
|
|
+ queryParams.getPredicates(OPERATOR_HAS).forEach(has => {
|
|
switch (has.field) {
|
|
switch (has.field) {
|
|
- case 'attachment':
|
|
|
|
- const attachments = Attachments.find({}, { fields: { cardId: 1 } });
|
|
|
|
|
|
+ case PREDICATE_ATTACHMENT:
|
|
selector.$and.push({
|
|
selector.$and.push({
|
|
- _id: { $in: attachments.map(a => a.cardId) },
|
|
|
|
|
|
+ _id: {
|
|
|
|
+ $in: Attachments.find({}, { fields: { cardId: 1 } }).map(
|
|
|
|
+ a => a.cardId,
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
});
|
|
});
|
|
break;
|
|
break;
|
|
- case 'checklist':
|
|
|
|
- const checklists = Checklists.find({}, { fields: { cardId: 1 } });
|
|
|
|
- selector.$and.push({ _id: { $in: checklists.map(a => a.cardId) } });
|
|
|
|
|
|
+ case PREDICATE_CHECKLIST:
|
|
|
|
+ selector.$and.push({
|
|
|
|
+ _id: {
|
|
|
|
+ $in: Checklists.find({}, { fields: { cardId: 1 } }).map(
|
|
|
|
+ a => a.cardId,
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ });
|
|
break;
|
|
break;
|
|
- case 'description':
|
|
|
|
- case 'startAt':
|
|
|
|
- case 'dueAt':
|
|
|
|
- case 'endAt':
|
|
|
|
|
|
+ case PREDICATE_DESCRIPTION:
|
|
|
|
+ case PREDICATE_START_AT:
|
|
|
|
+ case PREDICATE_DUE_AT:
|
|
|
|
+ case PREDICATE_END_AT:
|
|
if (has.exists) {
|
|
if (has.exists) {
|
|
selector[has.field] = { $exists: true, $nin: [null, ''] };
|
|
selector[has.field] = { $exists: true, $nin: [null, ''] };
|
|
} else {
|
|
} else {
|
|
selector[has.field] = { $in: [null, ''] };
|
|
selector[has.field] = { $in: [null, ''] };
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
- case 'assignees':
|
|
|
|
- case 'members':
|
|
|
|
|
|
+ case PREDICATE_ASSIGNEES:
|
|
|
|
+ case PREDICATE_MEMBERS:
|
|
if (has.exists) {
|
|
if (has.exists) {
|
|
selector[has.field] = { $exists: true, $nin: [null, []] };
|
|
selector[has.field] = { $exists: true, $nin: [null, []] };
|
|
} else {
|
|
} else {
|
|
@@ -573,26 +413,26 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
|
|
|
|
const attachments = Attachments.find({ 'original.name': regex });
|
|
const attachments = Attachments.find({ 'original.name': regex });
|
|
|
|
|
|
- // const comments = CardComments.find(
|
|
|
|
- // { text: regex },
|
|
|
|
- // { fields: { cardId: 1 } },
|
|
|
|
- // );
|
|
|
|
|
|
+ const comments = CardComments.find(
|
|
|
|
+ { text: regex },
|
|
|
|
+ { fields: { cardId: 1 } },
|
|
|
|
+ );
|
|
|
|
|
|
selector.$and.push({
|
|
selector.$and.push({
|
|
$or: [
|
|
$or: [
|
|
{ title: regex },
|
|
{ title: regex },
|
|
{ description: regex },
|
|
{ description: regex },
|
|
{ customFields: { $elemMatch: { value: regex } } },
|
|
{ customFields: { $elemMatch: { value: regex } } },
|
|
- {
|
|
|
|
- _id: {
|
|
|
|
- $in: CardComments.textSearch(userId, [queryParams.text]).map(
|
|
|
|
- com => com.cardId,
|
|
|
|
- ),
|
|
|
|
- },
|
|
|
|
- },
|
|
|
|
|
|
+ // {
|
|
|
|
+ // _id: {
|
|
|
|
+ // $in: CardComments.textSearch(userId, [queryParams.text]).map(
|
|
|
|
+ // com => com.cardId,
|
|
|
|
+ // ),
|
|
|
|
+ // },
|
|
|
|
+ // },
|
|
{ _id: { $in: checklists.map(list => list.cardId) } },
|
|
{ _id: { $in: checklists.map(list => list.cardId) } },
|
|
{ _id: { $in: attachments.map(attach => attach.cardId) } },
|
|
{ _id: { $in: attachments.map(attach => attach.cardId) } },
|
|
- // { _id: { $in: comments.map(com => com.cardId) } },
|
|
|
|
|
|
+ { _id: { $in: comments.map(com => com.cardId) } },
|
|
],
|
|
],
|
|
});
|
|
});
|
|
}
|
|
}
|
|
@@ -607,6 +447,29 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
// eslint-disable-next-line no-console
|
|
// eslint-disable-next-line no-console
|
|
// console.log('selector.$and:', selector.$and);
|
|
// console.log('selector.$and:', selector.$and);
|
|
|
|
|
|
|
|
+ const query = new Query();
|
|
|
|
+ query.selector = selector;
|
|
|
|
+ query.params = queryParams;
|
|
|
|
+ query._errors = errors;
|
|
|
|
+
|
|
|
|
+ return query;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function buildProjection(query) {
|
|
|
|
+ let skip = 0;
|
|
|
|
+ if (query.params.skip) {
|
|
|
|
+ skip = query.params.skip;
|
|
|
|
+ }
|
|
|
|
+ let limit = DEFAULT_LIMIT;
|
|
|
|
+ const configLimit = parseInt(process.env.RESULTS_PER_PAGE, 10);
|
|
|
|
+ if (!isNaN(configLimit) && configLimit > 0) {
|
|
|
|
+ limit = configLimit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (query.params.hasOperator(OPERATOR_LIMIT)) {
|
|
|
|
+ limit = query.params.getPredicate(OPERATOR_LIMIT);
|
|
|
|
+ }
|
|
|
|
+
|
|
const projection = {
|
|
const projection = {
|
|
fields: {
|
|
fields: {
|
|
_id: 1,
|
|
_id: 1,
|
|
@@ -636,10 +499,13 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
limit,
|
|
limit,
|
|
};
|
|
};
|
|
|
|
|
|
- if (queryParams.sort) {
|
|
|
|
- const order = queryParams.sort.order === 'asc' ? 1 : -1;
|
|
|
|
- switch (queryParams.sort.name) {
|
|
|
|
- case 'dueAt':
|
|
|
|
|
|
+ if (query.params.hasOperator(OPERATOR_SORT)) {
|
|
|
|
+ const order =
|
|
|
|
+ query.params.getPredicate(OPERATOR_SORT).order === ORDER_ASCENDING
|
|
|
|
+ ? 1
|
|
|
|
+ : -1;
|
|
|
|
+ switch (query.params.getPredicate(OPERATOR_SORT).name) {
|
|
|
|
+ case PREDICATE_DUE_AT:
|
|
projection.sort = {
|
|
projection.sort = {
|
|
dueAt: order,
|
|
dueAt: order,
|
|
boardId: 1,
|
|
boardId: 1,
|
|
@@ -648,7 +514,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
sort: 1,
|
|
sort: 1,
|
|
};
|
|
};
|
|
break;
|
|
break;
|
|
- case 'modifiedAt':
|
|
|
|
|
|
+ case PREDICATE_MODIFIED_AT:
|
|
projection.sort = {
|
|
projection.sort = {
|
|
modifiedAt: order,
|
|
modifiedAt: order,
|
|
boardId: 1,
|
|
boardId: 1,
|
|
@@ -657,7 +523,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
sort: 1,
|
|
sort: 1,
|
|
};
|
|
};
|
|
break;
|
|
break;
|
|
- case 'createdAt':
|
|
|
|
|
|
+ case PREDICATE_CREATED_AT:
|
|
projection.sort = {
|
|
projection.sort = {
|
|
createdAt: order,
|
|
createdAt: order,
|
|
boardId: 1,
|
|
boardId: 1,
|
|
@@ -666,7 +532,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
sort: 1,
|
|
sort: 1,
|
|
};
|
|
};
|
|
break;
|
|
break;
|
|
- case 'system':
|
|
|
|
|
|
+ case PREDICATE_SYSTEM:
|
|
projection.sort = {
|
|
projection.sort = {
|
|
boardId: order,
|
|
boardId: order,
|
|
swimlaneId: order,
|
|
swimlaneId: order,
|
|
@@ -681,77 +547,31 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|
// eslint-disable-next-line no-console
|
|
// eslint-disable-next-line no-console
|
|
// console.log('projection:', projection);
|
|
// console.log('projection:', projection);
|
|
|
|
|
|
- return findCards(sessionId, selector, projection, errors);
|
|
|
|
-});
|
|
|
|
-
|
|
|
|
-Meteor.publish('brokenCards', function() {
|
|
|
|
- const user = Users.findOne({ _id: this.userId });
|
|
|
|
-
|
|
|
|
- const permiitedBoards = [null];
|
|
|
|
- let selector = {};
|
|
|
|
- selector.$or = [
|
|
|
|
- { permission: 'public' },
|
|
|
|
- { members: { $elemMatch: { userId: user._id, isActive: true } } },
|
|
|
|
- ];
|
|
|
|
|
|
+ query.projection = projection;
|
|
|
|
|
|
- Boards.find(selector).forEach(board => {
|
|
|
|
- permiitedBoards.push(board._id);
|
|
|
|
- });
|
|
|
|
|
|
+ return query;
|
|
|
|
+}
|
|
|
|
|
|
- selector = {
|
|
|
|
- boardId: { $in: permiitedBoards },
|
|
|
|
- $or: [
|
|
|
|
- { boardId: { $in: [null, ''] } },
|
|
|
|
- { swimlaneId: { $in: [null, ''] } },
|
|
|
|
- { listId: { $in: [null, ''] } },
|
|
|
|
- ],
|
|
|
|
- };
|
|
|
|
|
|
+function buildQuery(queryParams) {
|
|
|
|
+ const query = buildSelector(queryParams);
|
|
|
|
|
|
- const cards = Cards.find(selector, {
|
|
|
|
- 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,
|
|
|
|
- },
|
|
|
|
- });
|
|
|
|
|
|
+ return buildProjection(query);
|
|
|
|
+}
|
|
|
|
|
|
- const boards = [];
|
|
|
|
- const swimlanes = [];
|
|
|
|
- const lists = [];
|
|
|
|
- const users = [];
|
|
|
|
-
|
|
|
|
- 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.members) {
|
|
|
|
- card.members.forEach(userId => {
|
|
|
|
- users.push(userId);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- if (card.assignees) {
|
|
|
|
- card.assignees.forEach(userId => {
|
|
|
|
- users.push(userId);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+Meteor.publish('brokenCards', function(sessionId) {
|
|
|
|
+ check(sessionId, String);
|
|
|
|
|
|
- return [
|
|
|
|
- cards,
|
|
|
|
- Boards.find({ _id: { $in: boards } }),
|
|
|
|
- Swimlanes.find({ _id: { $in: swimlanes } }),
|
|
|
|
- Lists.find({ _id: { $in: lists } }),
|
|
|
|
- Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
|
|
|
|
|
+ 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, ''] } },
|
|
];
|
|
];
|
|
|
|
+ // console.log('brokenCards selector:', query.selector);
|
|
|
|
+
|
|
|
|
+ return findCards(sessionId, query);
|
|
});
|
|
});
|
|
|
|
|
|
Meteor.publish('nextPage', function(sessionId) {
|
|
Meteor.publish('nextPage', function(sessionId) {
|
|
@@ -761,7 +581,7 @@ Meteor.publish('nextPage', function(sessionId) {
|
|
const projection = session.getProjection();
|
|
const projection = session.getProjection();
|
|
projection.skip = session.lastHit;
|
|
projection.skip = session.lastHit;
|
|
|
|
|
|
- return findCards(sessionId, session.getSelector(), projection);
|
|
|
|
|
|
+ return findCards(sessionId, new Query(session.getSelector(), projection));
|
|
});
|
|
});
|
|
|
|
|
|
Meteor.publish('previousPage', function(sessionId) {
|
|
Meteor.publish('previousPage', function(sessionId) {
|
|
@@ -771,19 +591,20 @@ Meteor.publish('previousPage', function(sessionId) {
|
|
const projection = session.getProjection();
|
|
const projection = session.getProjection();
|
|
projection.skip = session.lastHit - session.resultsCount - projection.limit;
|
|
projection.skip = session.lastHit - session.resultsCount - projection.limit;
|
|
|
|
|
|
- return findCards(sessionId, session.getSelector(), projection);
|
|
|
|
|
|
+ return findCards(sessionId, new Query(session.getSelector(), projection));
|
|
});
|
|
});
|
|
|
|
|
|
-function findCards(sessionId, selector, projection, errors = null) {
|
|
|
|
|
|
+function findCards(sessionId, query) {
|
|
const userId = Meteor.userId();
|
|
const userId = Meteor.userId();
|
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
// eslint-disable-next-line no-console
|
|
- // console.log('selector:', selector);
|
|
|
|
|
|
+ // console.log('selector:', query.selector);
|
|
|
|
+ // console.log('selector.$and:', query.selector.$and);
|
|
// eslint-disable-next-line no-console
|
|
// eslint-disable-next-line no-console
|
|
// console.log('projection:', projection);
|
|
// console.log('projection:', projection);
|
|
let cards;
|
|
let cards;
|
|
- if (!errors || !errors.hasErrors()) {
|
|
|
|
- cards = Cards.find(selector, projection);
|
|
|
|
|
|
+ if (!query.hasErrors()) {
|
|
|
|
+ cards = Cards.find(query.selector, query.projection);
|
|
}
|
|
}
|
|
// eslint-disable-next-line no-console
|
|
// eslint-disable-next-line no-console
|
|
// console.log('count:', cards.count());
|
|
// console.log('count:', cards.count());
|
|
@@ -794,19 +615,17 @@ function findCards(sessionId, selector, projection, errors = null) {
|
|
lastHit: 0,
|
|
lastHit: 0,
|
|
resultsCount: 0,
|
|
resultsCount: 0,
|
|
cards: [],
|
|
cards: [],
|
|
- selector: SessionData.pickle(selector),
|
|
|
|
- projection: SessionData.pickle(projection),
|
|
|
|
|
|
+ selector: SessionData.pickle(query.selector),
|
|
|
|
+ projection: SessionData.pickle(query.projection),
|
|
|
|
+ errors: query.errors(),
|
|
},
|
|
},
|
|
};
|
|
};
|
|
- if (errors) {
|
|
|
|
- update.$set.errors = errors.errorMessages();
|
|
|
|
- }
|
|
|
|
|
|
|
|
if (cards) {
|
|
if (cards) {
|
|
update.$set.totalHits = cards.count();
|
|
update.$set.totalHits = cards.count();
|
|
update.$set.lastHit =
|
|
update.$set.lastHit =
|
|
- projection.skip + projection.limit < cards.count()
|
|
|
|
- ? projection.skip + projection.limit
|
|
|
|
|
|
+ query.projection.skip + query.projection.limit < cards.count()
|
|
|
|
+ ? query.projection.skip + query.projection.limit
|
|
: cards.count();
|
|
: cards.count();
|
|
update.$set.cards = cards.map(card => {
|
|
update.$set.cards = cards.map(card => {
|
|
return card._id;
|
|
return card._id;
|
|
@@ -884,5 +703,5 @@ function findCards(sessionId, selector, projection, errors = null) {
|
|
];
|
|
];
|
|
}
|
|
}
|
|
|
|
|
|
- return [SessionData.find({ userId: userId, sessionId })];
|
|
|
|
|
|
+ return [SessionData.find({ userId, sessionId })];
|
|
}
|
|
}
|