mirror of
https://github.com/wekan/wekan.git
synced 2026-02-04 15:41:52 +01:00
Merge branch 'feature-custom-fields' of https://github.com/feuerball11/wekan into feature-custom-fields
This commit is contained in:
commit
a433f7d9fe
76 changed files with 3087 additions and 450 deletions
|
|
@ -6,9 +6,17 @@ template(name="archivedBoards")
|
|||
ul.archived-lists
|
||||
each archivedBoards
|
||||
li.archived-lists-item
|
||||
button.js-restore-board
|
||||
i.fa.fa-undo
|
||||
| {{_ 'restore-board'}}
|
||||
= title
|
||||
div.board-header-btns
|
||||
button.board-header-btn.js-delete-board
|
||||
i.fa.fa-trash-o
|
||||
| {{_ 'delete-board'}}
|
||||
button.board-header-btn.js-restore-board
|
||||
i.fa.fa-undo
|
||||
| {{_ 'restore-board'}}
|
||||
= title
|
||||
else
|
||||
li.no-items-message {{_ 'no-archived-boards'}}
|
||||
|
||||
template(name="boardDeletePopup")
|
||||
p {{_ 'delete-board-confirm-popup'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,18 @@ BlazeComponent.extendComponent({
|
|||
board.restore();
|
||||
Utils.goBoardId(board._id);
|
||||
},
|
||||
'click .js-delete-board': Popup.afterConfirm('boardDelete', function() {
|
||||
Popup.close();
|
||||
const isSandstorm = Meteor.settings && Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && Session.get('currentBoard')) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
Boards.remove(currentBoard._id);
|
||||
}
|
||||
const board = this.currentData();
|
||||
Boards.remove(board._id);
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
}];
|
||||
},
|
||||
}).register('archivedBoards');
|
||||
|
|
|
|||
|
|
@ -88,11 +88,13 @@ BlazeComponent.extendComponent({
|
|||
|
||||
isViewSwimlanes() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return (currentUser.profile.boardView === 'board-view-swimlanes');
|
||||
},
|
||||
|
||||
isViewLists() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return true;
|
||||
return (currentUser.profile.boardView === 'board-view-lists');
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ Template.boardMenuPopup.events({
|
|||
// confirm that the board was successfully archived.
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
'click .js-delete-board': Popup.afterConfirm('deleteBoard', function() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
Popup.close();
|
||||
Boards.remove(currentBoard._id);
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
|
||||
'click .js-import-board': Popup.open('chooseBoardSource'),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ Template.dateBadge.helpers({
|
|||
});
|
||||
|
||||
// editCardReceivedDatePopup
|
||||
(class extends EditCardDate {
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this.data().receivedAt && this.date.set(moment(this.data().receivedAt));
|
||||
|
|
@ -156,7 +156,7 @@ Template.dateBadge.helpers({
|
|||
}).register('editCardDueDatePopup');
|
||||
|
||||
// editCardEndDatePopup
|
||||
(class extends EditCardDate {
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this.data().endAt && this.date.set(moment(this.data().endAt));
|
||||
|
|
@ -279,11 +279,14 @@ class CardDueDate extends CardDate {
|
|||
|
||||
classes() {
|
||||
let classes = 'due-date' + ' ';
|
||||
if (this.now.get().diff(this.date.get(), 'days') >= 2)
|
||||
if ((this.now.get().diff(this.date.get(), 'days') >= 2) &&
|
||||
(this.date.get().isBefore(this.data().endAt)))
|
||||
classes += 'long-overdue';
|
||||
else if (this.now.get().diff(this.date.get(), 'minute') >= 0)
|
||||
else if ((this.now.get().diff(this.date.get(), 'minute') >= 0) &&
|
||||
(this.date.get().isBefore(this.data().endAt)))
|
||||
classes += 'due';
|
||||
else if (this.now.get().diff(this.date.get(), 'days') >= -1)
|
||||
else if ((this.now.get().diff(this.date.get(), 'days') >= -1) &&
|
||||
(this.date.get().isBefore(this.data().endAt)))
|
||||
classes += 'almost-due';
|
||||
return classes;
|
||||
}
|
||||
|
|
@ -355,4 +358,3 @@ CardEndDate.register('cardEndDate');
|
|||
return this.date.get().format('l');
|
||||
}
|
||||
}).register('minicardEndDate');
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,39 @@ template(name="cardDetails")
|
|||
+viewer
|
||||
= description
|
||||
|
||||
.card-details-items
|
||||
.card-details-item.card-details-item-name
|
||||
h3.card-details-item-title {{_ 'requested-by'}}
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-details-requester")
|
||||
+editCardRequesterForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if requestedBy
|
||||
+viewer
|
||||
= requestedBy
|
||||
else
|
||||
| {{_ 'add'}}
|
||||
else if requestedBy
|
||||
+viewer
|
||||
= requestedBy
|
||||
|
||||
.card-details-item.card-details-item-name
|
||||
h3.card-details-item-title {{_ 'assigned-by'}}
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-details-assigner")
|
||||
+editCardAssignerForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if assignedBy
|
||||
+viewer
|
||||
= assignedBy
|
||||
else
|
||||
| {{_ 'add'}}
|
||||
else if requestedBy
|
||||
+viewer
|
||||
= assignedBy
|
||||
|
||||
hr
|
||||
+checklists(cardId = _id)
|
||||
|
||||
|
|
@ -141,6 +174,18 @@ template(name="editCardTitleForm")
|
|||
button.primary.confirm.js-submit-edit-card-title-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="editCardRequesterForm")
|
||||
input.js-edit-card-requester(type='text' autofocus value=requestedBy)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-requester-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="editCardAssignerForm")
|
||||
input.js-edit-card-assigner(type='text' autofocus value=assignedBy)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-assigner-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="cardDetailsActionsPopup")
|
||||
ul.pop-over-list
|
||||
li: a.js-toggle-watch-card {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}}
|
||||
|
|
@ -150,8 +195,8 @@ template(name="cardDetailsActionsPopup")
|
|||
li: a.js-members {{_ 'card-edit-members'}}
|
||||
li: a.js-labels {{_ 'card-edit-labels'}}
|
||||
li: a.js-attachments {{_ 'card-edit-attachments'}}
|
||||
li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
|
||||
li: a.js-custom-fields {{_ 'card-edit-custom-fields'}}
|
||||
li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
|
||||
li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
|
||||
li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
|
||||
li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
|
||||
|
|
@ -178,7 +223,6 @@ template(name="copyCardPopup")
|
|||
= title
|
||||
+boardsAndLists
|
||||
|
||||
|
||||
template(name="copyChecklistToManyCardsPopup")
|
||||
label(for='copy-checklist-cards-title') {{_ 'copyChecklistToManyCardsPopup-instructions'}}:
|
||||
textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
|
||||
|
|
|
|||
|
|
@ -150,6 +150,20 @@ BlazeComponent.extendComponent({
|
|||
this.data().setTitle(title);
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-assigner'(evt) {
|
||||
evt.preventDefault();
|
||||
const assigner = this.currentComponent().getValue().trim();
|
||||
if (assigner) {
|
||||
this.data().setAssignedBy(assigner);
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-requester'(evt) {
|
||||
evt.preventDefault();
|
||||
const requester = this.currentComponent().getValue().trim();
|
||||
if (requester) {
|
||||
this.data().setRequestedBy(requester);
|
||||
}
|
||||
},
|
||||
'click .js-member': Popup.open('cardMember'),
|
||||
'click .js-add-members': Popup.open('cardMembers'),
|
||||
'click .js-add-labels': Popup.open('cardLabels'),
|
||||
|
|
@ -221,8 +235,8 @@ Template.cardDetailsActionsPopup.events({
|
|||
'click .js-members': Popup.open('cardMembers'),
|
||||
'click .js-labels': Popup.open('cardLabels'),
|
||||
'click .js-attachments': Popup.open('cardAttachments'),
|
||||
'click .js-received-date': Popup.open('editCardReceivedDate'),
|
||||
'click .js-custom-fields': Popup.open('cardCustomFields'),
|
||||
'click .js-received-date': Popup.open('editCardReceivedDate'),
|
||||
'click .js-start-date': Popup.open('editCardStartDate'),
|
||||
'click .js-due-date': Popup.open('editCardDueDate'),
|
||||
'click .js-end-date': Popup.open('editCardEndDate'),
|
||||
|
|
@ -269,6 +283,32 @@ Template.editCardTitleForm.events({
|
|||
},
|
||||
});
|
||||
|
||||
Template.editCardRequesterForm.onRendered(function() {
|
||||
autosize(this.$('.js-edit-card-requester'));
|
||||
});
|
||||
|
||||
Template.editCardRequesterForm.events({
|
||||
'keydown .js-edit-card-requester'(evt) {
|
||||
// If enter key was pressed, submit the data
|
||||
if (evt.keyCode === 13) {
|
||||
$('.js-submit-edit-card-requester-form').click();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardAssignerForm.onRendered(function() {
|
||||
autosize(this.$('.js-edit-card-assigner'));
|
||||
});
|
||||
|
||||
Template.editCardAssignerForm.events({
|
||||
'keydown .js-edit-card-assigner'(evt) {
|
||||
// If enter key was pressed, submit the data
|
||||
if (evt.keyCode === 13) {
|
||||
$('.js-submit-edit-card-assigner-form').click();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.moveCardPopup.events({
|
||||
'click .js-done' () {
|
||||
// XXX We should *not* get the currentCard from the global state, but
|
||||
|
|
|
|||
|
|
@ -82,7 +82,8 @@
|
|||
&.card-details-item-start,
|
||||
&.card-details-item-due,
|
||||
&.card-details-item-end,
|
||||
&.card-details-item-customfield
|
||||
&.card-details-item-customfield,
|
||||
&.card-details-item-name
|
||||
max-width: 50%
|
||||
flex-grow: 1
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// XXX Use .board-widget-labels as a flexbox container
|
||||
.card-label
|
||||
border-radius: 4px
|
||||
color: white
|
||||
color: white //Default white text, in select cases, changed to black to improve contrast between label colour and text
|
||||
display: inline-block
|
||||
font-weight: 700
|
||||
font-size: 13px
|
||||
|
|
@ -48,9 +48,11 @@
|
|||
|
||||
.card-label-yellow
|
||||
background-color: #fad900
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-orange
|
||||
background-color: #ff9f19
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-red
|
||||
background-color: #eb4646
|
||||
|
|
@ -63,6 +65,7 @@
|
|||
|
||||
.card-label-pink
|
||||
background-color: #ff78cb
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-sky
|
||||
background-color: #00c2e0
|
||||
|
|
@ -72,6 +75,55 @@
|
|||
|
||||
.card-label-lime
|
||||
background-color: #51e898
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-silver
|
||||
background-color: #c0c0c0
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-peachpuff
|
||||
background-color: #ffdab9
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-crimson
|
||||
background-color: #dc143c
|
||||
|
||||
.card-label-plum
|
||||
background-color: #dda0dd
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-darkgreen
|
||||
background-color: #006400
|
||||
|
||||
.card-label-slateblue
|
||||
background-color: #6a5acd
|
||||
|
||||
.card-label-magenta
|
||||
background-color: #ff00ff
|
||||
|
||||
.card-label-gold
|
||||
background-color: #ffd700
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-navy
|
||||
background-color: #000080
|
||||
|
||||
.card-label-gray
|
||||
background-color: #808080
|
||||
|
||||
.card-label-saddlebrown
|
||||
background-color: #8b4513
|
||||
|
||||
.card-label-paleturquoise
|
||||
background-color: #afeeee
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-mistyrose
|
||||
background-color: #ffe4e1
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-indigo
|
||||
background-color: #4b0082
|
||||
|
||||
.edit-label,
|
||||
.create-label
|
||||
|
|
|
|||
|
|
@ -10,20 +10,41 @@ template(name="minicard")
|
|||
+viewer
|
||||
= title
|
||||
.dates
|
||||
if receivedAt
|
||||
unless startAt
|
||||
unless dueAt
|
||||
unless endAt
|
||||
.date
|
||||
+miniCardReceivedDate
|
||||
if startAt
|
||||
.date
|
||||
+minicardStartDate
|
||||
if dueAt
|
||||
unless endAt
|
||||
.date
|
||||
+minicardDueDate
|
||||
if endAt
|
||||
.date
|
||||
+minicardDueDate
|
||||
+minicardEndDate
|
||||
if spentTime
|
||||
.date
|
||||
+cardSpentTime
|
||||
|
||||
.minicard-custom-fields
|
||||
each customFieldsWD
|
||||
if definition.showOnCard
|
||||
.minicard-custom-field
|
||||
.minicard-custom-field-item
|
||||
= definition.name
|
||||
.minicard-custom-field-item
|
||||
+viewer
|
||||
= trueValue
|
||||
|
||||
if members
|
||||
.minicard-members.js-minicard-members
|
||||
each members
|
||||
+userAvatar(userId=this)
|
||||
|
||||
.badges
|
||||
if comments.count
|
||||
.badge(title="{{_ 'card-comments-title' comments.count }}")
|
||||
|
|
|
|||
|
|
@ -77,6 +77,13 @@
|
|||
height: @width
|
||||
border-radius: 2px
|
||||
margin-left: 3px
|
||||
.minicard-custom-fields
|
||||
display:block;
|
||||
.minicard-custom-field
|
||||
display:flex;
|
||||
.minicard-custom-field-item
|
||||
max-width:50%;
|
||||
flex-grow:1;
|
||||
.minicard-title
|
||||
p:last-child
|
||||
margin-bottom: 0
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ template(name="createCustomFieldPopup")
|
|||
each dropdownItems.get
|
||||
input.js-dropdown-item(type="text" value=name placeholder="")
|
||||
input.js-dropdown-item.last(type="text" value="" placeholder="{{_ 'custom-field-dropdown-options-placeholder'}}")
|
||||
a.flex.js-field-show-on-card
|
||||
a.flex.js-field-show-on-card(class="{{#if showOnCard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if showOnCard}}is-checked{{/if}}")
|
||||
|
||||
span {{_ 'show-field-on-card'}}
|
||||
|
|
@ -49,4 +49,4 @@ template(name="createCustomFieldPopup")
|
|||
|
||||
template(name="deleteCustomFieldPopup")
|
||||
p {{_ "custom-field-delete-pop"}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,25 @@ template(name="filterSidebar")
|
|||
| (<span class="username">{{ username }}</span>)
|
||||
if Filter.members.isSelected _id
|
||||
i.fa.fa-check
|
||||
hr
|
||||
ul.sidebar-list
|
||||
li(class="{{#if Filter.customFields.isSelected undefined}}active{{/if}}")
|
||||
a.name.js-toggle-custom-fields-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-no-custom-fields'}}
|
||||
if Filter.customFields.isSelected undefined
|
||||
i.fa.fa-check
|
||||
each currentBoard.customFields
|
||||
li(class="{{#if Filter.customFields.isSelected _id}}active{{/if}}")
|
||||
a.name.js-toggle-custom-fields-filter
|
||||
span.sidebar-list-item-description
|
||||
{{ name }}
|
||||
if Filter.customFields.isSelected _id
|
||||
i.fa.fa-check
|
||||
hr
|
||||
span {{_ 'advanced-filter-label'}}
|
||||
input.js-field-advanced-filter(type="text")
|
||||
span {{_ 'advanced-filter-description'}}
|
||||
if Filter.isActive
|
||||
hr
|
||||
a.sidebar-btn.js-clear-all
|
||||
|
|
|
|||
|
|
@ -11,6 +11,16 @@ BlazeComponent.extendComponent({
|
|||
Filter.members.toggle(this.currentData()._id);
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-custom-fields-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.customFields.toggle(this.currentData()._id);
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'change .js-field-advanced-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.advanced.set(this.find('.js-field-advanced-filter').value.trim());
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-clear-all'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.reset();
|
||||
|
|
|
|||
|
|
@ -10,10 +10,13 @@ function showFilterSidebar() {
|
|||
|
||||
// Use a "set" filter for a field that is a set of documents uniquely
|
||||
// identified. For instance `{ labels: ['labelA', 'labelC', 'labelD'] }`.
|
||||
// use "subField" for searching inside object Fields.
|
||||
// For instance '{ 'customFields._id': ['field1','field2']} (subField would be: _id)
|
||||
class SetFilter {
|
||||
constructor() {
|
||||
constructor(subField = '') {
|
||||
this._dep = new Tracker.Dependency();
|
||||
this._selectedElements = [];
|
||||
this.subField = subField;
|
||||
}
|
||||
|
||||
isSelected(val) {
|
||||
|
|
@ -76,6 +79,295 @@ class SetFilter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Advanced filter forms a MongoSelector from a users String.
|
||||
// Build by: Ignatz 19.05.2018 (github feuerball11)
|
||||
class AdvancedFilter {
|
||||
constructor() {
|
||||
this._dep = new Tracker.Dependency();
|
||||
this._filter = '';
|
||||
this._lastValide = {};
|
||||
}
|
||||
|
||||
set(str) {
|
||||
this._filter = str;
|
||||
this._dep.changed();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._filter = '';
|
||||
this._lastValide = {};
|
||||
this._dep.changed();
|
||||
}
|
||||
|
||||
_isActive() {
|
||||
this._dep.depend();
|
||||
return this._filter !== '';
|
||||
}
|
||||
|
||||
_filterToCommands() {
|
||||
const commands = [];
|
||||
let current = '';
|
||||
let string = false;
|
||||
let wasString = false;
|
||||
let ignore = false;
|
||||
for (let i = 0; i < this._filter.length; i++) {
|
||||
const char = this._filter.charAt(i);
|
||||
if (ignore) {
|
||||
ignore = false;
|
||||
continue;
|
||||
}
|
||||
if (char === '\'') {
|
||||
string = !string;
|
||||
if (string) wasString = true;
|
||||
continue;
|
||||
}
|
||||
if (char === '\\') {
|
||||
ignore = true;
|
||||
continue;
|
||||
}
|
||||
if (char === ' ' && !string) {
|
||||
commands.push({ 'cmd': current, 'string': wasString });
|
||||
wasString = false;
|
||||
current = '';
|
||||
continue;
|
||||
}
|
||||
current += char;
|
||||
}
|
||||
if (current !== '') {
|
||||
commands.push({ 'cmd': current, 'string': wasString });
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
|
||||
_fieldNameToId(field) {
|
||||
const found = CustomFields.findOne({ 'name': field });
|
||||
return found._id;
|
||||
}
|
||||
|
||||
_fieldValueToId(field, value)
|
||||
{
|
||||
const found = CustomFields.findOne({ 'name': field });
|
||||
if (found.settings.dropdownItems && found.settings.dropdownItems.length > 0)
|
||||
{
|
||||
for (let i = 0; i < found.settings.dropdownItems.length; i++)
|
||||
{
|
||||
if (found.settings.dropdownItems[i].name === value)
|
||||
{
|
||||
return found.settings.dropdownItems[i]._id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
_arrayToSelector(commands) {
|
||||
try {
|
||||
//let changed = false;
|
||||
this._processSubCommands(commands);
|
||||
}
|
||||
catch (e) { return this._lastValide; }
|
||||
this._lastValide = { $or: commands };
|
||||
return { $or: commands };
|
||||
}
|
||||
|
||||
_processSubCommands(commands) {
|
||||
const subcommands = [];
|
||||
let level = 0;
|
||||
let start = -1;
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
if (commands[i].cmd) {
|
||||
switch (commands[i].cmd) {
|
||||
case '(':
|
||||
{
|
||||
level++;
|
||||
if (start === -1) start = i;
|
||||
continue;
|
||||
}
|
||||
case ')':
|
||||
{
|
||||
level--;
|
||||
commands.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (level > 0) {
|
||||
subcommands.push(commands[i]);
|
||||
commands.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start !== -1) {
|
||||
this._processSubCommands(subcommands);
|
||||
if (subcommands.length === 1)
|
||||
commands.splice(start, 0, subcommands[0]);
|
||||
else
|
||||
commands.splice(start, 0, subcommands);
|
||||
}
|
||||
this._processConditions(commands);
|
||||
this._processLogicalOperators(commands);
|
||||
}
|
||||
|
||||
_processConditions(commands) {
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
if (!commands[i].string && commands[i].cmd) {
|
||||
switch (commands[i].cmd) {
|
||||
case '=':
|
||||
case '==':
|
||||
case '===':
|
||||
{
|
||||
const field = commands[i - 1].cmd;
|
||||
const str = commands[i + 1].cmd;
|
||||
commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': {$in: [this._fieldValueToId(field, str), parseInt(str, 10)]} };
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case '!=':
|
||||
case '!==':
|
||||
{
|
||||
const field = commands[i - 1].cmd;
|
||||
const str = commands[i + 1].cmd;
|
||||
commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $not: {$in: [this._fieldValueToId(field, str), parseInt(str, 10)]} } };
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case '>':
|
||||
case 'gt':
|
||||
case 'Gt':
|
||||
case 'GT':
|
||||
{
|
||||
const field = commands[i - 1].cmd;
|
||||
const str = commands[i + 1].cmd;
|
||||
commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $gt: parseInt(str, 10) } };
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case '>=':
|
||||
case '>==':
|
||||
case 'gte':
|
||||
case 'Gte':
|
||||
case 'GTE':
|
||||
{
|
||||
const field = commands[i - 1].cmd;
|
||||
const str = commands[i + 1].cmd;
|
||||
commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $gte: parseInt(str, 10) } };
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case '<':
|
||||
case 'lt':
|
||||
case 'Lt':
|
||||
case 'LT':
|
||||
{
|
||||
const field = commands[i - 1].cmd;
|
||||
const str = commands[i + 1].cmd;
|
||||
commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $lt: parseInt(str, 10) } };
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case '<=':
|
||||
case '<==':
|
||||
case 'lte':
|
||||
case 'Lte':
|
||||
case 'LTE':
|
||||
{
|
||||
const field = commands[i - 1].cmd;
|
||||
const str = commands[i + 1].cmd;
|
||||
commands[i] = { 'customFields._id': this._fieldNameToId(field), 'customFields.value': { $lte: parseInt(str, 10) } };
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_processLogicalOperators(commands) {
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
if (!commands[i].string && commands[i].cmd) {
|
||||
switch (commands[i].cmd) {
|
||||
case 'or':
|
||||
case 'Or':
|
||||
case 'OR':
|
||||
case '|':
|
||||
case '||':
|
||||
{
|
||||
const op1 = commands[i - 1];
|
||||
const op2 = commands[i + 1];
|
||||
commands[i] = { $or: [op1, op2] };
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
case 'and':
|
||||
case 'And':
|
||||
case 'AND':
|
||||
case '&':
|
||||
case '&&':
|
||||
{
|
||||
const op1 = commands[i - 1];
|
||||
const op2 = commands[i + 1];
|
||||
commands[i] = { $and: [op1, op2] };
|
||||
commands.splice(i - 1, 1);
|
||||
commands.splice(i, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'not':
|
||||
case 'Not':
|
||||
case 'NOT':
|
||||
case '!':
|
||||
{
|
||||
const op1 = commands[i + 1];
|
||||
commands[i] = { $not: op1 };
|
||||
commands.splice(i + 1, 1);
|
||||
//changed = true;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_getMongoSelector() {
|
||||
this._dep.depend();
|
||||
const commands = this._filterToCommands();
|
||||
return this._arrayToSelector(commands);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The global Filter object.
|
||||
// XXX It would be possible to re-write this object more elegantly, and removing
|
||||
// the need to provide a list of `_fields`. We also should move methods into the
|
||||
|
|
@ -86,8 +378,10 @@ Filter = {
|
|||
// before changing the schema.
|
||||
labelIds: new SetFilter(),
|
||||
members: new SetFilter(),
|
||||
customFields: new SetFilter('_id'),
|
||||
advanced: new AdvancedFilter(),
|
||||
|
||||
_fields: ['labelIds', 'members'],
|
||||
_fields: ['labelIds', 'members', 'customFields'],
|
||||
|
||||
// We don't filter cards that have been added after the last filter change. To
|
||||
// implement this we keep the id of these cards in this `_exceptions` fields
|
||||
|
|
@ -98,7 +392,7 @@ Filter = {
|
|||
isActive() {
|
||||
return _.any(this._fields, (fieldName) => {
|
||||
return this[fieldName]._isActive();
|
||||
});
|
||||
}) || this.advanced._isActive();
|
||||
},
|
||||
|
||||
_getMongoSelector() {
|
||||
|
|
@ -111,7 +405,12 @@ Filter = {
|
|||
this._fields.forEach((fieldName) => {
|
||||
const filter = this[fieldName];
|
||||
if (filter._isActive()) {
|
||||
filterSelector[fieldName] = filter._getMongoSelector();
|
||||
if (filter.subField !== '') {
|
||||
filterSelector[`${fieldName}.${filter.subField}`] = filter._getMongoSelector();
|
||||
}
|
||||
else {
|
||||
filterSelector[fieldName] = filter._getMongoSelector();
|
||||
}
|
||||
emptySelector[fieldName] = filter._getEmptySelector();
|
||||
if (emptySelector[fieldName] !== null) {
|
||||
includeEmptySelectors = true;
|
||||
|
|
@ -119,13 +418,18 @@ Filter = {
|
|||
}
|
||||
});
|
||||
|
||||
const exceptionsSelector = {_id: {$in: this._exceptions}};
|
||||
const exceptionsSelector = { _id: { $in: this._exceptions } };
|
||||
this._exceptionsDep.depend();
|
||||
|
||||
if (includeEmptySelectors)
|
||||
return {$or: [filterSelector, exceptionsSelector, emptySelector]};
|
||||
else
|
||||
return {$or: [filterSelector, exceptionsSelector]};
|
||||
const selectors = [exceptionsSelector];
|
||||
|
||||
if (_.any(this._fields, (fieldName) => {
|
||||
return this[fieldName]._isActive();
|
||||
})) selectors.push(filterSelector);
|
||||
if (includeEmptySelectors) selectors.push(emptySelector);
|
||||
if (this.advanced._isActive()) selectors.push(this.advanced._getMongoSelector());
|
||||
|
||||
return { $or: selectors };
|
||||
},
|
||||
|
||||
mongoSelector(additionalSelector) {
|
||||
|
|
@ -133,7 +437,7 @@ Filter = {
|
|||
if (_.isUndefined(additionalSelector))
|
||||
return filterSelector;
|
||||
else
|
||||
return {$and: [filterSelector, additionalSelector]};
|
||||
return { $and: [filterSelector, additionalSelector] };
|
||||
},
|
||||
|
||||
reset() {
|
||||
|
|
@ -141,6 +445,7 @@ Filter = {
|
|||
const filter = this[fieldName];
|
||||
filter.reset();
|
||||
});
|
||||
this.advanced.reset();
|
||||
this.resetExceptions();
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue