mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
Add support for searching archived cards
* Add logic to search for archived or all cards * Add icons to board, swimlane and list titles to indicate if they are archived * Update search instructions
This commit is contained in:
parent
a9ceba690e
commit
6a32424a08
6 changed files with 198 additions and 77 deletions
|
|
@ -5,19 +5,28 @@ template(name="resultCard")
|
||||||
//= card.title
|
//= card.title
|
||||||
ul.result-card-context-list
|
ul.result-card-context-list
|
||||||
li.result-card-context(title="{{_ 'board'}}")
|
li.result-card-context(title="{{_ 'board'}}")
|
||||||
+viewer
|
.result-card-block-wrapper
|
||||||
= getBoard.title
|
+viewer
|
||||||
|
= getBoard.title
|
||||||
|
if getBoard.archived
|
||||||
|
i.fa.fa-archive
|
||||||
li.result-card-context.result-card-context-separator
|
li.result-card-context.result-card-context-separator
|
||||||
= ' '
|
= ' '
|
||||||
| {{_ 'context-separator'}}
|
| {{_ 'context-separator'}}
|
||||||
= ' '
|
= ' '
|
||||||
li.result-card-context(title="{{_ 'swimlane'}}")
|
li.result-card-context(title="{{_ 'swimlane'}}")
|
||||||
+viewer
|
.result-card-block-wrapper
|
||||||
= getSwimlane.title
|
+viewer
|
||||||
|
= getSwimlane.title
|
||||||
|
if getSwimlane.archived
|
||||||
|
i.fa.fa-archive
|
||||||
li.result-card-context.result-card-context-separator
|
li.result-card-context.result-card-context-separator
|
||||||
= ' '
|
= ' '
|
||||||
| {{_ 'context-separator'}}
|
| {{_ 'context-separator'}}
|
||||||
= ' '
|
= ' '
|
||||||
li.result-card-context(title="{{_ 'list'}}")
|
li.result-card-context(title="{{_ 'list'}}")
|
||||||
+viewer
|
.result-card-block-wrapper
|
||||||
= getList.title
|
+viewer
|
||||||
|
= getList.title
|
||||||
|
if getList.archived
|
||||||
|
i.fa.fa-archive
|
||||||
|
|
|
||||||
|
|
@ -177,8 +177,8 @@ BlazeComponent.extendComponent({
|
||||||
|
|
||||||
this.searching.set(true);
|
this.searching.set(true);
|
||||||
|
|
||||||
const reOperator1 = /^((?<operator>[\w\p{L}]+):|(?<abbrev>[#@]))(?<value>[\w\p{L}]+)(\s+|$)/iu;
|
const reOperator1 = /^((?<operator>[\p{Letter}\p{Mark}]+):|(?<abbrev>[#@]))(?<value>[\p{Letter}\p{Mark}]+)(\s+|$)/iu;
|
||||||
const reOperator2 = /^((?<operator>[\w\p{L}]+):|(?<abbrev>[#@]))(?<quote>["']*)(?<value>.*?)\k<quote>(\s+|$)/iu;
|
const reOperator2 = /^((?<operator>[\p{Letter}\p{Mark}]+):|(?<abbrev>[#@]))(?<quote>["']*)(?<value>.*?)\k<quote>(\s+|$)/iu;
|
||||||
const reText = /^(?<text>\S+)(\s+|$)/u;
|
const reText = /^(?<text>\S+)(\s+|$)/u;
|
||||||
const reQuotedText = /^(?<quote>["'])(?<text>[\w\p{L}]+)\k<quote>(\s+|$)/u;
|
const reQuotedText = /^(?<quote>["'])(?<text>[\w\p{L}]+)\k<quote>(\s+|$)/u;
|
||||||
|
|
||||||
|
|
@ -197,7 +197,7 @@ BlazeComponent.extendComponent({
|
||||||
'operator-member-abbrev': 'members',
|
'operator-member-abbrev': 'members',
|
||||||
'operator-assignee': 'assignees',
|
'operator-assignee': 'assignees',
|
||||||
'operator-assignee-abbrev': 'assignees',
|
'operator-assignee-abbrev': 'assignees',
|
||||||
'operator-is': 'is',
|
'operator-status': 'status',
|
||||||
'operator-due': 'dueAt',
|
'operator-due': 'dueAt',
|
||||||
'operator-created': 'createdAt',
|
'operator-created': 'createdAt',
|
||||||
'operator-modified': 'modifiedAt',
|
'operator-modified': 'modifiedAt',
|
||||||
|
|
@ -207,31 +207,33 @@ BlazeComponent.extendComponent({
|
||||||
const predicates = {
|
const predicates = {
|
||||||
due: {
|
due: {
|
||||||
'predicate-overdue': 'overdue',
|
'predicate-overdue': 'overdue',
|
||||||
'predicate-day': 'day',
|
},
|
||||||
|
durations: {
|
||||||
'predicate-week': 'week',
|
'predicate-week': 'week',
|
||||||
'predicate-month': 'month',
|
'predicate-month': 'month',
|
||||||
'predicate-quarter': 'quarter',
|
'predicate-quarter': 'quarter',
|
||||||
'predicate-year': 'year',
|
'predicate-year': 'year',
|
||||||
},
|
},
|
||||||
date: {
|
status: {
|
||||||
'predicate-day': 'day',
|
|
||||||
'predicate-week': 'week',
|
|
||||||
'predicate-month': 'month',
|
|
||||||
'predicate-quarter': 'quarter',
|
|
||||||
'predicate-year': 'year',
|
|
||||||
},
|
|
||||||
is: {
|
|
||||||
'predicate-archived': 'archived',
|
'predicate-archived': 'archived',
|
||||||
'predicate-active': 'active',
|
'predicate-all': 'all',
|
||||||
|
'predicate-ended': 'ended',
|
||||||
|
},
|
||||||
|
sorts: {
|
||||||
|
'predicate-due': 'dueAt',
|
||||||
|
'predicate-created': 'createdAt',
|
||||||
|
'predicate-modified': 'modifiedAt',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const predicateTranslations = {};
|
const predicateTranslations = {};
|
||||||
Object.entries(predicates, ([category, predicates]) => {
|
Object.entries(predicates).forEach(([category, catPreds]) => {
|
||||||
predicateTranslations[category] = {};
|
predicateTranslations[category] = {};
|
||||||
Object.entries(predicates, ([tag, value]) => {
|
Object.entries(catPreds).forEach(([tag, value]) => {
|
||||||
predicateTranslations[category][TAPi18n.__(tag)] = value;
|
predicateTranslations[category][TAPi18n.__(tag)] = value;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
// console.log('predicateTranslations:', predicateTranslations);
|
||||||
|
|
||||||
const operatorMap = {};
|
const operatorMap = {};
|
||||||
Object.entries(operators).forEach(([key, value]) => {
|
Object.entries(operators).forEach(([key, value]) => {
|
||||||
|
|
@ -248,7 +250,7 @@ BlazeComponent.extendComponent({
|
||||||
members: [],
|
members: [],
|
||||||
assignees: [],
|
assignees: [],
|
||||||
labels: [],
|
labels: [],
|
||||||
is: [],
|
status: [],
|
||||||
dueAt: null,
|
dueAt: null,
|
||||||
createdAt: null,
|
createdAt: null,
|
||||||
modifiedAt: null,
|
modifiedAt: null,
|
||||||
|
|
@ -285,8 +287,8 @@ BlazeComponent.extendComponent({
|
||||||
let days = parseInt(value, 10);
|
let days = parseInt(value, 10);
|
||||||
let duration = null;
|
let duration = null;
|
||||||
if (isNaN(days)) {
|
if (isNaN(days)) {
|
||||||
if (predicateTranslations.date.keys().includes(value)) {
|
if (predicateTranslations.durations[value]) {
|
||||||
duration = predicateTranslations.date[value];
|
duration = predicateTranslations.durations[value];
|
||||||
value = moment();
|
value = moment();
|
||||||
} else if (predicateTranslations.due[value] === 'overdue') {
|
} else if (predicateTranslations.due[value] === 'overdue') {
|
||||||
value = moment();
|
value = moment();
|
||||||
|
|
@ -312,11 +314,22 @@ BlazeComponent.extendComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (operatorMap[op] === 'sort') {
|
} else if (operatorMap[op] === 'sort') {
|
||||||
if (!['due', 'modified', 'created', 'system'].includes(value)) {
|
if (!predicateTranslations.sorts[value]) {
|
||||||
this.parsingErrors.push({
|
this.parsingErrors.push({
|
||||||
tag: 'operator-sort-invalid',
|
tag: 'operator-sort-invalid',
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
value = predicateTranslations.sorts[value];
|
||||||
|
}
|
||||||
|
} else if (operatorMap[op] === 'status') {
|
||||||
|
if (!predicateTranslations.status[value]) {
|
||||||
|
this.parsingErrors.push({
|
||||||
|
tag: 'operator-status-invalid',
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
value = predicateTranslations.status[value];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Array.isArray(params[operatorMap[op]])) {
|
if (Array.isArray(params[operatorMap[op]])) {
|
||||||
|
|
@ -359,12 +372,13 @@ BlazeComponent.extendComponent({
|
||||||
if (this.parsingErrors.length) {
|
if (this.parsingErrors.length) {
|
||||||
this.searching.set(false);
|
this.searching.set(false);
|
||||||
this.queryErrors = this.parsingErrorMessages();
|
this.queryErrors = this.parsingErrorMessages();
|
||||||
|
this.hasResults.set(true);
|
||||||
this.hasQueryErrors.set(true);
|
this.hasQueryErrors.set(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.autorun(() => {
|
this.autorun(() => {
|
||||||
const handle = subManager.subscribe(
|
const handle = Meteor.subscribe(
|
||||||
'globalSearch',
|
'globalSearch',
|
||||||
SessionData.getSessionId(),
|
SessionData.getSessionId(),
|
||||||
params,
|
params,
|
||||||
|
|
@ -407,6 +421,7 @@ BlazeComponent.extendComponent({
|
||||||
operator_board: TAPi18n.__('operator-board'),
|
operator_board: TAPi18n.__('operator-board'),
|
||||||
operator_list: TAPi18n.__('operator-list'),
|
operator_list: TAPi18n.__('operator-list'),
|
||||||
operator_swimlane: TAPi18n.__('operator-swimlane'),
|
operator_swimlane: TAPi18n.__('operator-swimlane'),
|
||||||
|
operator_comment: TAPi18n.__('operator-comment'),
|
||||||
operator_label: TAPi18n.__('operator-label'),
|
operator_label: TAPi18n.__('operator-label'),
|
||||||
operator_label_abbrev: TAPi18n.__('operator-label-abbrev'),
|
operator_label_abbrev: TAPi18n.__('operator-label-abbrev'),
|
||||||
operator_user: TAPi18n.__('operator-user'),
|
operator_user: TAPi18n.__('operator-user'),
|
||||||
|
|
@ -415,6 +430,18 @@ BlazeComponent.extendComponent({
|
||||||
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
|
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
|
||||||
operator_assignee: TAPi18n.__('operator-assignee'),
|
operator_assignee: TAPi18n.__('operator-assignee'),
|
||||||
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
|
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
|
||||||
|
operator_due: TAPi18n.__('operator-due'),
|
||||||
|
operator_created: TAPi18n.__('operator-created'),
|
||||||
|
operator_modified: TAPi18n.__('operator-modified'),
|
||||||
|
operator_status: TAPi18n.__('operator-status'),
|
||||||
|
predicate_overdue: TAPi18n.__('predicate-overdue'),
|
||||||
|
predicate_archived: TAPi18n.__('predicate-archived'),
|
||||||
|
predicate_all: TAPi18n.__('predicate-all'),
|
||||||
|
predicate_ended: TAPi18n.__('predicate-ended'),
|
||||||
|
predicate_week: TAPi18n.__('predicate-week'),
|
||||||
|
predicate_month: TAPi18n.__('predicate-month'),
|
||||||
|
predicate_quarter: TAPi18n.__('predicate-quarter'),
|
||||||
|
predicate_year: TAPi18n.__('predicate-year'),
|
||||||
};
|
};
|
||||||
|
|
||||||
text = `# ${TAPi18n.__('globalSearch-instructions-heading')}`;
|
text = `# ${TAPi18n.__('globalSearch-instructions-heading')}`;
|
||||||
|
|
@ -432,6 +459,10 @@ BlazeComponent.extendComponent({
|
||||||
'globalSearch-instructions-operator-swimlane',
|
'globalSearch-instructions-operator-swimlane',
|
||||||
tags,
|
tags,
|
||||||
)}`;
|
)}`;
|
||||||
|
text += `\n* ${TAPi18n.__(
|
||||||
|
'globalSearch-instructions-operator-comment',
|
||||||
|
tags,
|
||||||
|
)}`;
|
||||||
text += `\n* ${TAPi18n.__(
|
text += `\n* ${TAPi18n.__(
|
||||||
'globalSearch-instructions-operator-label',
|
'globalSearch-instructions-operator-label',
|
||||||
tags,
|
tags,
|
||||||
|
|
@ -453,11 +484,27 @@ BlazeComponent.extendComponent({
|
||||||
'globalSearch-instructions-operator-assignee',
|
'globalSearch-instructions-operator-assignee',
|
||||||
tags,
|
tags,
|
||||||
)}`;
|
)}`;
|
||||||
|
text += `\n* ${TAPi18n.__('globalSearch-instructions-operator-due', tags)}`;
|
||||||
|
text += `\n* ${TAPi18n.__(
|
||||||
|
'globalSearch-instructions-operator-created',
|
||||||
|
tags,
|
||||||
|
)}`;
|
||||||
|
text += `\n* ${TAPi18n.__(
|
||||||
|
'globalSearch-instructions-operator-modified',
|
||||||
|
tags,
|
||||||
|
)}`;
|
||||||
|
text += `\n* ${TAPi18n.__(
|
||||||
|
'globalSearch-instructions-status-archived',
|
||||||
|
tags,
|
||||||
|
)}`;
|
||||||
|
text += `\n* ${TAPi18n.__('globalSearch-instructions-status-all', tags)}`;
|
||||||
|
text += `\n* ${TAPi18n.__('globalSearch-instructions-status-ended', tags)}`;
|
||||||
|
|
||||||
text += `\n## ${TAPi18n.__('heading-notes')}`;
|
text += `\n## ${TAPi18n.__('heading-notes')}`;
|
||||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-1', tags)}`;
|
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-1', tags)}`;
|
||||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-2', tags)}`;
|
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-2', tags)}`;
|
||||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-3', tags)}`;
|
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-3', tags)}`;
|
||||||
|
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-3-2', tags)}`;
|
||||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-4', tags)}`;
|
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-4', tags)}`;
|
||||||
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-5', tags)}`;
|
text += `\n* ${TAPi18n.__('globalSearch-instructions-notes-5', tags)}`;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@
|
||||||
"board-view-swimlanes": "خطوط السباحة",
|
"board-view-swimlanes": "خطوط السباحة",
|
||||||
"board-view-collapse": "Collapse",
|
"board-view-collapse": "Collapse",
|
||||||
"board-view-gantt": "Gantt",
|
"board-view-gantt": "Gantt",
|
||||||
"board-view-lists": "القائمات",
|
"board-view-lists": "اللستات",
|
||||||
"bucket-example": "مثل « todo list » على سبيل المثال",
|
"bucket-example": "مثل « todo list » على سبيل المثال",
|
||||||
"cancel": "إلغاء",
|
"cancel": "إلغاء",
|
||||||
"card-archived": "البطاقة منقولة الى الارشيف",
|
"card-archived": "البطاقة منقولة الى الارشيف",
|
||||||
|
|
@ -420,7 +420,7 @@
|
||||||
"link-list": "رابط إلى هذه القائمة",
|
"link-list": "رابط إلى هذه القائمة",
|
||||||
"list-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the list. There is no undo.",
|
"list-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the list. There is no undo.",
|
||||||
"list-delete-suggest-archive": "You can move a list to Archive to remove it from the board and preserve the activity.",
|
"list-delete-suggest-archive": "You can move a list to Archive to remove it from the board and preserve the activity.",
|
||||||
"lists": "القائمات",
|
"lists": "استات",
|
||||||
"swimlanes": "خطوط السباحة",
|
"swimlanes": "خطوط السباحة",
|
||||||
"log-out": "تسجيل الخروج",
|
"log-out": "تسجيل الخروج",
|
||||||
"log-in": "تسجيل الدخول",
|
"log-in": "تسجيل الدخول",
|
||||||
|
|
@ -609,8 +609,8 @@
|
||||||
"accounts": "الحسابات",
|
"accounts": "الحسابات",
|
||||||
"accounts-allowEmailChange": "السماح بتغيير البريد الإلكتروني",
|
"accounts-allowEmailChange": "السماح بتغيير البريد الإلكتروني",
|
||||||
"accounts-allowUserNameChange": "Allow Username Change",
|
"accounts-allowUserNameChange": "Allow Username Change",
|
||||||
"createdAt": "Created at",
|
"createdAt": "تاريخ الإنشاء",
|
||||||
"modifiedAt": "Modified at",
|
"modifiedAt": "تاريخ التعديل",
|
||||||
"verified": "Verified",
|
"verified": "Verified",
|
||||||
"active": "نشط",
|
"active": "نشط",
|
||||||
"card-received": "Received",
|
"card-received": "Received",
|
||||||
|
|
@ -900,6 +900,17 @@
|
||||||
"operator-modified": "modified",
|
"operator-modified": "modified",
|
||||||
"operator-unknown-error": "%s is not an operator",
|
"operator-unknown-error": "%s is not an operator",
|
||||||
"operator-number-expected": "operator __operator__ expected a number, got '__value__'",
|
"operator-number-expected": "operator __operator__ expected a number, got '__value__'",
|
||||||
|
"predicate-archived": "مؤرشف",
|
||||||
|
"predicate-ended": "ended",
|
||||||
|
"predicate-all": "كله",
|
||||||
|
"predicate-overdue": "متاخر",
|
||||||
|
"predicate-week": "اسبوع",
|
||||||
|
"predicate-month": "شهر",
|
||||||
|
"predicate-quarter": "ربع",
|
||||||
|
"predicate-year": "سنة",
|
||||||
|
"predicate-due": "due",
|
||||||
|
"predicate-modified": "متعديل",
|
||||||
|
"predicate-created": "created",
|
||||||
"heading-notes": "ملاحظات",
|
"heading-notes": "ملاحظات",
|
||||||
"globalSearch-instructions-heading": "تعليمات البحث",
|
"globalSearch-instructions-heading": "تعليمات البحث",
|
||||||
"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-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\"`).",
|
||||||
|
|
|
||||||
|
|
@ -896,18 +896,27 @@
|
||||||
"operator-member-abbrev": "m",
|
"operator-member-abbrev": "m",
|
||||||
"operator-assignee": "assignee",
|
"operator-assignee": "assignee",
|
||||||
"operator-assignee-abbrev": "a",
|
"operator-assignee-abbrev": "a",
|
||||||
"operator-is": "is",
|
"operator-status": "status",
|
||||||
"operator-due": "due",
|
"operator-due": "due",
|
||||||
"operator-created": "created",
|
"operator-created": "created",
|
||||||
"operator-modified": "modified",
|
"operator-modified": "modified",
|
||||||
"operator-sort": "sort",
|
"operator-sort": "sort",
|
||||||
"operator-comment": "comment",
|
"operator-comment": "comment",
|
||||||
"predicate-archived": "archived",
|
"predicate-archived": "archived",
|
||||||
"predicate-active": "active",
|
"predicate-ended": "ended",
|
||||||
|
"predicate-all": "all",
|
||||||
"predicate-overdue": "overdue",
|
"predicate-overdue": "overdue",
|
||||||
|
"predicate-week": "week",
|
||||||
|
"predicate-month": "month",
|
||||||
|
"predicate-quarter": "quarter",
|
||||||
|
"predicate-year": "year",
|
||||||
|
"predicate-due": "due",
|
||||||
|
"predicate-modified": "modified",
|
||||||
|
"predicate-created": "created",
|
||||||
"operator-unknown-error": "%s is not an operator",
|
"operator-unknown-error": "%s is not an operator",
|
||||||
"operator-number-expected": "operator __operator__ expected a number, got '__value__'",
|
"operator-number-expected": "operator __operator__ expected a number, got '__value__'",
|
||||||
"operator-sort-invalid": "sort of '%s' is invalid",
|
"operator-sort-invalid": "sort of '%s' is invalid",
|
||||||
|
"operator-status-invalid": "'%s' is not a valid status",
|
||||||
"heading-notes": "Notes",
|
"heading-notes": "Notes",
|
||||||
"globalSearch-instructions-heading": "Search Instructions",
|
"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-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\"`).",
|
||||||
|
|
@ -922,12 +931,16 @@
|
||||||
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - shorthand for `user:username`",
|
"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-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-operator-assignee": "`__operator_assignee__:username` - cards where the specified user is an *assignee*",
|
||||||
"globalSearch-instructions-operator-due": "`__operator_due__:n` - cards which are due *n* days from now. `__operator_due__:__predicate_overdue__ lists all ",
|
"globalSearch-instructions-operator-due": "`__operator_due__:n` - cards which are due *n* days from now. `__operator_due__:__predicate_overdue__ lists all cards past their due date.",
|
||||||
"globalSearch-instructions-operator-created": "`__operator_created__:n` - cards which which were created *n* days ago",
|
"globalSearch-instructions-operator-created": "`__operator_created__:n` - cards which which were created *n* days ago",
|
||||||
"globalSearch-instructions-operator-modified": "`__operator_modified__:n` - cards which which were modified *n* days ago",
|
"globalSearch-instructions-operator-modified": "`__operator_modified__:n` - cards which which were modified *n* days ago",
|
||||||
|
"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-notes-1": "Multiple operators may be specified.",
|
"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-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-3": "Differing operators are *AND*ed together. Only cards that match all of the differing operators are returned. `__operator_list__:Available __operator_label__:red` returns only cards in the list *Available* with a *red* label.",
|
||||||
|
"globalSearch-instructions-notes-3-2": "Days can be specified as an integer or using `__predicate_week__`, `__predicate_month__`, `__predicate_quarter__` or `__predicate_year__`",
|
||||||
"globalSearch-instructions-notes-4": "Text searches are case insensitive.",
|
"globalSearch-instructions-notes-4": "Text searches are case insensitive.",
|
||||||
"globalSearch-instructions-notes-5": "Currently archived cards are not searched.",
|
"globalSearch-instructions-notes-5": "Currently archived cards are not searched.",
|
||||||
"link-to-search": "Link to this search",
|
"link-to-search": "Link to this search",
|
||||||
|
|
|
||||||
|
|
@ -1278,37 +1278,33 @@ Boards.userSearch = (
|
||||||
userId,
|
userId,
|
||||||
selector = {},
|
selector = {},
|
||||||
projection = {},
|
projection = {},
|
||||||
includeArchived = false,
|
// includeArchived = false,
|
||||||
) => {
|
) => {
|
||||||
if (!includeArchived) {
|
// if (!includeArchived) {
|
||||||
selector.archived = false;
|
// selector.archived = false;
|
||||||
}
|
// }
|
||||||
selector.$or = [
|
selector.$or = [{ permission: 'public' }];
|
||||||
{ permission: 'public' },
|
|
||||||
{ members: { $elemMatch: { userId, isActive: true } } },
|
|
||||||
];
|
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } });
|
||||||
|
}
|
||||||
return Boards.find(selector, projection);
|
return Boards.find(selector, projection);
|
||||||
};
|
};
|
||||||
|
|
||||||
Boards.userBoards = (userId, includeArchived = false, selector = {}) => {
|
Boards.userBoards = (userId, archived = false, selector = {}) => {
|
||||||
check(userId, String);
|
if (typeof archived === 'boolean') {
|
||||||
|
selector.archived = archived;
|
||||||
if (!includeArchived) {
|
|
||||||
selector = {
|
|
||||||
archived: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
selector.$or = [
|
selector.$or = [{ permission: 'public' }];
|
||||||
{ permission: 'public' },
|
|
||||||
{ members: { $elemMatch: { userId, isActive: true } } },
|
|
||||||
];
|
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } });
|
||||||
|
}
|
||||||
return Boards.find(selector);
|
return Boards.find(selector);
|
||||||
};
|
};
|
||||||
|
|
||||||
Boards.userBoardIds = (userId, includeArchived = false, selector = {}) => {
|
Boards.userBoardIds = (userId, archived = false, selector = {}) => {
|
||||||
return Boards.userBoards(userId, includeArchived, selector).map(board => {
|
return Boards.userBoards(userId, archived, selector).map(board => {
|
||||||
return board._id;
|
return board._id;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
||||||
users: [],
|
users: [],
|
||||||
members: [],
|
members: [],
|
||||||
assignees: [],
|
assignees: [],
|
||||||
is: [],
|
status: [],
|
||||||
comments: [],
|
comments: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -247,14 +247,51 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
let archived = false;
|
||||||
|
let endAt = null;
|
||||||
|
if (queryParams.status.length) {
|
||||||
|
queryParams.status.forEach(status => {
|
||||||
|
if (status === 'archived') {
|
||||||
|
archived = true;
|
||||||
|
} else if (status === 'all') {
|
||||||
|
archived = null;
|
||||||
|
} else if (status === 'ended') {
|
||||||
|
endAt = { $nin: [null, ''] };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
const selector = {
|
const selector = {
|
||||||
archived: false,
|
|
||||||
type: 'cardType-card',
|
type: 'cardType-card',
|
||||||
boardId: { $in: Boards.userBoardIds(userId) },
|
// boardId: { $in: Boards.userBoardIds(userId) },
|
||||||
swimlaneId: { $nin: Swimlanes.archivedSwimlaneIds() },
|
$and: [],
|
||||||
listId: { $nin: Lists.archivedListIds() },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const boardsSelector = {};
|
||||||
|
if (archived !== null) {
|
||||||
|
boardsSelector.archived = archived;
|
||||||
|
if (archived) {
|
||||||
|
selector.boardId = { $in: Boards.userBoardIds(userId, null) };
|
||||||
|
selector.$and.push({
|
||||||
|
$or: [
|
||||||
|
{ boardId: { $in: Boards.userBoardIds(userId, archived) } },
|
||||||
|
{ swimlaneId: { $in: Swimlanes.archivedSwimlaneIds() } },
|
||||||
|
{ listId: { $in: Lists.archivedListIds() } },
|
||||||
|
{ archived: true },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
selector.boardId = { $in: Boards.userBoardIds(userId, false) };
|
||||||
|
selector.swimlaneId = { $nin: Swimlanes.archivedSwimlaneIds() };
|
||||||
|
selector.listId = { $nin: Lists.archivedListIds() };
|
||||||
|
selector.archived = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selector.boardId = { $in: Boards.userBoardIds(userId, null) };
|
||||||
|
}
|
||||||
|
if (endAt !== null) {
|
||||||
|
selector.endAt = endAt;
|
||||||
|
}
|
||||||
|
|
||||||
if (queryParams.boards.length) {
|
if (queryParams.boards.length) {
|
||||||
const queryBoards = [];
|
const queryBoards = [];
|
||||||
queryParams.boards.forEach(query => {
|
queryParams.boards.forEach(query => {
|
||||||
|
|
@ -383,10 +420,12 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryMembers.length && queryAssignees.length) {
|
if (queryMembers.length && queryAssignees.length) {
|
||||||
selector.$or = [
|
selector.$and.push({
|
||||||
{ members: { $in: queryMembers } },
|
$or: [
|
||||||
{ assignees: { $in: queryAssignees } },
|
{ members: { $in: queryMembers } },
|
||||||
];
|
{ assignees: { $in: queryAssignees } },
|
||||||
|
],
|
||||||
|
});
|
||||||
} else if (queryMembers.length) {
|
} else if (queryMembers.length) {
|
||||||
selector.members = { $in: queryMembers };
|
selector.members = { $in: queryMembers };
|
||||||
} else if (queryAssignees.length) {
|
} else if (queryAssignees.length) {
|
||||||
|
|
@ -450,24 +489,30 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
||||||
if (queryParams.text) {
|
if (queryParams.text) {
|
||||||
const regex = new RegExp(escapeForRegex(queryParams.text), 'i');
|
const regex = new RegExp(escapeForRegex(queryParams.text), 'i');
|
||||||
|
|
||||||
selector.$or = [
|
selector.$and.push({
|
||||||
{ title: regex },
|
$or: [
|
||||||
{ description: regex },
|
{ title: regex },
|
||||||
{ customFields: { $elemMatch: { value: regex } } },
|
{ description: regex },
|
||||||
{
|
{ customFields: { $elemMatch: { value: regex } } },
|
||||||
_id: {
|
{
|
||||||
$in: CardComments.textSearch(userId, [queryParams.text]).map(
|
_id: {
|
||||||
com => com.cardId,
|
$in: CardComments.textSearch(userId, [queryParams.text]).map(
|
||||||
),
|
com => com.cardId,
|
||||||
|
),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
];
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selector.$and.length === 0) {
|
||||||
|
delete selector.$and;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
// console.log('selector:', selector);
|
console.log('selector:', selector);
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
// console.log('selector.$or:', selector.$or);
|
console.log('selector.$and:', selector.$and);
|
||||||
|
|
||||||
const projection = {
|
const projection = {
|
||||||
fields: {
|
fields: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue