Merge pull request #3437 from jrsupplee/search

Global Search fixes and updates
This commit is contained in:
Lauri Ojansivu 2021-01-17 23:53:18 +02:00 committed by GitHub
commit 7f0da49e0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 489 additions and 264 deletions

View file

@ -1,6 +1,6 @@
template(name="resultCard") template(name="resultCard")
.result-card-wrapper .result-card-wrapper
a.minicard-wrapper.card-title(href=card.absoluteUrl) a.minicard-wrapper.card-title(href=absoluteUrl)
+minicard(this) +minicard(this)
//= card.title //= card.title
ul.result-card-context-list ul.result-card-context-list

View file

@ -14,52 +14,26 @@ template(name="globalSearch")
if currentUser if currentUser
.wrapper .wrapper
form.global-search-instructions.js-search-query-form form.global-search-instructions.js-search-query-form
input.global-search-query-input(type="text" name="searchQuery" placeholder="{{_ 'search-example'}}" autofocus dir="auto") input.global-search-query-input(type="text" name="searchQuery" placeholder="{{_ 'search-example'}}" value="{{ query.get }}" autofocus dir="auto")
if searching.get if searching.get
+spinner +spinner
else if hasResults.get else if hasResults.get
.global-search-dueat-list-wrapper .global-search-results-list-wrapper
h1 if hasQueryErrors.get
if $eq resultsCount.get 0
| {{_ 'no-cards-found' }}
else if $eq resultsCount.get 1
| {{_ 'one-card-found' }}
else if $eq resultsCount.get totalHits.get
| {{_ 'n-cards-found' resultsCount.get }}
else
| {{_ 'n-n-of-n-cards-found' 1 resultsCount.get totalHits.get }}
if queryErrors.get
div div
each msg in errorMessages each msg in errorMessages
span.global-search-error-messages span.global-search-error-messages
| {{_ msg.tag msg.value }} | {{_ msg.tag msg.value }}
else
h1
= resultsHeading.get
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
each card in results each card in results
a.minicard-wrapper(href=card.absoluteUrl)
+resultCard(card) +resultCard(card)
else else
.global-search-instructions .global-search-instructions
h1 Search Operators
+viewer +viewer
= 'Searches can include operators to refine the search. Operators are specified by writing the operator' = searchInstructions
= 'name and value separated by a colon. For example, an operator specification of `list:Blocked` would limit the search'
= 'to cards that are contained in a list named *Blocked*. If the value contains spaces or special characters'
= 'it must be enclosed in quotation marks (e.g. `list:"To Review"`).\n'
= 'Available operators are:\n'
= '* `board:title` - cards in boards matching the specified title\n'
= '* `list:title` - cards in lists matching the specified title\n'
= '* `swimlane:title` - cards in swimlanes matching the specified title\n'
= '* `label:color` - cards that have a label matching the given color\n'
= '* `label:name` - cards that have a label matching the given name\n'
= '* `user:username` - cards where the specified user is a member or assignee\n'
= '* `@username` - shorthand for `user:username`\n'
= '* `#label` - shorthand for `label:color-or-name`\n'
= '## Notes\n'
= '* Multiple operators may be specified.\n'
= '* Similar operators are *OR*ed together. Cards that match any of the conditions will be returned.\n'
= ' `list:Available list:Blocked` would return cards contained in any list named *Blocked* or *Available*.\n'
= '* Differing operators are *AND*ed together. Only cards that match all of the differing operators are returned.\n'
= '`list:Available label:red` returns only cards in the list *Available* with a *red* label.\n'
= '* Text searches are case insensitive.\n'
template(name="globalSearchViewChangePopup") template(name="globalSearchViewChangePopup")
if currentUser if currentUser

View file

