Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
451acb1c30 | ||
|
|
d7c663819c | ||
|
|
63d933a51d | ||
|
|
0afe8abdb8 | ||
|
|
413f91d0c8 | ||
|
|
fbb98e23c3 | ||
|
|
c4ea7457dc | ||
|
|
d0083d37da | ||
|
|
6f18a75e5c | ||
|
|
6b8939c02e | ||
|
|
86e5356b0f | ||
|
|
c0f748bcb5 | ||
|
|
0acb8f5d69 | ||
|
|
37db870475 | ||
|
|
0d5f33299e | ||
|
|
78f818bbb0 | ||
|
|
edafb6a6d2 | ||
|
|
c0984aa740 | ||
|
|
6253bbdc0b | ||
|
|
254a9abad2 | ||
|
|
83e77f8d82 | ||
|
|
2726643586 | ||
|
|
b8f2a468d8 | ||
|
|
fb771d46dd | ||
|
|
ecd4c7fb02 | ||
|
|
da2be59185 | ||
|
|
93a3fa9c75 | ||
|
|
cf546c71e3 | ||
|
|
2791b7da22 | ||
|
|
48fc442b85 | ||
|
|
2e6b18dfa7 | ||
|
|
7f0da49e0a | ||
|
|
b5124d0f6a | ||
|
|
e70a1f0a60 | ||
|
|
7e8475e64d | ||
|
|
e161ada214 | ||
|
|
409b8559d7 | ||
|
|
d74dc92681 | ||
|
|
5f62d7307f | ||
|
|
3866ed3196 | ||
|
|
b039bad649 | ||
|
|
58a6354010 | ||
|
|
429d9474a9 | ||
|
|
be03d2ae9a | ||
|
|
5db40beb79 | ||
|
|
10f74f5152 | ||
|
|
0580237270 | ||
|
|
8059856c39 | ||
|
|
a3518a3bcf | ||
|
|
7b8d67de63 | ||
|
|
7a5aa469cd | ||
|
|
1e415b38d2 | ||
|
|
ff626fb559 |
No files matched your search
+1
-1
@@ -72,7 +72,7 @@ horka:swipebox@1.0.2
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.0.11
|
||||
htmljs@1.0.11
|
||||
http@1.4.2
|
||||
http@1.4.3
|
||||
id-map@1.1.0
|
||||
idmontie:migrations@1.0.3
|
||||
inter-process-messaging@0.1.1
|
||||
|
||||
@@ -1,3 +1,63 @@
|
||||
# v4.81 2021-01-18 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Global Search: Use translated color names](https://github.com/wekan/wekan/pull/3440).
|
||||
Thanks to jrsupplee and xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Restore original working Dockerfile](https://github.com/wekan/wekan/commit/c4ea7457dcf1db200c87784c35b7d3c390e94d80).
|
||||
Thanks to gpalyu and xet7.
|
||||
- [Reverted Cards, custom fields are displayed in alphabetic order from Wekan v4.71
|
||||
https://github.com/wekan/wekan/pulls/3417 because it caused board not
|
||||
loading](https://github.com/wekan/wekan/commit/413f91d0c8f2d3f9df9bf036bb20551dba29bc2e).
|
||||
Thanks to olivierlambert and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v4.80 2021-01-18 Wekan release
|
||||
|
||||
This release adds the following improvements:
|
||||
|
||||
- [Global Search fixes and updates](https://github.com/wekan/wekan/pulls/3437).
|
||||
Thanks to jrsupplee.
|
||||
- [Use table-cell instead of inline-block in my-cards-list-wrapper CSS](https://github.com/wekan/wekan/commit/3866ed31965eb5b722e88c4d3e7628d516375088).
|
||||
Thanks to johappel and xet7.
|
||||
- [Use multi stage build based on Node images](https://github.com/wekan/wekan/pull/3438).
|
||||
Thanks to GavinLilly.
|
||||
- [Try to use buster base images, because when using Wekan Alpine, registering new user of Wekan does not work,
|
||||
maybe because of glibc/musl](https://github.com/wekan/wekan/commit/254a9abad2dec620d95c02ac9209e9f569407986).
|
||||
Thanks to GavinLilly and xet7.
|
||||
- [Use MongoDB setFeatureCompatibilityVersion 4.2 on Snap. TODO: Docker](https://github.com/wekan/wekan/commit/2791b7da22ddb0ff5588eca56f1dc90ff5ffdd2d).
|
||||
Thanks to GuidoDr and xet7.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Update dependencies](https://github.com/wekan/wekan/commit/c0f748bcb5dfebe7fa90be647a1ed23f0edcc304).
|
||||
Thanks to developers of dependencies.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fixed Linked card makes board not load when CustomField definition is undefined](https://github.com/wekan/wekan/commit/0d5f33299ee25e1bee4ca4fc3b3c2483c29e367c).
|
||||
Thanks to olivierlambert and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v4.79 2021-01-17 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [At Search All Cards, now it's possible to click found card to open it](https://github.com/wekan/wekan/commit/10f74f5152117358e9c6b9bb0e81b8c284841aff).
|
||||
Thanks to xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fixed: Linked card makes board not load](https://github.com/wekan/wekan/commit/be03d2ae9aa708119992548145cbaf82e1f87419).
|
||||
Thanks to akitzing, galletl, pdonias, olivierlambert and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v4.78 2021-01-16 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v4.78.0"
|
||||
appVersion: "v4.81.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
template(name="resultCard")
|
||||
.result-card-wrapper
|
||||
a.minicard-wrapper.card-title(href=card.absoluteUrl)
|
||||
a.minicard-wrapper.card-title(href=absoluteUrl)
|
||||
+minicard(this)
|
||||
//= card.title
|
||||
ul.result-card-context-list
|
||||
|
||||
Executable → Regular
File mode changed.
@@ -14,51 +14,26 @@ template(name="globalSearch")
|
||||
if currentUser
|
||||
.wrapper
|
||||
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
|
||||
+spinner
|
||||
else if hasResults.get
|
||||
.global-search-dueat-list-wrapper
|
||||
h1
|
||||
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
|
||||
.global-search-results-list-wrapper
|
||||
if hasQueryErrors.get
|
||||
div
|
||||
each msg in errorMessages
|
||||
span.global-search-error-messages
|
||||
| {{_ msg.tag msg.value }}
|
||||
each card in results
|
||||
+resultCard(card)
|
||||
else
|
||||
h1
|
||||
= resultsHeading.get
|
||||
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
|
||||
each card in results
|
||||
+resultCard(card)
|
||||
else
|
||||
.global-search-instructions
|
||||
h1 Search Operators
|
||||
+viewer
|
||||
= '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. `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'
|
||||
= searchInstructions
|
||||
|
||||
template(name="globalSearchViewChangePopup")
|
||||
if currentUser
|
||||
|
||||
@@ -36,164 +36,333 @@ BlazeComponent.extendComponent({
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.isPageReady = new ReactiveVar(true);
|
||||
this.searching = new ReactiveVar(false);
|
||||
this.hasResults = new ReactiveVar(false);
|
||||
this.hasQueryErrors = new ReactiveVar(false);
|
||||
this.query = new ReactiveVar('');
|
||||
this.resultsHeading = new ReactiveVar('');
|
||||
this.searchLink = new ReactiveVar(null);
|
||||
this.queryParams = null;
|
||||
this.resultsCount = new ReactiveVar(0);
|
||||
this.totalHits = new ReactiveVar(0);
|
||||
this.queryErrors = new ReactiveVar(null);
|
||||
this.parsingErrors = [];
|
||||
this.resultsCount = 0;
|
||||
this.totalHits = 0;
|
||||
this.queryErrors = null;
|
||||
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() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('getting results');
|
||||
if (this.queryParams) {
|
||||
const results = Cards.globalSearch(this.queryParams);
|
||||
this.queryErrors = results.errors;
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('user:', Meteor.user());
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('user:', Meteor.user().sessionData);
|
||||
// console.log('errors:', results.errors);
|
||||
this.totalHits.set(Meteor.user().sessionData.totalHits);
|
||||
this.resultsCount.set(results.cards.count());
|
||||
this.queryErrors.set(results.errors);
|
||||
return results.cards;
|
||||
// console.log('errors:', this.queryErrors);
|
||||
if (this.errorMessages().length) {
|
||||
this.hasQueryErrors.set(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
this.resultsCount.set(0);
|
||||
this.resultsCount = 0;
|
||||
return [];
|
||||
},
|
||||
|
||||
errorMessages() {
|
||||
const errors = this.queryErrors.get();
|
||||
const messages = [];
|
||||
|
||||
errors.notFound.boards.forEach(board => {
|
||||
messages.push({ tag: 'board-title-not-found', value: board });
|
||||
});
|
||||
errors.notFound.swimlanes.forEach(swim => {
|
||||
messages.push({ tag: 'swimlane-title-not-found', value: swim });
|
||||
});
|
||||
errors.notFound.lists.forEach(list => {
|
||||
messages.push({ tag: 'list-title-not-found', value: list });
|
||||
});
|
||||
errors.notFound.users.forEach(user => {
|
||||
messages.push({ tag: 'user-username-not-found', value: user });
|
||||
});
|
||||
if (this.queryErrors) {
|
||||
this.queryErrors.notFound.boards.forEach(board => {
|
||||
messages.push({ tag: 'board-title-not-found', value: board });
|
||||
});
|
||||
this.queryErrors.notFound.swimlanes.forEach(swim => {
|
||||
messages.push({ tag: 'swimlane-title-not-found', value: swim });
|
||||
});
|
||||
this.queryErrors.notFound.lists.forEach(list => {
|
||||
messages.push({ tag: 'list-title-not-found', value: list });
|
||||
});
|
||||
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 });
|
||||
});
|
||||
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;
|
||||
},
|
||||
|
||||
searchAllBoards(query) {
|
||||
this.query.set(query);
|
||||
|
||||
this.resetSearch();
|
||||
|
||||
if (!query) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.searching.set(true);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('query:', query);
|
||||
|
||||
const reOperator1 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<value>\w+)(\s+|$)/;
|
||||
const reOperator2 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<quote>["']*)(?<value>.*?)\k<quote>(\s+|$)/;
|
||||
const reText = /^(?<text>\S+)(\s+|$)/;
|
||||
const reQuotedText = /^(?<quote>["'])(?<text>\w+)\k<quote>(\s+|$)/;
|
||||
|
||||
const colorMap = {};
|
||||
colorMap[TAPi18n.__('color-black')] = 'black';
|
||||
colorMap[TAPi18n.__('color-blue')] = 'blue';
|
||||
colorMap[TAPi18n.__('color-crimson')] = 'crimson';
|
||||
colorMap[TAPi18n.__('color-darkgreen')] = 'darkgreen';
|
||||
colorMap[TAPi18n.__('color-gold')] = 'gold';
|
||||
colorMap[TAPi18n.__('color-gray')] = 'gray';
|
||||
colorMap[TAPi18n.__('color-green')] = 'green';
|
||||
colorMap[TAPi18n.__('color-indigo')] = 'indigo';
|
||||
colorMap[TAPi18n.__('color-lime')] = 'lime';
|
||||
colorMap[TAPi18n.__('color-magenta')] = 'magenta';
|
||||
colorMap[TAPi18n.__('color-mistyrose')] = 'mistyrose';
|
||||
colorMap[TAPi18n.__('color-navy')] = 'navy';
|
||||
colorMap[TAPi18n.__('color-orange')] = 'orange';
|
||||
colorMap[TAPi18n.__('color-paleturquoise')] = 'paleturquoise';
|
||||
colorMap[TAPi18n.__('color-peachpuff')] = 'peachpuff';
|
||||
colorMap[TAPi18n.__('color-pink')] = 'pink';
|
||||
colorMap[TAPi18n.__('color-plum')] = 'plum';
|
||||
colorMap[TAPi18n.__('color-purple')] = 'purple';
|
||||
colorMap[TAPi18n.__('color-red')] = 'red';
|
||||
colorMap[TAPi18n.__('color-saddlebrown')] = 'saddlebrown';
|
||||
colorMap[TAPi18n.__('color-silver')] = 'silver';
|
||||
colorMap[TAPi18n.__('color-sky')] = 'sky';
|
||||
colorMap[TAPi18n.__('color-slateblue')] = 'slateblue';
|
||||
colorMap[TAPi18n.__('color-white')] = 'white';
|
||||
colorMap[TAPi18n.__('color-yellow')] = 'yellow';
|
||||
|
||||
const operatorMap = {};
|
||||
operatorMap[TAPi18n.__('operator-board')] = 'boards';
|
||||
operatorMap[TAPi18n.__('operator-board-abbrev')] = 'boards';
|
||||
operatorMap[TAPi18n.__('operator-swimlane')] = 'swimlanes';
|
||||
operatorMap[TAPi18n.__('operator-swimlane-abbrev')] = 'swimlanes';
|
||||
operatorMap[TAPi18n.__('operator-list')] = 'lists';
|
||||
operatorMap[TAPi18n.__('operator-list-abbrev')] = 'lists';
|
||||
operatorMap[TAPi18n.__('operator-label')] = 'labels';
|
||||
operatorMap[TAPi18n.__('operator-label-abbrev')] = 'labels';
|
||||
operatorMap[TAPi18n.__('operator-user')] = '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';
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('operatorMap:', operatorMap);
|
||||
const params = {
|
||||
boards: [],
|
||||
swimlanes: [],
|
||||
lists: [],
|
||||
users: [],
|
||||
members: [],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
is: [],
|
||||
};
|
||||
|
||||
let text = '';
|
||||
while (query) {
|
||||
m = query.match(reOperator1);
|
||||
if (!m) {
|
||||
m = query.match(reOperator2);
|
||||
if (m) {
|
||||
query = query.replace(reOperator2, '');
|
||||
}
|
||||
} else {
|
||||
query = query.replace(reOperator1, '');
|
||||
}
|
||||
if (m) {
|
||||
let op;
|
||||
if (m.groups.operator) {
|
||||
op = m.groups.operator.toLowerCase();
|
||||
} else {
|
||||
op = m.groups.abbrev;
|
||||
}
|
||||
if (op in operatorMap) {
|
||||
let value = m.groups.value;
|
||||
if (operatorMap[op] === 'labels') {
|
||||
if (value in colorMap) {
|
||||
value = colorMap[value];
|
||||
}
|
||||
}
|
||||
params[operatorMap[op]].push(value);
|
||||
} else {
|
||||
this.parsingErrors.push({
|
||||
tag: 'operator-unknown-error',
|
||||
value: op,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m = query.match(reQuotedText);
|
||||
if (!m) {
|
||||
m = query.match(reText);
|
||||
if (m) {
|
||||
query = query.replace(reText, '');
|
||||
}
|
||||
} else {
|
||||
query = query.replace(reQuotedText, '');
|
||||
}
|
||||
if (m) {
|
||||
text += (text ? ' ' : '') + m.groups.text;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('text:', text);
|
||||
params.text = text;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('params:', params);
|
||||
|
||||
this.queryParams = params;
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe('globalSearch', params);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('ready:', handle.ready());
|
||||
if (handle.ready()) {
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
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.query.set(evt.target.searchQuery.value);
|
||||
this.queryErrors.set(null);
|
||||
|
||||
if (!this.query.get()) {
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.searching.set(true);
|
||||
this.hasResults.set(false);
|
||||
|
||||
let query = this.query.get();
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('query:', query);
|
||||
|
||||
const reOperator1 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<value>\w+)(\s+|$)/;
|
||||
const reOperator2 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<quote>["']*)(?<value>.*?)\k<quote>(\s+|$)/;
|
||||
const reText = /^(?<text>\S+)(\s+|$)/;
|
||||
const reQuotedText = /^(?<quote>["'])(?<text>\w+)\k<quote>(\s+|$)/;
|
||||
|
||||
const operatorMap = {};
|
||||
operatorMap[TAPi18n.__('operator-board')] = 'boards';
|
||||
operatorMap[TAPi18n.__('operator-board-abbrev')] = 'boards';
|
||||
operatorMap[TAPi18n.__('operator-swimlane')] = 'swimlanes';
|
||||
operatorMap[TAPi18n.__('operator-swimlane-abbrev')] = 'swimlanes';
|
||||
operatorMap[TAPi18n.__('operator-list')] = 'lists';
|
||||
operatorMap[TAPi18n.__('operator-list-abbrev')] = 'lists';
|
||||
operatorMap[TAPi18n.__('operator-label')] = 'labels';
|
||||
operatorMap[TAPi18n.__('operator-label-abbrev')] = 'labels';
|
||||
operatorMap[TAPi18n.__('operator-user')] = 'users';
|
||||
operatorMap[TAPi18n.__('operator-user-abbrev')] = 'users';
|
||||
operatorMap[TAPi18n.__('operator-is')] = 'is';
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('operatorMap:', operatorMap);
|
||||
const params = {
|
||||
boards: [],
|
||||
swimlanes: [],
|
||||
lists: [],
|
||||
users: [],
|
||||
labels: [],
|
||||
is: [],
|
||||
};
|
||||
|
||||
let text = '';
|
||||
while (query) {
|
||||
m = query.match(reOperator1);
|
||||
if (!m) {
|
||||
m = query.match(reOperator2);
|
||||
if (m) {
|
||||
query = query.replace(reOperator2, '');
|
||||
}
|
||||
} else {
|
||||
query = query.replace(reOperator1, '');
|
||||
}
|
||||
if (m) {
|
||||
let op;
|
||||
if (m.groups.operator) {
|
||||
op = m.groups.operator.toLowerCase();
|
||||
} else {
|
||||
op = m.groups.abbrev;
|
||||
}
|
||||
if (op in operatorMap) {
|
||||
params[operatorMap[op]].push(m.groups.value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m = query.match(reQuotedText);
|
||||
if (!m) {
|
||||
m = query.match(reText);
|
||||
if (m) {
|
||||
query = query.replace(reText, '');
|
||||
}
|
||||
} else {
|
||||
query = query.replace(reQuotedText, '');
|
||||
}
|
||||
if (m) {
|
||||
text += (text ? ' ' : '') + m.groups.text;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('text:', text);
|
||||
params.text = text;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('params:', params);
|
||||
|
||||
this.queryParams = params;
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe('globalSearch', params);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('ready:', handle.ready());
|
||||
if (handle.ready()) {
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
this.searchAllBoards(evt.target.searchQuery.value);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
margin-top: 0
|
||||
margin-bottom: 10px
|
||||
|
||||
.global-search-dueat-list-wrapper
|
||||
.global-search-results-list-wrapper
|
||||
max-width: 500px
|
||||
margin-right: auto
|
||||
margin-left: auto
|
||||
@@ -91,7 +91,7 @@
|
||||
font-style: italic
|
||||
|
||||
code
|
||||
color: white
|
||||
background-color: grey
|
||||
color: black
|
||||
background-color: lightgrey
|
||||
padding: 0.1rem !important
|
||||
font-size: 0.7rem !important
|
||||
@@ -43,7 +43,7 @@
|
||||
border-radius: 5px
|
||||
padding: 1.5rem
|
||||
padding-top: 0.75rem
|
||||
display: inline-block
|
||||
display: table-cell
|
||||
min-width: 250px
|
||||
max-width: 350px
|
||||
|
||||
|
||||
@@ -158,7 +158,14 @@ FlowRouter.route('/global-search', {
|
||||
|
||||
Utils.manageCustomUI();
|
||||
Utils.manageMatomo();
|
||||
DocHead.setTitle(TAPi18n.__('globalSearch-title'));
|
||||
|
||||
if (FlowRouter.getQueryParam('q')) {
|
||||
Session.set(
|
||||
'globalQuery',
|
||||
decodeURIComponent(FlowRouter.getQueryParam('q')),
|
||||
);
|
||||
}
|
||||
BlazeLayout.render('defaultLayout', {
|
||||
headerBar: 'globalSearchHeaderBar',
|
||||
content: 'globalSearch',
|
||||
|
||||
@@ -131,14 +131,6 @@ services:
|
||||
#build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile
|
||||
# args:
|
||||
# - NODE_VERSION=${NODE_VERSION}
|
||||
# - METEOR_RELEASE=${METEOR_RELEASE}
|
||||
# - NPM_VERSION=${NPM_VERSION}
|
||||
# - ARCHITECTURE=${ARCHITECTURE}
|
||||
# - SRC_PATH=${SRC_PATH}
|
||||
# - METEOR_EDGE=${METEOR_EDGE}
|
||||
# - USE_EDGE=${USE_EDGE}
|
||||
#-------------------------------------------------------------------------------------
|
||||
ports:
|
||||
# Docker outsideport:insideport. Do not add anything extra here.
|
||||
|
||||
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+27
-2
@@ -868,12 +868,13 @@
|
||||
"board-title-not-found": "Board '%s' not found.",
|
||||
"swimlane-title-not-found": "Swimlane '%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.",
|
||||
"globalSearch-title": "Search All Boards",
|
||||
"no-cards-found": "No Cards Found",
|
||||
"one-card-found": "One Card 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-abbrev": "b",
|
||||
"operator-swimlane": "swimlane",
|
||||
@@ -884,5 +885,29 @@
|
||||
"operator-label-abbrev": "#",
|
||||
"operator-user": "user",
|
||||
"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"
|
||||
}
|
||||
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+27
-2
@@ -868,12 +868,13 @@
|
||||
"board-title-not-found": "Доска '%s' не найдена.",
|
||||
"swimlane-title-not-found": "Дорожка '%s' не найдена.",
|
||||
"list-title-not-found": "Список '%s' не найден.",
|
||||
"label-not-found": "Label '%s' not found.",
|
||||
"user-username-not-found": "Имя пользователя '%s' не найдено.",
|
||||
"globalSearch-title": "Искать на всех досках",
|
||||
"no-cards-found": "Ни одной карточки не найдено",
|
||||
"one-card-found": "Найдена одна карточка",
|
||||
"n-cards-found": "Карточек найдено %s",
|
||||
"n-n-of-n-cards-found": "Карточки %s - %s из %s найденных",
|
||||
"n-n-of-n-cards-found": "__start__-__end__ of __total__ Cards Found",
|
||||
"operator-board": "доска",
|
||||
"operator-board-abbrev": "д",
|
||||
"operator-swimlane": "дорожка",
|
||||
@@ -884,5 +885,29 @@
|
||||
"operator-label-abbrev": "#",
|
||||
"operator-user": "пользователь",
|
||||
"operator-user-abbrev": "@",
|
||||
"operator-is": " "
|
||||
"operator-member": "участника",
|
||||
"operator-member-abbrev": "m",
|
||||
"operator-assignee": "assignee",
|
||||
"operator-assignee-abbrev": "a",
|
||||
"operator-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"
|
||||
}
|
||||
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+912
-887
File diff suppressed because it is too large.
Load diff
+242
-66
@@ -689,7 +689,7 @@ Cards.helpers({
|
||||
|
||||
// match right definition to each field
|
||||
if (!this.customFields) return [];
|
||||
const ret = this.customFields.map(customField => {
|
||||
return this.customFields.map(customField => {
|
||||
const definition = definitions.find(definition => {
|
||||
return definition._id === customField._id;
|
||||
});
|
||||
@@ -715,8 +715,6 @@ Cards.helpers({
|
||||
definition,
|
||||
};
|
||||
});
|
||||
ret.sort((a, b) => a.definition.name.localeCompare(b.definition.name));
|
||||
return ret;
|
||||
},
|
||||
|
||||
colorClass() {
|
||||
@@ -858,12 +856,20 @@ Cards.helpers({
|
||||
getMembers() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.members;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.members;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
return board.activeMembers().map(member => {
|
||||
return member.userId;
|
||||
});
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.activeMembers().map(member => {
|
||||
return member.userId;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return this.members;
|
||||
}
|
||||
@@ -872,12 +878,20 @@ Cards.helpers({
|
||||
getAssignees() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.assignees;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.assignees;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
return board.activeMembers().map(assignee => {
|
||||
return assignee.userId;
|
||||
});
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.activeMembers().map(assignee => {
|
||||
return assignee.userId;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return this.assignees;
|
||||
}
|
||||
@@ -967,7 +981,11 @@ Cards.helpers({
|
||||
getReceived() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.receivedAt;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.receivedAt;
|
||||
}
|
||||
} else {
|
||||
return this.receivedAt;
|
||||
}
|
||||
@@ -984,10 +1002,18 @@ Cards.helpers({
|
||||
getStart() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.startAt;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.startAt;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
return board.startAt;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.startAt;
|
||||
}
|
||||
} else {
|
||||
return this.startAt;
|
||||
}
|
||||
@@ -1006,10 +1032,18 @@ Cards.helpers({
|
||||
getDue() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.dueAt;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.dueAt;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
return board.dueAt;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.dueAt;
|
||||
}
|
||||
} else {
|
||||
return this.dueAt;
|
||||
}
|
||||
@@ -1028,10 +1062,18 @@ Cards.helpers({
|
||||
getEnd() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.endAt;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.endAt;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
return board.endAt;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.endAt;
|
||||
}
|
||||
} else {
|
||||
return this.endAt;
|
||||
}
|
||||
@@ -1050,10 +1092,18 @@ Cards.helpers({
|
||||
getIsOvertime() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.isOvertime;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.isOvertime;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
return board.isOvertime;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.isOvertime;
|
||||
}
|
||||
} else {
|
||||
return this.isOvertime;
|
||||
}
|
||||
@@ -1072,10 +1122,18 @@ Cards.helpers({
|
||||
getSpentTime() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.spentTime;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.spentTime;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
return board.spentTime;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.spentTime;
|
||||
}
|
||||
} else {
|
||||
return this.spentTime;
|
||||
}
|
||||
@@ -1094,12 +1152,22 @@ Cards.helpers({
|
||||
getVoteQuestion() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
if (card && card.vote) return card.vote.question;
|
||||
else return null;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else if (card && card.vote) {
|
||||
return card.vote.question;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
if (board && board.vote) return board.vote.question;
|
||||
else return null;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else if (board && board.vote) {
|
||||
return board.vote.question;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (this.vote) {
|
||||
return this.vote.question;
|
||||
} else {
|
||||
@@ -1110,12 +1178,22 @@ Cards.helpers({
|
||||
getVotePublic() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
if (card && card.vote) return card.vote.public;
|
||||
else return null;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else if (card && card.vote) {
|
||||
return card.vote.public;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
if (board && board.vote) return board.vote.public;
|
||||
else return null;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else if (board && board.vote) {
|
||||
return board.vote.public;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (this.vote) {
|
||||
return this.vote.public;
|
||||
} else {
|
||||
@@ -1126,12 +1204,22 @@ Cards.helpers({
|
||||
getVoteEnd() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
if (card && card.vote) return card.vote.end;
|
||||
else return null;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else if (card && card.vote) {
|
||||
return card.vote.end;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
if (board && board.vote) return board.vote.end;
|
||||
else return null;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else if (board && board.vote) {
|
||||
return board.vote.end;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (this.vote) {
|
||||
return this.vote.end;
|
||||
} else {
|
||||
@@ -1184,10 +1272,20 @@ Cards.helpers({
|
||||
getTitle() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.title;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.title;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
return board.title;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.title;
|
||||
}
|
||||
} else if (this.title === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return this.title;
|
||||
}
|
||||
@@ -1196,14 +1294,29 @@ Cards.helpers({
|
||||
getBoardTitle() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
}
|
||||
const board = Boards.findOne({ _id: card.boardId });
|
||||
return board.title;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.title;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
return board.title;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.title;
|
||||
}
|
||||
} else {
|
||||
const board = Boards.findOne({ _id: this.boardId });
|
||||
return board.title;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.title;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1220,10 +1333,18 @@ Cards.helpers({
|
||||
getArchived() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.archived;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.archived;
|
||||
}
|
||||
} else if (this.isLinkedBoard()) {
|
||||
const board = Boards.findOne({ _id: this.linkedId });
|
||||
return board.archived;
|
||||
if (board === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return board.archived;
|
||||
}
|
||||
} else {
|
||||
return this.archived;
|
||||
}
|
||||
@@ -1240,7 +1361,11 @@ Cards.helpers({
|
||||
getRequestedBy() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.requestedBy;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.requestedBy;
|
||||
}
|
||||
} else {
|
||||
return this.requestedBy;
|
||||
}
|
||||
@@ -1257,7 +1382,11 @@ Cards.helpers({
|
||||
getAssignedBy() {
|
||||
if (this.isLinkedCard()) {
|
||||
const card = Cards.findOne({ _id: this.linkedId });
|
||||
return card.assignedBy;
|
||||
if (card === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return card.assignedBy;
|
||||
}
|
||||
} else {
|
||||
return this.assignedBy;
|
||||
}
|
||||
@@ -1735,16 +1864,29 @@ Cards.globalSearch = queryParams => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('userId:', userId);
|
||||
|
||||
const errors = {
|
||||
notFound: {
|
||||
boards: [],
|
||||
swimlanes: [],
|
||||
lists: [],
|
||||
labels: [],
|
||||
users: [],
|
||||
is: [],
|
||||
},
|
||||
};
|
||||
const errors = new (class {
|
||||
constructor() {
|
||||
this.notFound = {
|
||||
boards: [],
|
||||
swimlanes: [],
|
||||
lists: [],
|
||||
labels: [],
|
||||
users: [],
|
||||
members: [],
|
||||
assignees: [],
|
||||
is: [],
|
||||
};
|
||||
}
|
||||
|
||||
hasErrors() {
|
||||
for (const prop in this.notFound) {
|
||||
if (this.notFound[prop].length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
const selector = {
|
||||
archived: false,
|
||||
@@ -1808,25 +1950,63 @@ Cards.globalSearch = queryParams => {
|
||||
selector.listId.$in = queryLists;
|
||||
}
|
||||
|
||||
const queryMembers = [];
|
||||
const queryAssignees = [];
|
||||
if (queryParams.users.length) {
|
||||
const queryUsers = [];
|
||||
queryParams.users.forEach(query => {
|
||||
const users = Users.find({
|
||||
username: query,
|
||||
});
|
||||
if (users.count()) {
|
||||
users.forEach(user => {
|
||||
queryUsers.push(user._id);
|
||||
queryMembers.push(user._id);
|
||||
queryAssignees.push(user._id);
|
||||
});
|
||||
} else {
|
||||
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 = [
|
||||
{ members: { $in: queryUsers } },
|
||||
{ assignees: { $in: queryUsers } },
|
||||
{ members: { $in: queryMembers } },
|
||||
{ assignees: { $in: queryAssignees } },
|
||||
];
|
||||
} else if (queryMembers.length) {
|
||||
selector.members = { $in: queryMembers };
|
||||
} else if (queryAssignees.length) {
|
||||
selector.assignees = { $in: queryAssignees };
|
||||
}
|
||||
|
||||
if (queryParams.labels.length) {
|
||||
@@ -1872,7 +2052,7 @@ Cards.globalSearch = queryParams => {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
errors.notFound.labels.push({ tag: 'label', value: label });
|
||||
errors.notFound.labels.push(label);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1880,6 +2060,10 @@ Cards.globalSearch = queryParams => {
|
||||
});
|
||||
}
|
||||
|
||||
if (errors.hasErrors()) {
|
||||
return { cards: null, errors };
|
||||
}
|
||||
|
||||
if (queryParams.text) {
|
||||
const regex = new RegExp(queryParams.text, 'i');
|
||||
|
||||
@@ -1914,14 +2098,6 @@ Cards.globalSearch = queryParams => {
|
||||
// eslint-disable-next-line no-console
|
||||
// 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 };
|
||||
};
|
||||
|
||||
|
||||
@@ -377,6 +377,14 @@ Users.initEasySearch(searchInFields, {
|
||||
returnFields: [...searchInFields, 'profile.avatarUrl'],
|
||||
});
|
||||
|
||||
Users.safeFields = {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
};
|
||||
|
||||
if (Meteor.isClient) {
|
||||
Users.helpers({
|
||||
isBoardMember() {
|
||||
|
||||
@@ -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;
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v4.78.0",
|
||||
"version": "v4.81.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v4.78.0",
|
||||
"version": "v4.81.0",
|
||||
"description": "Open-Source kanban",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1524,7 +1524,7 @@ var n=this.pipeline.run(e.tokenizer(t)),r=new e.Vector,i=[],o=this._fields.reduc
|
||||
<ul class="toc-list-h1">
|
||||
|
||||
<li>
|
||||
<a href="#wekan-rest-api" class="toc-h1 toc-link" data-title="Wekan REST API v4.78">Wekan REST API v4.78</a>
|
||||
<a href="#wekan-rest-api" class="toc-h1 toc-link" data-title="Wekan REST API v4.81">Wekan REST API v4.81</a>
|
||||
|
||||
</li>
|
||||
|
||||
@@ -2037,7 +2037,7 @@ var n=this.pipeline.run(e.tokenizer(t)),r=new e.Vector,i=[],o=this._fields.reduc
|
||||
<div class="page-wrapper">
|
||||
<div class="dark-box"></div>
|
||||
<div class="content">
|
||||
<h1 id="wekan-rest-api">Wekan REST API v4.78</h1>
|
||||
<h1 id="wekan-rest-api">Wekan REST API v4.81</h1>
|
||||
<blockquote>
|
||||
<p>Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.</p>
|
||||
</blockquote>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: Wekan REST API
|
||||
version: v4.78
|
||||
version: v4.81
|
||||
description: |
|
||||
The REST API allows you to control and extend Wekan with ease.
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ const pkgdef :Spk.PackageDefinition = (
|
||||
appTitle = (defaultText = "Wekan"),
|
||||
# The name of the app as it is displayed to the user.
|
||||
|
||||
appVersion = 478,
|
||||
appVersion = 481,
|
||||
# Increment this for every release.
|
||||
|
||||
appMarketingVersion = (defaultText = "4.78.0~2021-01-16"),
|
||||
appMarketingVersion = (defaultText = "4.81.0~2021-01-18"),
|
||||
# Human-readable presentation of the app version.
|
||||
|
||||
minUpgradableAppVersion = 0,
|
||||
|
||||
@@ -72,18 +72,7 @@ Meteor.publish('myCards', function() {
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
Lists.find({ _id: { $in: lists } }),
|
||||
Users.find(
|
||||
{ _id: { $in: users } },
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
},
|
||||
},
|
||||
),
|
||||
Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
||||
];
|
||||
});
|
||||
|
||||
@@ -93,18 +82,7 @@ Meteor.publish('dueCards', function(allUsers = false) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('all users:', allUsers);
|
||||
|
||||
const user = Users.findOne(
|
||||
{ _id: this.userId },
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
const user = Users.findOne({ _id: this.userId });
|
||||
|
||||
const archivedBoards = [];
|
||||
Boards.find({ archived: true }).forEach(board => {
|
||||
@@ -115,14 +93,12 @@ Meteor.publish('dueCards', function(allUsers = false) {
|
||||
let selector = {
|
||||
archived: false,
|
||||
};
|
||||
// for admins and users, allow her to see cards only from boards where
|
||||
// she is a member
|
||||
//if (!user.isAdmin) {
|
||||
|
||||
selector.$or = [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
|
||||
];
|
||||
//}
|
||||
|
||||
Boards.find(selector).forEach(board => {
|
||||
permiitedBoards.push(board._id);
|
||||
});
|
||||
@@ -193,18 +169,7 @@ Meteor.publish('dueCards', function(allUsers = false) {
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
Lists.find({ _id: { $in: lists } }),
|
||||
Users.find(
|
||||
{ _id: { $in: users } },
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
},
|
||||
},
|
||||
),
|
||||
Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
||||
];
|
||||
});
|
||||
|
||||
@@ -216,6 +181,20 @@ Meteor.publish('globalSearch', function(queryParams) {
|
||||
|
||||
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 swimlanes = [];
|
||||
const lists = [];
|
||||
@@ -244,34 +223,21 @@ Meteor.publish('globalSearch', function(queryParams) {
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
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() {
|
||||
const user = Users.findOne(
|
||||
{ _id: this.userId },
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
const user = Users.findOne({ _id: this.userId });
|
||||
|
||||
const permiitedBoards = [null];
|
||||
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 = [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
|
||||
];
|
||||
//}
|
||||
|
||||
Boards.find(selector).forEach(board => {
|
||||
permiitedBoards.push(board._id);
|
||||
});
|
||||
@@ -328,17 +294,6 @@ Meteor.publish('brokenCards', function() {
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
Lists.find({ _id: { $in: lists } }),
|
||||
Users.find(
|
||||
{ _id: { $in: users } },
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 1,
|
||||
'profile.avatarUrl': 1,
|
||||
'profile.initials': 1,
|
||||
},
|
||||
},
|
||||
),
|
||||
Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
||||
];
|
||||
});
|
||||
@@ -95,6 +95,6 @@ else
|
||||
|
||||
# Drop indexes on database upgrade, when starting MongoDB
|
||||
#mongo wekan --eval "db.getCollectionNames().forEach(function(col_name) { var coll = db.getCollection(col_name); coll.dropIndexes(); });" $BIND_OPTIONS
|
||||
mongo wekan --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "4.4" });' $BIND_OPTIONS
|
||||
mongo wekan --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "4.2" });' $BIND_OPTIONS
|
||||
|
||||
fi
|
||||
Reference in new issue
Block a user