diff --git a/client/components/main/globalSearch.js b/client/components/main/globalSearch.js index d2c7a25fc..a6013baf2 100644 --- a/client/components/main/globalSearch.js +++ b/client/components/main/globalSearch.js @@ -239,6 +239,8 @@ BlazeComponent.extendComponent({ 'predicate-archived': 'archived', 'predicate-all': 'all', 'predicate-ended': 'ended', + 'predicate-public': 'public', + 'predicate-private': 'private', }, sorts: { 'predicate-due': 'dueAt', @@ -540,6 +542,8 @@ BlazeComponent.extendComponent({ predicate_attachment: TAPi18n.__('predicate-attachment'), predicate_description: TAPi18n.__('predicate-description'), predicate_checklist: TAPi18n.__('predicate-checklist'), + predicate_public: TAPi18n.__('predicate-public'), + predicate_private: TAPi18n.__('predicate-private'), }; text = `# ${TAPi18n.__('globalSearch-instructions-heading')}`; @@ -595,6 +599,14 @@ BlazeComponent.extendComponent({ 'globalSearch-instructions-status-archived', tags, )}`; + text += `\n* ${TAPi18n.__( + 'globalSearch-instructions-status-public', + tags, + )}`; + text += `\n* ${TAPi18n.__( + 'globalSearch-instructions-status-private', + tags, + )}`; text += `\n* ${TAPi18n.__('globalSearch-instructions-status-all', tags)}`; text += `\n* ${TAPi18n.__('globalSearch-instructions-status-ended', tags)}`; diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index c1e39cf26..ffb4d0f2c 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -921,6 +921,8 @@ "predicate-attachment": "attachment", "predicate-description": "description", "predicate-checklist": "checklist", + "predicate-public": "public", + "predicate-private": "private", "operator-unknown-error": "%s is not an operator", "operator-number-expected": "operator __operator__ expected a number, got '__value__'", "operator-sort-invalid": "sort of '%s' is invalid", @@ -948,6 +950,8 @@ "globalSearch-instructions-status-archived": "`__operator_status__:__predicate_archived__` - cards that are archived.", "globalSearch-instructions-status-all": "`__operator_status__:__predicate_all__` - all archived and unarchived cards.", "globalSearch-instructions-status-ended": "`__operator_status__:__predicate_ended__` - cards with an end date.", + "globalSearch-instructions-status-public": "`__operator_status__:__predicate_public__` - cards only in public boards.", + "globalSearch-instructions-status-private": "`__operator_status__:__predicate_private__` - cards only in private boards.", "globalSearch-instructions-operator-has": "`__operator_has__:field` - where *field* is one of `__predicate_attachment__`, `__predicate_checklist__` or `__predicate_description__`", "globalSearch-instructions-notes-1": "Multiple operators may be specified.", "globalSearch-instructions-notes-2": "Similar operators are *OR*ed together. Cards that match any of the conditions will be returned.\n`__operator_list__:Available __operator_list__:Blocked` would return cards contained in any list named *Blocked* or *Available*.", diff --git a/server/publications/cards.js b/server/publications/cards.js index 77a5734fb..e61dce1f1 100644 --- a/server/publications/cards.js +++ b/server/publications/cards.js @@ -263,6 +263,8 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) { if (queryParams.selector) { selector = queryParams.selector; } else { + const boardsSelector = {}; + let archived = false; let endAt = null; if (queryParams.status.length) { @@ -273,6 +275,8 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) { archived = null; } else if (status === 'ended') { endAt = { $nin: [null, ''] }; + } else if (['private', 'public'].includes(status)) { + boardsSelector.permission = status; } }); } @@ -282,27 +286,35 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) { $and: [], }; - const boardsSelector = {}; if (archived !== null) { - boardsSelector.archived = archived; if (archived) { - selector.boardId = { $in: Boards.userBoardIds(userId, null) }; + selector.boardId = { + $in: Boards.userBoardIds(userId, null, boardsSelector), + }; selector.$and.push({ $or: [ - { boardId: { $in: Boards.userBoardIds(userId, archived) } }, + { + boardId: { + $in: Boards.userBoardIds(userId, archived, boardsSelector), + }, + }, { swimlaneId: { $in: Swimlanes.archivedSwimlaneIds() } }, { listId: { $in: Lists.archivedListIds() } }, { archived: true }, ], }); } else { - selector.boardId = { $in: Boards.userBoardIds(userId, false) }; + 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) }; + selector.boardId = { + $in: Boards.userBoardIds(userId, null, boardsSelector), + }; } if (endAt !== null) { selector.endAt = endAt; @@ -720,6 +732,9 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) { 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) } }), + Attachments.find({ cardId: { $in: cards.map(c => c._id) } }), + CardComments.find({ cardId: { $in: cards.map(c => c._id) } }), SessionData.find({ userId: this.userId, sessionId }), ]; }