diff --git a/locales/default.pot b/locales/default.pot index f0bc0574b..a90838d04 100644 --- a/locales/default.pot +++ b/locales/default.pot @@ -1609,6 +1609,21 @@ msgstr "" msgid "Error while creating new object." msgstr "" +msgid "Edit related objects" +msgstr "" + +msgid "Choose object type" +msgstr "" + +msgid "Choose relation" +msgstr "" + +msgid "Filter to apply" +msgstr "" + +msgid "Search related objects" +msgstr "" + msgid "Action" msgstr "" diff --git a/locales/en_US/default.po b/locales/en_US/default.po index 413e1e849..00dadeb67 100644 --- a/locales/en_US/default.po +++ b/locales/en_US/default.po @@ -1612,6 +1612,21 @@ msgstr "" msgid "Error while creating new object." msgstr "" +msgid "Edit related objects" +msgstr "" + +msgid "Choose object type" +msgstr "" + +msgid "Choose relation" +msgstr "" + +msgid "Filter to apply" +msgstr "" + +msgid "Search related objects" +msgstr "" + msgid "Action" msgstr "" diff --git a/locales/it_IT/default.po b/locales/it_IT/default.po index a95dc55f1..01df7a0ef 100644 --- a/locales/it_IT/default.po +++ b/locales/it_IT/default.po @@ -1631,6 +1631,21 @@ msgstr "Dato obbligatorio mancante \"${ required }\". Riprovare" msgid "Error while creating new object." msgstr "Si รจ verificato un errore durante la creazione del nuovo oggetto." +msgid "Edit related objects" +msgstr "Modifica oggetti correlati" + +msgid "Choose object type" +msgstr "Scegli il tipo di oggetto" + +msgid "Choose relation" +msgstr "Scegli la relazione" + +msgid "Filter to apply" +msgstr "Filtro da applicare" + +msgid "Search related objects" +msgstr "Cerca oggetti correlati" + msgid "Action" msgstr "Azione" diff --git a/resources/js/app/app.js b/resources/js/app/app.js index 24f1b3a85..746b02120 100644 --- a/resources/js/app/app.js +++ b/resources/js/app/app.js @@ -111,6 +111,7 @@ const _vueInstance = new Vue({ FieldTextarea: () => import(/* webpackChunkName: "field-textarea" */'app/components/form/field-textarea'), FieldTitle: () => import(/* webpackChunkName: "field-title" */'app/components/form/field-title'), ObjectInfo: () => import(/* webpackChunkName: "object-info" */'app/components/object-info/object-info'), + RelatedObjectsFilter: () => import(/* webpackChunkName: "related-objects-filter" */'app/components/related-objects-filter/related-objects-filter'), ModuleProperties: () => import(/* webpackChunkName: "module-properties" */'app/components/module/module-properties'), ModuleSetup: () => import(/* webpackChunkName: "module-setup" */'app/components/module/module-setup'), AddRelatedById: () => import(/* webpackChunkName: "add-related-by-id" */'app/components/add-related-by-id/add-related-by-id'), @@ -624,6 +625,7 @@ window._vueInstance = _vueInstance; // use component everywhere in Manager Vue.component('AppIcon', AppIcon); +Vue.component('ClipboardItem', _vueInstance.$options.components.ClipboardItem); Vue.component('DateRangesView', _vueInstance.$options.components.DateRangesView); Vue.component('FieldCheckbox', _vueInstance.$options.components.FieldCheckbox); Vue.component('FieldGeoCoordinates', _vueInstance.$options.components.FieldGeoCoordinates); @@ -643,5 +645,7 @@ Vue.component('ModuleSetup', _vueInstance.$options.components.ModuleSetup); Vue.component('ObjectCategories', _vueInstance.$options.components.ObjectCategories); Vue.component('ObjectCaptions', _vueInstance.$options.components.ObjectCaptions); Vue.component('ObjectInfo', _vueInstance.$options.components.ObjectInfo); +Vue.component('RelatedObjectsFilter', _vueInstance.$options.components.RelatedObjectsFilter); +Vue.component('Thumbnail', _vueInstance.$options.components.Thumbnail); Vue.component('RibbonItem', _vueInstance.$options.components.RibbonItem); Vue.component('UploadedObject', _vueInstance.$options.components.UploadedObject); diff --git a/resources/js/app/components/filter-box.vue b/resources/js/app/components/filter-box.vue index f113215f5..d612acb3e 100644 --- a/resources/js/app/components/filter-box.vue +++ b/resources/js/app/components/filter-box.vue @@ -38,6 +38,10 @@ export default { default: '[10]' }, filterActive: Boolean, + filterRelationsActive: { + type: Boolean, + default: false + }, filterList: { type: Array, default: () => [], @@ -81,7 +85,9 @@ export default { * When disabled, only direct children are fetched. * This will switch the API filter between `parent` and `ancestor`. */ + editFilterRelations: this.filterRelationsActive, filterByDescendants: false, + filterRelations: {}, folder: null, moreFilters: this.filterActive, pageSize: this.pagination.page_size, @@ -364,6 +370,10 @@ export default { const filter = this.prepareFilters(); const filterObject = { ...this.queryFilter, filter }; + filterObject.filter = { + ...filterObject.filter, + ...this.filterRelations, + }; this.availableFilters = this.filtersByType?.[filterObject?.filter?.type] || []; this.$emit('filter-objects', filterObject); }, @@ -461,6 +471,20 @@ export default { delete this.queryFilter.filter[oldFilter]; this.queryFilter.filter[newFilter] = this.folder?.id || null; }, + + toggleFilterRelations() { + this.editFilterRelations = !this.editFilterRelations; + }, + + updateFilterRelations(filter) { + this.filterRelations = {}; + for (const [key, value] of Object.entries(filter)) { + this.filterRelations[key] = []; + for (const v of value) { + this.filterRelations[key].push(v); + } + } + }, } }; diff --git a/resources/js/app/components/object-info/object-info.vue b/resources/js/app/components/object-info/object-info.vue index 1d0739910..eb0ce58fa 100644 --- a/resources/js/app/components/object-info/object-info.vue +++ b/resources/js/app/components/object-info/object-info.vue @@ -57,7 +57,7 @@ export default { fillData() { const source = BEDITA?.indexLists?.[this.reloadedData?.type] || {}; this.fields = source || ['title', 'description']; - this.fields = this.fields.filter((value, index, array) => { + this.fields = this.fields?.filter((value, index, array) => { return array.indexOf(value) === index; }); for (const field of this.fields) { diff --git a/resources/js/app/components/related-objects-filter/related-objects-filter.vue b/resources/js/app/components/related-objects-filter/related-objects-filter.vue new file mode 100644 index 000000000..3fb122353 --- /dev/null +++ b/resources/js/app/components/related-objects-filter/related-objects-filter.vue @@ -0,0 +1,133 @@ + + + diff --git a/resources/js/app/components/related-objects-filter/related-objects-panel.vue b/resources/js/app/components/related-objects-filter/related-objects-panel.vue new file mode 100644 index 000000000..81cdec1f1 --- /dev/null +++ b/resources/js/app/components/related-objects-filter/related-objects-panel.vue @@ -0,0 +1,401 @@ + + + diff --git a/resources/js/app/components/related-objects-filter/results-pagination.vue b/resources/js/app/components/related-objects-filter/results-pagination.vue new file mode 100644 index 000000000..34f22e311 --- /dev/null +++ b/resources/js/app/components/related-objects-filter/results-pagination.vue @@ -0,0 +1,137 @@ + + diff --git a/resources/js/app/components/related-objects-filter/search-result.vue b/resources/js/app/components/related-objects-filter/search-result.vue new file mode 100644 index 000000000..1ebf018db --- /dev/null +++ b/resources/js/app/components/related-objects-filter/search-result.vue @@ -0,0 +1,133 @@ + + + diff --git a/src/View/Helper/ArrayHelper.php b/src/View/Helper/ArrayHelper.php index 92482e9c5..5f292c2c5 100644 --- a/src/View/Helper/ArrayHelper.php +++ b/src/View/Helper/ArrayHelper.php @@ -81,4 +81,16 @@ public function extract($data, $path): array { return (array)Hash::extract($data, $path); } + + /** + * Return intersection of two arrays reindexing values. + * + * @param array $arr1 First array. + * @param array $arr2 Second array. + * @return array The intersection of two arrays. + */ + public function intersect(array $arr1, array $arr2): array + { + return array_values(array_intersect($arr1, $arr2)); + } } diff --git a/templates/Element/FilterBox/filter_box.twig b/templates/Element/FilterBox/filter_box.twig index cf887d72b..b81d926cd 100644 --- a/templates/Element/FilterBox/filter_box.twig +++ b/templates/Element/FilterBox/filter_box.twig @@ -1,7 +1,7 @@
{% if not hideFilter %}
- {{ element('FilterBox/filter_box_common', { showFilterSearchByType }) }} + {{ element('FilterBox/filter_box_common', { showFilterSearchByType, hideFilterRelations }) }}
{% endif %} diff --git a/templates/Element/FilterBox/filter_box_common.twig b/templates/Element/FilterBox/filter_box_common.twig index ad8775d4f..88f759f6e 100644 --- a/templates/Element/FilterBox/filter_box_common.twig +++ b/templates/Element/FilterBox/filter_box_common.twig @@ -58,12 +58,34 @@ {{ __('Reset') }} + {% if hideFilterRelations == '' %} + + {% endif %} + + +
+ + + {% if hideFilterRelations == '' %} +
+
+ +
+ {% endif %}
{# object type filter #} diff --git a/templates/Element/Form/relation.twig b/templates/Element/Form/relation.twig index 655342939..4cb6ed074 100644 --- a/templates/Element/Form/relation.twig +++ b/templates/Element/Form/relation.twig @@ -44,7 +44,7 @@ @filter-reset="reloadObjects" inline-template > - {{ element('FilterBox/filter_box') }} + {{ element('FilterBox/filter_box', {'hideFilterRelations': true}) }}
diff --git a/templates/Element/Modules/index_header.twig b/templates/Element/Modules/index_header.twig index 6a61dae36..5ba3c0720 100644 --- a/templates/Element/Modules/index_header.twig +++ b/templates/Element/Modules/index_header.twig @@ -1,12 +1,14 @@
{% set filterActive = (_view.request.getQuery('filter') or _view.request.getQuery('q')) %} {% set list = Schema.filterList(filter | default([]), schema.properties) %} + {% set relationsFilter = Array.intersect(_view.request.getQuery('filter')|keys|default([]), schema.relations|keys|default([])) %} - {{ element('FilterBox/filter_box', { 'showFilterSearchByType': true }) }} + {{ element('FilterBox/filter_box', { 'showFilterSearchByType': true, 'hideFilterRelations': true }) }}
diff --git a/tests/TestCase/View/Helper/ArrayHelperTest.php b/tests/TestCase/View/Helper/ArrayHelperTest.php index 5b75f4d07..ce3fc5233 100644 --- a/tests/TestCase/View/Helper/ArrayHelperTest.php +++ b/tests/TestCase/View/Helper/ArrayHelperTest.php @@ -199,4 +199,19 @@ public function testExtract(): void $actual = $this->Array->extract($data, $pattern); static::assertSame($expected, $actual); } + + /** + * Test `intersect()` method. + * + * @return void + * @covers ::intersect() + */ + public function testIntersect(): void + { + $a = ['a', 'b', 'c', 'd']; + $b = ['c', 'd', 'e', 'f']; + $expected = ['c', 'd']; + $actual = $this->Array->intersect($a, $b); + static::assertSame($expected, $actual); + } }