diff --git a/application/Espo/Resources/i18n/en_US/Global.json b/application/Espo/Resources/i18n/en_US/Global.json index ed75ed3267..796f3409c3 100644 --- a/application/Espo/Resources/i18n/en_US/Global.json +++ b/application/Espo/Resources/i18n/en_US/Global.json @@ -430,7 +430,8 @@ "searchRanges": { "is": "Is", "isEmpty": "Is Empty", - "isNotEmpty": "Is Not Empty" + "isNotEmpty": "Is Not Empty", + "isOneOf": "Is One Of" }, "varcharSearchRanges": { "equals": "Equals", diff --git a/frontend/client/res/templates/fields/link/search.tpl b/frontend/client/res/templates/fields/link/search.tpl index 48b8498460..b6adee9a87 100644 --- a/frontend/client/res/templates/fields/link/search.tpl +++ b/frontend/client/res/templates/fields/link/search.tpl @@ -12,4 +12,16 @@ +
+ diff --git a/frontend/client/src/views/fields/link-multiple.js b/frontend/client/src/views/fields/link-multiple.js index e039707e6e..139a386420 100644 --- a/frontend/client/src/views/fields/link-multiple.js +++ b/frontend/client/src/views/fields/link-multiple.js @@ -26,19 +26,19 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -Espo.define('Views.Fields.LinkMultiple', 'Views.Fields.Base', function (Dep) { +Espo.define('views/fields/link-multiple', 'views/fields/base', function (Dep) { return Dep.extend({ type: 'linkMultiple', - listTemplate: 'fields.link-multiple.detail', + listTemplate: 'fields/link-multiple/detail', - detailTemplate: 'fields.link-multiple.detail', + detailTemplate: 'fields/link-multiple/detail', - editTemplate: 'fields.link-multiple.edit', + editTemplate: 'fields/link-multiple/edit', - searchTemplate: 'fields.link-multiple.search', + searchTemplate: 'fields/link-multiple/search', nameHashName: null, @@ -52,7 +52,7 @@ Espo.define('Views.Fields.LinkMultiple', 'Views.Fields.Base', function (Dep) { autocompleteDisabled: false, - selectRecordsViewName: 'Modals.SelectRecords', + selectRecordsViewName: 'views/modals/select-records', createDisabled: false, @@ -115,7 +115,7 @@ Espo.define('Views.Fields.LinkMultiple', 'Views.Fields.Base', function (Dep) { }, function (dialog) { dialog.render(); self.notify(false); - dialog.once('select', function (models) { + this.listenToOnce(dialog, 'select', function (models) { if (Object.prototype.toString.call(models) !== '[object Array]') { models = [models]; } @@ -123,7 +123,7 @@ Espo.define('Views.Fields.LinkMultiple', 'Views.Fields.Base', function (Dep) { self.addLink(model.id, model.get('name')); }); }); - }); + }, this); }); this.events['click a[data-action="clearLink"]'] = function (e) { @@ -151,6 +151,12 @@ Espo.define('Views.Fields.LinkMultiple', 'Views.Fields.Base', function (Dep) { if (this.mode == 'edit' || this.mode == 'search') { this.$element = this.$el.find('input.main-element'); + var $element = this.$element; + + $element.on('change', function () { + $element.val(''); + }); + if (!this.autocompleteDisabled) { this.$element.autocomplete({ serviceUrl: function (q) { @@ -181,25 +187,18 @@ Espo.define('Views.Fields.LinkMultiple', 'Views.Fields.Base', function (Dep) { this.$element.val(''); }.bind(this) }); + + + this.once('render', function () { + $element.autocomplete('dispose'); + }, this); + + this.once('remove', function () { + $element.autocomplete('dispose'); + }, this); } - - var $element = this.$element; - - $element.on('change', function () { - $element.val(''); - }); - - this.once('render', function () { - $element.autocomplete('dispose'); - }, this); - - this.once('remove', function () { - $element.autocomplete('dispose'); - }, this); - this.renderLinks(); - } }, @@ -234,11 +233,11 @@ Espo.define('Views.Fields.LinkMultiple', 'Views.Fields.Base', function (Dep) { }, addLinkHtml: function (id, name) { - var container = this.$el.find('.link-container'); + var $container = this.$el.find('.link-container'); var $el = $('').addClass('link-' + id).addClass('list-group-item'); $el.html(name + ' '); $el.append(''); - container.append($el); + $container.append($el); return $el; }, diff --git a/frontend/client/src/views/fields/link.js b/frontend/client/src/views/fields/link.js index c15d948e70..a686191f1d 100644 --- a/frontend/client/src/views/fields/link.js +++ b/frontend/client/src/views/fields/link.js @@ -106,6 +106,39 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) { this.clearLink(); }); } + + if (this.mode == 'search') { + this.addActionHandler('selectLinkOneOf', function () { + this.notify('Loading...'); + + var viewName = this.getMetadata().get('clientDefs.' + this.foreignScope + '.modalViews.select') || this.selectRecordsViewName; + + this.createView('dialog', viewName, { + scope: this.foreignScope, + createButton: !this.createDisabled && this.mode != 'search', + filters: this.getSelectFilters(), + boolFilterList: this.getSelectBoolFilterList(), + primaryFilterName: this.getSelectPrimaryFilterName(), + multiple: true + }, function (view) { + view.render(); + this.notify(false); + this.listenToOnce(view, 'select', function (models) { + if (Object.prototype.toString.call(models) !== '[object Array]') { + models = [models]; + } + models.forEach(function (model) { + this.addLinkOneOf(model.id, model.get('name')); + }, this); + }); + }, this); + }); + + this.events['click a[data-action="clearLinkOneOf"]'] = function (e) { + var id = $(e.currentTarget).data('id').toString(); + this.deleteLinkOneOf(id); + }; + } }, select: function (model) { @@ -121,7 +154,10 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) { }, setupSearch: function () { - this.searchParams.typeOptions = ['is', 'isEmpty', 'isNotEmpty']; + this.searchParams.typeOptions = ['is', 'isEmpty', 'isNotEmpty', 'isOneOf']; + this.searchParams.oneOfIdList = this.searchParams.oneOfIdList || []; + this.searchParams.oneOfNameHash = this.searchParams.oneOfNameHash || {}; + this.events = _.extend({ 'change select.search-type': function (e) { var type = $(e.currentTarget).val(); @@ -131,10 +167,16 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) { }, handleSearchType: function (type) { - if (~['isEmpty', 'isNotEmpty'].indexOf(type)) { - this.$el.find('div.primary').addClass('hidden'); - } else { + if (~['is'].indexOf(type)) { this.$el.find('div.primary').removeClass('hidden'); + } else { + this.$el.find('div.primary').addClass('hidden'); + } + + if (type === 'isOneOf') { + this.$el.find('div.one-of-container').removeClass('hidden'); + } else { + this.$el.find('div.one-of-container').addClass('hidden'); } }, @@ -177,6 +219,7 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) { }.bind(this)); } + var $elementName = this.$elementName; if (!this.autocompleteDisabled) { this.$elementName.autocomplete({ @@ -212,28 +255,75 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) { }, this); }.bind(this) }); - } - var $elementName = this.$elementName; + this.once('render', function () { + $elementName.autocomplete('dispose'); + }, this); + + this.once('remove', function () { + $elementName.autocomplete('dispose'); + }, this); + + + if (this.mode == 'search') { + var $elementOneOf = this.$el.find('input.element-one-of'); + $elementOneOf.autocomplete({ + serviceUrl: function (q) { + return this.getAutocompleteUrl(q); + }.bind(this), + minChars: 1, + paramName: 'q', + formatResult: function (suggestion) { + return suggestion.name; + }, + transformResult: function (response) { + var response = JSON.parse(response); + var list = []; + response.list.forEach(function(item) { + list.push({ + id: item.id, + name: item.name, + data: item.id, + value: item.name + }); + }, this); + return { + suggestions: list + }; + }.bind(this), + onSelect: function (s) { + this.addLinkOneOf(s.id, s.name); + $elementOneOf.val(''); + }.bind(this) + }); + + + this.once('render', function () { + $elementOneOf.autocomplete('dispose'); + }, this); + + this.once('remove', function () { + $elementOneOf.autocomplete('dispose'); + }, this); + } + } $elementName.on('change', function () { if (!this.model.get(this.idName)) { $elementName.val(this.model.get(this.nameName)); } }.bind(this)); - - this.once('render', function () { - $elementName.autocomplete('dispose'); - }, this); - - this.once('remove', function () { - $elementName.autocomplete('dispose'); - }, this); } if (this.mode == 'search') { var type = this.$el.find('select.search-type').val(); this.handleSearchType(type); + + if (type == 'isOneOf') { + this.searchParams.oneOfIdList.forEach(function (id) { + this.addLinkOneOfHtml(id, this.searchParams.oneOfNameHash[id]); + }, this); + } } }, @@ -251,6 +341,38 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) { } }, + deleteLinkOneOf: function (id) { + this.deleteLinkOneOfHtml(id); + + var index = this.searchParams.oneOfIdList.indexOf(id); + if (index > -1) { + this.searchParams.oneOfIdList.splice(index, 1); + } + delete this.searchParams.oneOfNameHash[id]; + }, + + addLinkOneOf: function (id, name) { + if (!~this.searchParams.oneOfIdList.indexOf(id)) { + this.searchParams.oneOfIdList.push(id); + this.searchParams.oneOfNameHash[id] = name; + this.addLinkOneOfHtml(id, name); + } + }, + + deleteLinkOneOfHtml: function (id) { + this.$el.find('.link-container .link-' + id).remove(); + }, + + addLinkOneOfHtml: function (id, name) { + var $container = this.$el.find('.link-container'); + var $el = $('').addClass('link-' + id).addClass('list-group-item'); + $el.html(name + ' '); + $el.append(''); + $container.append($el); + + return $el; + }, + fetch: function () { var data = {}; data[this.nameName] = this.$el.find('[name="'+this.nameName+'"]').val() || null; @@ -277,6 +399,17 @@ Espo.define('views/fields/link', 'views/fields/base', function (Dep) { field: this.idName }; return data; + } else if (type == 'isOneOf') { + var data = { + type: 'in', + typeFront: type, + field: this.idName, + value: this.searchParams.oneOfIdList, + oneOfIdList: this.searchParams.oneOfIdList, + oneOfNameHash: this.searchParams.oneOfNameHash + }; + return data; + } else { if (!value) { return false; diff --git a/frontend/client/src/views/record/search.js b/frontend/client/src/views/record/search.js index b763969df3..07ee6fc4f8 100644 --- a/frontend/client/src/views/record/search.js +++ b/frontend/client/src/views/record/search.js @@ -26,11 +26,11 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -Espo.define('Views.Record.Search', 'View', function (Dep) { +Espo.define('views/record/search', 'view', function (Dep) { return Dep.extend({ - template: 'record.search', + template: 'record/search', scope: null, @@ -270,25 +270,6 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { this.textFilter = ''; this.selectPreset(this.presetName, true); - - /*if (!this.searchManager.emptyOnReset) { - this.textFilter = ''; - this.selectPreset(this.presetName); - } else { - - this.removeFilters(); - - this.presetName = null; - - this.searchManager.reset(); - this.loadSearchData(); - - this.createFilters(function () { - this.render(); - }.bind(this)); - - this.updateCollection(); - }*/ }, savePreset: function (name) { diff --git a/frontend/client/src/views/search/filter.js b/frontend/client/src/views/search/filter.js index bdb453391f..f86bf76724 100644 --- a/frontend/client/src/views/search/filter.js +++ b/frontend/client/src/views/search/filter.js @@ -24,40 +24,40 @@ * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. - ************************************************************************/ + ************************************************************************/ -Espo.define('Views.Search.Filter', 'View', function (Dep) { +Espo.define('views/search/filter', 'view', function (Dep) { return Dep.extend({ - template: 'search.filter', - + template: 'search/filter', + data: function () { return { name: this.name, scope: this.model.name, notRemovable: this.options.notRemovable }; - }, - + }, + setup: function () { - var name = this.name = this.options.name; + var name = this.name = this.options.name; var type = this.model.getFieldType(name); - - if (type) { + + if (type) { var viewName = this.model.getFieldParam(name, 'view') || 'Fields.' + Espo.Utils.upperCaseFirst(type); - + this.createView('field', viewName, { mode: 'search', model: this.model, el: this.options.el + ' .field', defs: { - name: name, + name: name, }, searchParams: this.options.params, }); } }, - }); + }); });