@ -36,72 +36,106 @@ BlazeComponent.extendComponent({
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
onCreated() { onCreated() {
this.isPageReady = new ReactiveVar(true);
this.searching = new ReactiveVar(false); this.searching = new ReactiveVar(false);
this.hasResults = new ReactiveVar(false); this.hasResults = new ReactiveVar(false);
this.hasQueryErrors = new ReactiveVar(false);
this.query = new ReactiveVar(''); this.query = new ReactiveVar('');
this.resultsHeading = new ReactiveVar('');
this.searchLink = new ReactiveVar(null);
this.queryParams = null; this.queryParams = null;
this.resultsCount = new ReactiveVar(0); this.parsingErrors = [];
this.totalHits = new ReactiveVar(0); this.resultsCount = 0;
this.queryErrors = new ReactiveVar(null); this.totalHits = 0;
this.queryErrors = null;
Meteor.subscribe('setting'); Meteor.subscribe('setting');
if (Session.get('globalQuery')) {
this.searchAllBoards(Session.get('globalQuery'));
}
},
resetSearch() {
this.searching.set(false);
this.hasResults.set(false);
this.hasQueryErrors.set(false);
this.resultsHeading.set('');
this.parsingErrors = [];
this.resultsCount = 0;
this.totalHits = 0;
this.queryErrors = null;
}, },
results() { results() {
// eslint-disable-next-line no-console
// console.log('getting results');
if (this.queryParams) { if (this.queryParams) {
const results = Cards.globalSearch(this.queryParams); const results = Cards.globalSearch(this.queryParams);
this.queryErrors = results.errors;
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
// console.log('user:', Meteor.user()); // console.log('errors:', this.queryErrors);
// eslint-disable-next-line no-console if (this.errorMessages().length) {
// console.log('user:', Meteor.user().sessionData); this.hasQueryErrors.set(true);
// console.log('errors:', results.errors); return null;
this.totalHits.set(Meteor.user().sessionData.totalHits); }
this.resultsCount.set(results.cards.count());
this.queryErrors.set(results.errors); if (results.cards) {
const sessionData = SessionData.findOne({ userId: Meteor.userId() });
this.totalHits = sessionData.totalHits;
this.resultsCount = results.cards.count();
this.resultsHeading.set(this.getResultsHeading());
return results.cards; return results.cards;
} }
this.resultsCount.set(0); }
this.resultsCount = 0;
return []; return [];
}, },
errorMessages() { errorMessages() {
const errors = this.queryErrors.get();
const messages = []; const messages = [];
errors.notFound.boards.forEach(board => { if (this.queryErrors) {
this.queryErrors.notFound.boards.forEach(board => {
messages.push({ tag: 'board-title-not-found', value: board }); messages.push({ tag: 'board-title-not-found', value: board });
}); });
errors.notFound.swimlanes.forEach(swim => { this.queryErrors.notFound.swimlanes.forEach(swim => {
messages.push({ tag: 'swimlane-title-not-found', value: swim }); messages.push({ tag: 'swimlane-title-not-found', value: swim });
}); });
errors.notFound.lists.forEach(list => { this.queryErrors.notFound.lists.forEach(list => {
messages.push({ tag: 'list-title-not-found', value: list }); messages.push({ tag: 'list-title-not-found', value: list });
}); });
errors.notFound.users.forEach(user => { this.queryErrors.notFound.labels.forEach(label => {
messages.push({ tag: 'label-not-found', value: label });
});
this.queryErrors.notFound.users.forEach(user => {
messages.push({ tag: 'user-username-not-found', value: user }); messages.push({ tag: 'user-username-not-found', value: user });
}); });
this.queryErrors.notFound.members.forEach(user => {
messages.push({ tag: 'user-username-not-found', value: user });
});
this.queryErrors.notFound.assignees.forEach(user => {
messages.push({ tag: 'user-username-not-found', value: user });
});
}
if (this.parsingErrors.length) {
this.parsingErrors.forEach(err => {
messages.push(err);
});
}
return messages; return messages;
}, },
events() { searchAllBoards(query) {
return [ this.query.set(query);
{
'submit .js-search-query-form'(evt) {
evt.preventDefault();
this.query.set(evt.target.searchQuery.value);
this.queryErrors.set(null);
if (!this.query.get()) { this.resetSearch();
this.searching.set(false);
this.hasResults.set(false); if (!query) {
return; return;
} }
this.searching.set(true); this.searching.set(true);
this.hasResults.set(false);
let query = this.query.get();
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
// console.log('query:', query); // console.log('query:', query);
@ -121,6 +155,10 @@ BlazeComponent.extendComponent({
operatorMap[TAPi18n.__('operator-label-abbrev')] = 'labels'; operatorMap[TAPi18n.__('operator-label-abbrev')] = 'labels';
operatorMap[TAPi18n.__('operator-user')] = 'users'; operatorMap[TAPi18n.__('operator-user')] = 'users';
operatorMap[TAPi18n.__('operator-user-abbrev')] = 'users'; operatorMap[TAPi18n.__('operator-user-abbrev')] = 'users';
operatorMap[TAPi18n.__('operator-member')] = 'members';
operatorMap[TAPi18n.__('operator-member-abbrev')] = 'members';
operatorMap[TAPi18n.__('operator-assignee')] = 'assignees';
operatorMap[TAPi18n.__('operator-assignee-abbrev')] = 'assignees';
operatorMap[TAPi18n.__('operator-is')] = 'is'; operatorMap[TAPi18n.__('operator-is')] = 'is';
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -130,6 +168,8 @@ BlazeComponent.extendComponent({
swimlanes: [], swimlanes: [],
lists: [], lists: [],
users: [], users: [],
members: [],
assignees: [],
labels: [], labels: [],
is: [], is: [],
}; };
@ -154,6 +194,11 @@ BlazeComponent.extendComponent({
} }
if (op in operatorMap) { if (op in operatorMap) {
params[operatorMap[op]].push(m.groups.value); params[operatorMap[op]].push(m.groups.value);
} else {
this.parsingErrors.push({
tag: 'operator-unknown-error',
value: op,
});
} }
continue; continue;
} }
@ -195,6 +240,97 @@ BlazeComponent.extendComponent({
}); });
}); });
}, },
getResultsHeading() {
if (this.resultsCount === 0) {
return TAPi18n.__('no-cards-found');
} else if (this.resultsCount === 1) {
return TAPi18n.__('one-card-found');
} else if (this.resultsCount === this.totalHits) {
return TAPi18n.__('n-cards-found', this.resultsCount);
}
return TAPi18n.__('n-n-of-n-cards-found', {
start: 1,
end: this.resultsCount,
total: this.totalHits,
});
},
getSearchHref() {
const baseUrl = window.location.href.replace(/([?#].*$|\s*$)/, '');
return `${baseUrl}?q=${encodeURIComponent(this.query.get())}`;
},
searchInstructions() {
tags = {
operator_board: TAPi18n.__('operator-board'),
operator_list: TAPi18n.__('operator-list'),
operator_swimlane: TAPi18n.__('operator-swimlane'),
operator_label: TAPi18n.__('operator-label'),
operator_label_abbrev: TAPi18n.__('operator-label-abbrev'),
operator_user: TAPi18n.__('operator-user'),
operator_user_abbrev: TAPi18n.__('operator-user-abbrev'),
operator_member: TAPi18n.__('operator-member'),
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
operator_assignee: TAPi18n.__('operator-assignee'),
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
};
text = `# ${TAPi18n.__('globalSearch-instructions-heading')}`;
text += `\n${TAPi18n.__('globalSearch-instructions-description', tags)}`;
text += `\n${TAPi18n.__('globalSearch-instructions-operators', tags)}`;
text += `\n* ${TAPi18n.__(
'globalSearch-instructions-operator-board',
tags,
)}`;
text += `\n* ${TAPi18n.__(
'globalSearch-instructions-operator-list',
tags,
)}`;
text += `\n* ${TAPi18n.__(
'globalSearch-instructions-operator-swimlane',
tags,
)}`;
text += `\n* ${TAPi18n.__(
'globalSearch-instructions-operator-label',
tags,
)}`;
text += `\n* ${TAPi18n.__(
'globalSearch-instructions-operator-hash',
tags,
)}`;
text += `\n* ${TAPi18n.__(
'globalSearch-instructions-operator-user',
tags,
)}`;
text += `\n* ${TAPi18n.__('globalSearch-instructions-operator-at', tags)}`;
text += `\n* ${TAPi18n.__(
'globalSearch-instructions-operator-member',
tags,
)}`;
text += `\n* ${TAPi18n.__(
'globalSearch-instructions-operator-assignee',
tags,
)}`;
text += `\n## ${TAPi18n.__('heading-notes')}`;
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-1', tags)}`;
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-2', tags)}`;
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-3', tags)}`;
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-4', tags)}`;
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-5', tags)}`;
return text;
},
events() {
return [
{
'submit .js-search-query-form'(evt) {
evt.preventDefault();
this.searchAllBoards(evt.target.searchQuery.value);
},
}, },
]; ];
}, },

