|
|
|
@ -1,48 +1,6 @@ |
|
|
|
|
import { CardSearchPagedComponent } from '../../lib/cardSearch'; |
|
|
|
|
import Boards from '../../../models/boards'; |
|
|
|
|
import moment from 'moment'; |
|
|
|
|
import { |
|
|
|
|
OPERATOR_ASSIGNEE, |
|
|
|
|
OPERATOR_BOARD, |
|
|
|
|
OPERATOR_COMMENT, |
|
|
|
|
OPERATOR_CREATED_AT, |
|
|
|
|
OPERATOR_DUE, |
|
|
|
|
OPERATOR_HAS, |
|
|
|
|
OPERATOR_LABEL, |
|
|
|
|
OPERATOR_LIMIT, |
|
|
|
|
OPERATOR_LIST, |
|
|
|
|
OPERATOR_MEMBER, |
|
|
|
|
OPERATOR_MODIFIED_AT, |
|
|
|
|
OPERATOR_SORT, |
|
|
|
|
OPERATOR_STATUS, |
|
|
|
|
OPERATOR_SWIMLANE, |
|
|
|
|
OPERATOR_UNKNOWN, |
|
|
|
|
OPERATOR_USER, |
|
|
|
|
ORDER_ASCENDING, |
|
|
|
|
ORDER_DESCENDING, |
|
|
|
|
PREDICATE_ALL, |
|
|
|
|
PREDICATE_ARCHIVED, |
|
|
|
|
PREDICATE_ASSIGNEES, |
|
|
|
|
PREDICATE_ATTACHMENT, |
|
|
|
|
PREDICATE_CHECKLIST, |
|
|
|
|
PREDICATE_CREATED_AT, |
|
|
|
|
PREDICATE_DESCRIPTION, |
|
|
|
|
PREDICATE_DUE_AT, |
|
|
|
|
PREDICATE_END_AT, |
|
|
|
|
PREDICATE_ENDED, |
|
|
|
|
PREDICATE_MEMBERS, |
|
|
|
|
PREDICATE_MODIFIED_AT, |
|
|
|
|
PREDICATE_MONTH, |
|
|
|
|
PREDICATE_OPEN, |
|
|
|
|
PREDICATE_OVERDUE, |
|
|
|
|
PREDICATE_PRIVATE, |
|
|
|
|
PREDICATE_PUBLIC, |
|
|
|
|
PREDICATE_QUARTER, |
|
|
|
|
PREDICATE_START_AT, |
|
|
|
|
PREDICATE_WEEK, |
|
|
|
|
PREDICATE_YEAR, |
|
|
|
|
} from '../../../config/search-const'; |
|
|
|
|
import { QueryErrors, QueryParams } from '../../../config/query-classes'; |
|
|
|
|
import { Query, QueryErrors } from '../../../config/query-classes'; |
|
|
|
|
|
|
|
|
|
// const subManager = new SubsManager();
|
|
|
|
|
|
|
|
|
@ -62,24 +20,6 @@ Template.globalSearch.helpers({ |
|
|
|
|
}, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
BlazeComponent.extendComponent({ |
|
|
|
|
events() { |
|
|
|
|
return [ |
|
|
|
|
{ |
|
|
|
|
'click .js-due-cards-view-me'() { |
|
|
|
|
Utils.setDueCardsView('me'); |
|
|
|
|
Popup.close(); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
'click .js-due-cards-view-all'() { |
|
|
|
|
Utils.setDueCardsView('all'); |
|
|
|
|
Popup.close(); |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
]; |
|
|
|
|
}, |
|
|
|
|
}).register('globalSearchViewChangePopup'); |
|
|
|
|
|
|
|
|
|
class GlobalSearchComponent extends CardSearchPagedComponent { |
|
|
|
|
onCreated() { |
|
|
|
|
super.onCreated(); |
|
|
|
@ -87,7 +27,6 @@ class GlobalSearchComponent extends CardSearchPagedComponent { |
|
|
|
|
this.myLabelNames = new ReactiveVar([]); |
|
|
|
|
this.myBoardNames = new ReactiveVar([]); |
|
|
|
|
this.parsingErrors = new QueryErrors(); |
|
|
|
|
this.colorMap = null; |
|
|
|
|
this.queryParams = null; |
|
|
|
|
|
|
|
|
|
Meteor.call('myLists', (err, data) => { |
|
|
|
@ -114,9 +53,6 @@ class GlobalSearchComponent extends CardSearchPagedComponent { |
|
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
//console.log('lang:', TAPi18n.getLanguage());
|
|
|
|
|
this.colorMap = Boards.colorMap(); |
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
// console.log('colorMap:', this.colorMap);
|
|
|
|
|
|
|
|
|
|
if (Session.get('globalQuery')) { |
|
|
|
|
this.searchAllBoards(Session.get('globalQuery')); |
|
|
|
@ -139,327 +75,38 @@ class GlobalSearchComponent extends CardSearchPagedComponent { |
|
|
|
|
this.parsingErrors.errorMessages(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
searchAllBoards(query) { |
|
|
|
|
query = query.trim(); |
|
|
|
|
searchAllBoards(queryText) { |
|
|
|
|
queryText = queryText.trim(); |
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
//console.log('query:', query);
|
|
|
|
|
//console.log('queryText:', queryText);
|
|
|
|
|
|
|
|
|
|
this.query.set(query); |
|
|
|
|
this.query.set(queryText); |
|
|
|
|
|
|
|
|
|
this.resetSearch(); |
|
|
|
|
|
|
|
|
|
if (!query) { |
|
|
|
|
if (!queryText) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.searching.set(true); |
|
|
|
|
|
|
|
|
|
const reOperator1 = new RegExp( |
|
|
|
|
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<value>[\\p{Letter}\\p{Mark}]+)(\\s+|$)', |
|
|
|
|
'iu', |
|
|
|
|
); |
|
|
|
|
const reOperator2 = new RegExp( |
|
|
|
|
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<quote>["\']*)(?<value>.*?)\\k<quote>(\\s+|$)', |
|
|
|
|
'iu', |
|
|
|
|
); |
|
|
|
|
const reText = new RegExp('^(?<text>\\S+)(\\s+|$)', 'u'); |
|
|
|
|
const reQuotedText = new RegExp( |
|
|
|
|
'^(?<quote>["\'])(?<text>.*?)\\k<quote>(\\s+|$)', |
|
|
|
|
'u', |
|
|
|
|
); |
|
|
|
|
const reNegatedOperator = new RegExp('^-(?<operator>.*)$'); |
|
|
|
|
|
|
|
|
|
const operators = { |
|
|
|
|
'operator-board': OPERATOR_BOARD, |
|
|
|
|
'operator-board-abbrev': OPERATOR_BOARD, |
|
|
|
|
'operator-swimlane': OPERATOR_SWIMLANE, |
|
|
|
|
'operator-swimlane-abbrev': OPERATOR_SWIMLANE, |
|
|
|
|
'operator-list': OPERATOR_LIST, |
|
|
|
|
'operator-list-abbrev': OPERATOR_LIST, |
|
|
|
|
'operator-label': OPERATOR_LABEL, |
|
|
|
|
'operator-label-abbrev': OPERATOR_LABEL, |
|
|
|
|
'operator-user': OPERATOR_USER, |
|
|
|
|
'operator-user-abbrev': OPERATOR_USER, |
|
|
|
|
'operator-member': OPERATOR_MEMBER, |
|
|
|
|
'operator-member-abbrev': OPERATOR_MEMBER, |
|
|
|
|
'operator-assignee': OPERATOR_ASSIGNEE, |
|
|
|
|
'operator-assignee-abbrev': OPERATOR_ASSIGNEE, |
|
|
|
|
'operator-status': OPERATOR_STATUS, |
|
|
|
|
'operator-due': OPERATOR_DUE, |
|
|
|
|
'operator-created': OPERATOR_CREATED_AT, |
|
|
|
|
'operator-modified': OPERATOR_MODIFIED_AT, |
|
|
|
|
'operator-comment': OPERATOR_COMMENT, |
|
|
|
|
'operator-has': OPERATOR_HAS, |
|
|
|
|
'operator-sort': OPERATOR_SORT, |
|
|
|
|
'operator-limit': OPERATOR_LIMIT, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const predicates = { |
|
|
|
|
due: { |
|
|
|
|
'predicate-overdue': PREDICATE_OVERDUE, |
|
|
|
|
}, |
|
|
|
|
durations: { |
|
|
|
|
'predicate-week': PREDICATE_WEEK, |
|
|
|
|
'predicate-month': PREDICATE_MONTH, |
|
|
|
|
'predicate-quarter': PREDICATE_QUARTER, |
|
|
|
|
'predicate-year': PREDICATE_YEAR, |
|
|
|
|
}, |
|
|
|
|
status: { |
|
|
|
|
'predicate-archived': PREDICATE_ARCHIVED, |
|
|
|
|
'predicate-all': PREDICATE_ALL, |
|
|
|
|
'predicate-open': PREDICATE_OPEN, |
|
|
|
|
'predicate-ended': PREDICATE_ENDED, |
|
|
|
|
'predicate-public': PREDICATE_PUBLIC, |
|
|
|
|
'predicate-private': PREDICATE_PRIVATE, |
|
|
|
|
}, |
|
|
|
|
sorts: { |
|
|
|
|
'predicate-due': PREDICATE_DUE_AT, |
|
|
|
|
'predicate-created': PREDICATE_CREATED_AT, |
|
|
|
|
'predicate-modified': PREDICATE_MODIFIED_AT, |
|
|
|
|
}, |
|
|
|
|
has: { |
|
|
|
|
'predicate-description': PREDICATE_DESCRIPTION, |
|
|
|
|
'predicate-checklist': PREDICATE_CHECKLIST, |
|
|
|
|
'predicate-attachment': PREDICATE_ATTACHMENT, |
|
|
|
|
'predicate-start': PREDICATE_START_AT, |
|
|
|
|
'predicate-end': PREDICATE_END_AT, |
|
|
|
|
'predicate-due': PREDICATE_DUE_AT, |
|
|
|
|
'predicate-assignee': PREDICATE_ASSIGNEES, |
|
|
|
|
'predicate-member': PREDICATE_MEMBERS, |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
const predicateTranslations = {}; |
|
|
|
|
Object.entries(predicates).forEach(([category, catPreds]) => { |
|
|
|
|
predicateTranslations[category] = {}; |
|
|
|
|
Object.entries(catPreds).forEach(([tag, value]) => { |
|
|
|
|
predicateTranslations[category][TAPi18n.__(tag)] = value; |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
// console.log('predicateTranslations:', predicateTranslations);
|
|
|
|
|
|
|
|
|
|
const operatorMap = {}; |
|
|
|
|
Object.entries(operators).forEach(([key, value]) => { |
|
|
|
|
operatorMap[TAPi18n.__(key).toLowerCase()] = value; |
|
|
|
|
}); |
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
// console.log('operatorMap:', operatorMap);
|
|
|
|
|
|
|
|
|
|
const params = new QueryParams(); |
|
|
|
|
let text = ''; |
|
|
|
|
while (query) { |
|
|
|
|
let 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.toLowerCase(); |
|
|
|
|
} |
|
|
|
|
// eslint-disable-next-line no-prototype-builtins
|
|
|
|
|
if (operatorMap.hasOwnProperty(op)) { |
|
|
|
|
const operator = operatorMap[op]; |
|
|
|
|
let value = m.groups.value; |
|
|
|
|
if (operator === OPERATOR_LABEL) { |
|
|
|
|
if (value in this.colorMap) { |
|
|
|
|
value = this.colorMap[value]; |
|
|
|
|
// console.log('found color:', value);
|
|
|
|
|
} |
|
|
|
|
} else if ( |
|
|
|
|
[OPERATOR_DUE, OPERATOR_CREATED_AT, OPERATOR_MODIFIED_AT].includes( |
|
|
|
|
operator, |
|
|
|
|
) |
|
|
|
|
) { |
|
|
|
|
const days = parseInt(value, 10); |
|
|
|
|
let duration = null; |
|
|
|
|
if (isNaN(days)) { |
|
|
|
|
// duration was specified as text
|
|
|
|
|
if (predicateTranslations.durations[value]) { |
|
|
|
|
duration = predicateTranslations.durations[value]; |
|
|
|
|
let date = null; |
|
|
|
|
switch (duration) { |
|
|
|
|
case PREDICATE_WEEK: |
|
|
|
|
// eslint-disable-next-line no-case-declarations
|
|
|
|
|
const week = moment().week(); |
|
|
|
|
if (week === 52) { |
|
|
|
|
date = moment(1, 'W'); |
|
|
|
|
date.set('year', date.year() + 1); |
|
|
|
|
} else { |
|
|
|
|
date = moment(week + 1, 'W'); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case PREDICATE_MONTH: |
|
|
|
|
// eslint-disable-next-line no-case-declarations
|
|
|
|
|
const month = moment().month(); |
|
|
|
|
// .month() is zero indexed
|
|
|
|
|
if (month === 11) { |
|
|
|
|
date = moment(1, 'M'); |
|
|
|
|
date.set('year', date.year() + 1); |
|
|
|
|
} else { |
|
|
|
|
date = moment(month + 2, 'M'); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case PREDICATE_QUARTER: |
|
|
|
|
// eslint-disable-next-line no-case-declarations
|
|
|
|
|
const quarter = moment().quarter(); |
|
|
|
|
if (quarter === 4) { |
|
|
|
|
date = moment(1, 'Q'); |
|
|
|
|
date.set('year', date.year() + 1); |
|
|
|
|
} else { |
|
|
|
|
date = moment(quarter + 1, 'Q'); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case PREDICATE_YEAR: |
|
|
|
|
date = moment(moment().year() + 1, 'YYYY'); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
if (date) { |
|
|
|
|
value = { |
|
|
|
|
operator: '$lt', |
|
|
|
|
value: date.format('YYYY-MM-DD'), |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} else if ( |
|
|
|
|
operator === OPERATOR_DUE && |
|
|
|
|
value === PREDICATE_OVERDUE |
|
|
|
|
) { |
|
|
|
|
value = { |
|
|
|
|
operator: '$lt', |
|
|
|
|
value: moment().format('YYYY-MM-DD'), |
|
|
|
|
}; |
|
|
|
|
} else { |
|
|
|
|
this.parsingErrors.addError(OPERATOR_DUE, { |
|
|
|
|
tag: 'operator-number-expected', |
|
|
|
|
value: { operator: op, value }, |
|
|
|
|
}); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} else if (operator === OPERATOR_DUE) { |
|
|
|
|
value = { |
|
|
|
|
operator: '$lt', |
|
|
|
|
value: moment(moment().format('YYYY-MM-DD')) |
|
|
|
|
.add(days + 1, duration ? duration : 'days') |
|
|
|
|
.format(), |
|
|
|
|
}; |
|
|
|
|
} else { |
|
|
|
|
value = { |
|
|
|
|
operator: '$gte', |
|
|
|
|
value: moment(moment().format('YYYY-MM-DD')) |
|
|
|
|
.subtract(days, duration ? duration : 'days') |
|
|
|
|
.format(), |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} else if (operator === OPERATOR_SORT) { |
|
|
|
|
let negated = false; |
|
|
|
|
const m = value.match(reNegatedOperator); |
|
|
|
|
if (m) { |
|
|
|
|
value = m.groups.operator; |
|
|
|
|
negated = true; |
|
|
|
|
} |
|
|
|
|
if (!predicateTranslations.sorts[value]) { |
|
|
|
|
this.parsingErrors.addError(OPERATOR_SORT, { |
|
|
|
|
tag: 'operator-sort-invalid', |
|
|
|
|
value, |
|
|
|
|
}); |
|
|
|
|
continue; |
|
|
|
|
} else { |
|
|
|
|
value = { |
|
|
|
|
name: predicateTranslations.sorts[value], |
|
|
|
|
order: negated ? ORDER_DESCENDING : ORDER_ASCENDING, |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} else if (operator === OPERATOR_STATUS) { |
|
|
|
|
if (!predicateTranslations.status[value]) { |
|
|
|
|
this.parsingErrors.addError(OPERATOR_STATUS, { |
|
|
|
|
tag: 'operator-status-invalid', |
|
|
|
|
value, |
|
|
|
|
}); |
|
|
|
|
continue; |
|
|
|
|
} else { |
|
|
|
|
value = predicateTranslations.status[value]; |
|
|
|
|
} |
|
|
|
|
} else if (operator === OPERATOR_HAS) { |
|
|
|
|
let negated = false; |
|
|
|
|
const m = value.match(reNegatedOperator); |
|
|
|
|
if (m) { |
|
|
|
|
value = m.groups.operator; |
|
|
|
|
negated = true; |
|
|
|
|
} |
|
|
|
|
if (!predicateTranslations.has[value]) { |
|
|
|
|
this.parsingErrors.addError(OPERATOR_HAS, { |
|
|
|
|
tag: 'operator-has-invalid', |
|
|
|
|
value, |
|
|
|
|
}); |
|
|
|
|
continue; |
|
|
|
|
} else { |
|
|
|
|
value = { |
|
|
|
|
field: predicateTranslations.has[value], |
|
|
|
|
exists: !negated, |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} else if (operator === OPERATOR_LIMIT) { |
|
|
|
|
const limit = parseInt(value, 10); |
|
|
|
|
if (isNaN(limit) || limit < 1) { |
|
|
|
|
this.parsingErrors.addError(OPERATOR_LIMIT, { |
|
|
|
|
tag: 'operator-limit-invalid', |
|
|
|
|
value, |
|
|
|
|
}); |
|
|
|
|
continue; |
|
|
|
|
} else { |
|
|
|
|
value = limit; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
params.addPredicate(operator, value); |
|
|
|
|
} else { |
|
|
|
|
this.parsingErrors.addError(OPERATOR_UNKNOWN, { |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
const query = new Query(); |
|
|
|
|
query.buildParams(queryText); |
|
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
// console.log('text:', text);
|
|
|
|
|
params.text = text; |
|
|
|
|
// console.log('params:', query.getParams());
|
|
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
console.log('params:', params); |
|
|
|
|
this.queryParams = query.getParams(); |
|
|
|
|
|
|
|
|
|
this.queryParams = params; |
|
|
|
|
|
|
|
|
|
if (this.parsingErrors.hasErrors()) { |
|
|
|
|
if (query.hasErrors()) { |
|
|
|
|
this.searching.set(false); |
|
|
|
|
this.queryErrors = this.parsingErrorMessages(); |
|
|
|
|
this.queryErrors = query.errors(); |
|
|
|
|
this.hasResults.set(true); |
|
|
|
|
this.hasQueryErrors.set(true); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.runGlobalSearch(params.getParams()); |
|
|
|
|
this.runGlobalSearch(query.getParams()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
searchInstructions() { |
|
|
|
|