View file

@ -51,7 +51,7 @@
margin-top: 0 margin-top: 0
margin-bottom: 10px margin-bottom: 10px
.global-search-dueat-list-wrapper .global-search-results-list-wrapper
max-width: 500px max-width: 500px
margin-right: auto margin-right: auto
margin-left: auto margin-left: auto
@ -91,7 +91,7 @@
font-style: italic font-style: italic
code code
color: white color: black
background-color: grey background-color: lightgrey
padding: 0.1rem !important padding: 0.1rem !important
font-size: 0.7rem !important font-size: 0.7rem !important

View file

@ -158,7 +158,14 @@ FlowRouter.route('/global-search', {
Utils.manageCustomUI(); Utils.manageCustomUI();
Utils.manageMatomo(); Utils.manageMatomo();
DocHead.setTitle(TAPi18n.__('globalSearch-title'));
if (FlowRouter.getQueryParam('q')) {
Session.set(
'globalQuery',
decodeURIComponent(FlowRouter.getQueryParam('q')),
);
}
BlazeLayout.render('defaultLayout', { BlazeLayout.render('defaultLayout', {
headerBar: 'globalSearchHeaderBar', headerBar: 'globalSearchHeaderBar',
content: 'globalSearch', content: 'globalSearch',

View file

@ -868,12 +868,13 @@
"board-title-not-found": "Board '%s' not found.", "board-title-not-found": "Board '%s' not found.",
"swimlane-title-not-found": "Swimlane '%s' not found.", "swimlane-title-not-found": "Swimlane '%s' not found.",
"list-title-not-found": "List '%s' not found.", "list-title-not-found": "List '%s' not found.",
"label-not-found": "Label '%s' not found.",
"user-username-not-found": "Username '%s' not found.", "user-username-not-found": "Username '%s' not found.",
"globalSearch-title": "Search All Boards", "globalSearch-title": "Search All Boards",
"no-cards-found": "No Cards Found", "no-cards-found": "No Cards Found",
"one-card-found": "One Card Found", "one-card-found": "One Card Found",
"n-cards-found": "%s Cards Found", "n-cards-found": "%s Cards Found",
"n-n-of-n-cards-found": "%s-%s of %s Cards Found", "n-n-of-n-cards-found": "__start__-__end__ of __total__ Cards Found",
"operator-board": "board", "operator-board": "board",
"operator-board-abbrev": "b", "operator-board-abbrev": "b",
"operator-swimlane": "swimlane", "operator-swimlane": "swimlane",
@ -884,5 +885,29 @@
"operator-label-abbrev": "#", "operator-label-abbrev": "#",
"operator-user": "user", "operator-user": "user",
"operator-user-abbrev": "@", "operator-user-abbrev": "@",
"operator-is": "is" "operator-member": "member",
"operator-member-abbrev": "m",
"operator-assignee": "assignee",
"operator-assignee-abbrev": "a",
"operator-is": "is",
"operator-unknown-error": "%s is not an operator",
"heading-notes": "Notes",
"globalSearch-instructions-heading": "Search Instructions",
"globalSearch-instructions-description": "Searches can include operators to refine the search. Operators are specified by writing the operator name and value separated by a colon. For example, an operator specification of `list:Blocked` would limit the search to cards that are contained in a list named *Blocked*. If the value contains spaces or special characters it must be enclosed in quotation marks (e.g. `__operator_list__:\"To Review\"`).",
"globalSearch-instructions-operators": "Available operators:",
"globalSearch-instructions-operator-board": "`__operator_board__:title` - cards in boards matching the specified title",
"globalSearch-instructions-operator-list": "`__operator_list__:title` - cards in lists matching the specified title",
"globalSearch-instructions-operator-swimlane": "`__operator_swimlane__:title` - cards in swimlanes matching the specified title",
"globalSearch-instructions-operator-label": "`__operator_label__:color` `__operator_label__:name` - cards that have a label matching the given color or name",
"globalSearch-instructions-operator-hash": "`__operator_label_abbrev__label` - shorthand for `__operator_label__:label`",
"globalSearch-instructions-operator-user": "`__operator_user__:username` - cards where the specified user is a *member* or *assignee*",
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - shorthand for `user:username`",
"globalSearch-instructions-operator-member": "`__operator_member__:username` - cards where the specified user is a *member*",
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:username` - cards where the specified user is an *assignee*",
"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*.",
"globalSearch-instructions-notes-3": "Differing operators are *AND*ed together. Only cards that match all of the differing operators are returned.\n`__operator_list__:Available __operator_label__:red` returns only cards in the list *Available* with a *red* label.",
"globalSearch-instructions-notes-4": "Text searches are case insensitive.",
"globalSearch-instructions-notes-5": "Currently archived cards are not searched.",
"link-to-search": "Link to this search"
} }

View file

@ -1866,16 +1866,29 @@ Cards.globalSearch = queryParams => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
// console.log('userId:', userId); // console.log('userId:', userId);
const errors = { const errors = new (class {
notFound: { constructor() {
this.notFound = {
boards: [], boards: [],
swimlanes: [], swimlanes: [],
lists: [], lists: [],
labels: [], labels: [],
users: [], users: [],
members: [],
assignees: [],
is: [], is: [],
},
}; };
}
hasErrors() {
for (const prop in this.notFound) {
if (this.notFound[prop].length) {
return true;
}
}
return false;
}
})();
const selector = { const selector = {
archived: false, archived: false,
@ -1939,25 +1952,63 @@ Cards.globalSearch = queryParams => {
selector.listId.$in = queryLists; selector.listId.$in = queryLists;
} }
const queryMembers = [];
const queryAssignees = [];
if (queryParams.users.length) { if (queryParams.users.length) {
const queryUsers = [];
queryParams.users.forEach(query => { queryParams.users.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 => {
queryUsers.push(user._id); queryMembers.push(user._id);
queryAssignees.push(user._id);
}); });
} else { } else {
errors.notFound.users.push(query); errors.notFound.users.push(query);
} }
}); });
}
if (queryParams.members.length) {
queryParams.members.forEach(query => {
const users = Users.find({
username: query,
});
if (users.count()) {
users.forEach(user => {
queryMembers.push(user._id);
});
} else {
errors.notFound.members.push(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);
});
} else {
errors.notFound.assignees.push(query);
}
});
}
if (queryMembers.length && queryAssignees.length) {
selector.$or = [ selector.$or = [
{ members: { $in: queryUsers } }, { members: { $in: queryMembers } },
{ assignees: { $in: queryUsers } }, { assignees: { $in: queryAssignees } },
]; ];
} else if (queryMembers.length) {
selector.members = { $in: queryMembers };
} else if (queryAssignees.length) {
selector.assignees = { $in: queryAssignees };
} }
if (queryParams.labels.length) { if (queryParams.labels.length) {
@ -2003,7 +2054,7 @@ Cards.globalSearch = queryParams => {
}); });
}); });
} else { } else {
errors.notFound.labels.push({ tag: 'label', value: label }); errors.notFound.labels.push(label);
} }
} }
@ -2011,6 +2062,10 @@ Cards.globalSearch = queryParams => {
}); });
} }
if (errors.hasErrors()) {
return { cards: null, errors };
}
if (queryParams.text) { if (queryParams.text) {
const regex = new RegExp(queryParams.text, 'i'); const regex = new RegExp(queryParams.text, 'i');
@ -2045,14 +2100,6 @@ Cards.globalSearch = queryParams => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
// console.log('count:', cards.count()); // console.log('count:', cards.count());
if (Meteor.isServer) {
Users.update(userId, {
$set: {
'sessionData.totalHits': cards.count(),
'sessionData.lastHit': cards.count() > 50 ? 50 : cards.count(),
},
});
}
return { cards, errors }; return { cards, errors };
}; };

View file

@ -377,6 +377,14 @@ Users.initEasySearch(searchInFields, {
returnFields: [...searchInFields, 'profile.avatarUrl'], returnFields: [...searchInFields, 'profile.avatarUrl'],
}); });
Users.safeFields = {
_id: 1,
username: 1,
'profile.fullname': 1,
'profile.avatarUrl': 1,
'profile.initials': 1,
};
if (Meteor.isClient) { if (Meteor.isClient) {
Users.helpers({ Users.helpers({
isBoardMember() { isBoardMember() {

73
models/usersessiondata.js Normal file
View file

@ -0,0 +1,73 @@
SessionData = new Mongo.Collection('sessiondata');
/**
* A UserSessionData in Wekan. Organization in Trello.
*/
SessionData.attachSchema(
new SimpleSchema({
_id: {
/**
* the organization id
*/
type: Number,
optional: true,
// eslint-disable-next-line consistent-return
autoValue() {
if (this.isInsert && !this.isSet) {
return incrementCounter('counters', 'orgId', 1);
}
},
},
userId: {
/**
* userId of the user
*/
type: String,
optional: false,
},
totalHits: {
/**
* total number of hits in the last report query
*/
type: Number,
optional: true,
},
lastHit: {
/**
* the last hit returned from a report query
*/
type: Number,
optional: true,
},
createdAt: {
/**
* creation date of the team
*/
type: Date,
// eslint-disable-next-line consistent-return
autoValue() {
if (this.isInsert) {
return new Date();
} else if (this.isUpsert) {
return { $setOnInsert: new Date() };
} else {
this.unset();
}
},
},
modifiedAt: {
type: Date,
denyUpdate: false,
// eslint-disable-next-line consistent-return
autoValue() {
if (this.isInsert || this.isUpsert || this.isUpdate) {
return new Date();
} else {
this.unset();
}
},
},
}),
);
export default SessionData;

View file

@ -72,18 +72,7 @@ Meteor.publish('myCards', function() {
Boards.find({ _id: { $in: boards } }), Boards.find({ _id: { $in: boards } }),
Swimlanes.find({ _id: { $in: swimlanes } }), Swimlanes.find({ _id: { $in: swimlanes } }),
Lists.find({ _id: { $in: lists } }), Lists.find({ _id: { $in: lists } }),
Users.find( Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
{ _id: { $in: users } },
{
fields: {
_id: 1,
username: 1,
'profile.fullname': 1,
'profile.avatarUrl': 1,
'profile.initials': 1,
},
},
),
]; ];
}); });
@ -93,18 +82,7 @@ Meteor.publish('dueCards', function(allUsers = false) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
// console.log('all users:', allUsers); // console.log('all users:', allUsers);
const user = Users.findOne( const user = Users.findOne({ _id: this.userId });
{ _id: this.userId },
{
fields: {
_id: 1,
username: 1,
'profile.fullname': 1,
'profile.avatarUrl': 1,
'profile.initials': 1,
},
},
);
const archivedBoards = []; const archivedBoards = [];
Boards.find({ archived: true }).forEach(board => { Boards.find({ archived: true }).forEach(board => {
@ -115,14 +93,12 @@ Meteor.publish('dueCards', function(allUsers = false) {
let selector = { let selector = {
archived: false, archived: false,
}; };
// for admins and users, allow her to see cards only from boards where
// she is a member
//if (!user.isAdmin) {
selector.$or = [ selector.$or = [
{ permission: 'public' }, { permission: 'public' },
{ members: { $elemMatch: { userId: user._id, isActive: true } } }, { members: { $elemMatch: { userId: user._id, isActive: true } } },
]; ];
//}
Boards.find(selector).forEach(board => { Boards.find(selector).forEach(board => {
permiitedBoards.push(board._id); permiitedBoards.push(board._id);
}); });
@ -193,18 +169,7 @@ Meteor.publish('dueCards', function(allUsers = false) {
Boards.find({ _id: { $in: boards } }), Boards.find({ _id: { $in: boards } }),
Swimlanes.find({ _id: { $in: swimlanes } }), Swimlanes.find({ _id: { $in: swimlanes } }),
Lists.find({ _id: { $in: lists } }), Lists.find({ _id: { $in: lists } }),
Users.find( Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
{ _id: { $in: users } },
{
fields: {
_id: 1,
username: 1,
'profile.fullname': 1,
'profile.avatarUrl': 1,
'profile.initials': 1,
},
},
),
]; ];
}); });
@ -216,6 +181,20 @@ Meteor.publish('globalSearch', function(queryParams) {
const cards = Cards.globalSearch(queryParams).cards; const cards = Cards.globalSearch(queryParams).cards;
if (!cards) {
return [];
}
SessionData.upsert(
{ userId: this.userId },
{
$set: {
totalHits: cards.count(),
lastHit: cards.count() > 50 ? 50 : cards.count(),
},
},
);
const boards = []; const boards = [];
const swimlanes = []; const swimlanes = [];
const lists = []; const lists = [];
@ -244,34 +223,21 @@ Meteor.publish('globalSearch', function(queryParams) {
Boards.find({ _id: { $in: boards } }), Boards.find({ _id: { $in: boards } }),
Swimlanes.find({ _id: { $in: swimlanes } }), Swimlanes.find({ _id: { $in: swimlanes } }),
Lists.find({ _id: { $in: lists } }), Lists.find({ _id: { $in: lists } }),
Users.find({ _id: { $in: users } }), Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
SessionData.find({ userId: this.userId }),
]; ];
}); });
Meteor.publish('brokenCards', function() { Meteor.publish('brokenCards', function() {
const user = Users.findOne( const user = Users.findOne({ _id: this.userId });
{ _id: this.userId },
{
fields: {
_id: 1,
username: 1,
'profile.fullname': 1,
'profile.avatarUrl': 1,
'profile.initials': 1,
},
},
);
const permiitedBoards = [null]; const permiitedBoards = [null];
let selector = {}; let selector = {};
// for admins and users, if user is not an admin allow her to see cards only from boards where
// she is a member
//if (!user.isAdmin) {
selector.$or = [ selector.$or = [
{ permission: 'public' }, { permission: 'public' },
{ members: { $elemMatch: { userId: user._id, isActive: true } } }, { members: { $elemMatch: { userId: user._id, isActive: true } } },
]; ];
//}
Boards.find(selector).forEach(board => { Boards.find(selector).forEach(board => {
permiitedBoards.push(board._id); permiitedBoards.push(board._id);
}); });
@ -328,17 +294,6 @@ Meteor.publish('brokenCards', function() {
Boards.find({ _id: { $in: boards } }), Boards.find({ _id: { $in: boards } }),
Swimlanes.find({ _id: { $in: swimlanes } }), Swimlanes.find({ _id: { $in: swimlanes } }),
Lists.find({ _id: { $in: lists } }), Lists.find({ _id: { $in: lists } }),
Users.find( Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
{ _id: { $in: users } },
{
fields: {
_id: 1,
username: 1,
'profile.fullname': 1,
'profile.avatarUrl': 1,
'profile.initials': 1,
},
},
),
]; ];
}); });