diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/element/helpers/gridColumnConfig.js b/bundles/AdminBundle/Resources/public/js/pimcore/element/helpers/gridColumnConfig.js index 2f89843c51c..114ce787d5e 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/element/helpers/gridColumnConfig.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/element/helpers/gridColumnConfig.js @@ -263,6 +263,7 @@ pimcore.element.helpers.gridColumnConfig = { var menu = grid.headerCt.getMenu(); menu.add(columnConfig); // + var batchAllMenu = new Ext.menu.Item({ text: t("batch_change"), iconCls: "pimcore_icon_table pimcore_icon_overlay_go", @@ -330,11 +331,28 @@ pimcore.element.helpers.gridColumnConfig = { }); menu.add(batchRemoveSelectedMenu); + var filterByRelationMenu = new Ext.menu.Item({ + text: t("filter_by_relation"), + iconCls: "pimcore_icon_filter pimcore_icon_overlay_add", + handler: function (grid) { + var menu = grid.headerCt.getMenu(); + var column = menu.activeHeader; + this.filterPrepare(column); + }.bind(this, grid) + }); + menu.add(filterByRelationMenu); + // menu.on('beforeshow', function (batchAllMenu, batchSelectedMenu, grid) { var menu = grid.headerCt.getMenu(); var columnDataIndex = menu.activeHeader.dataIndex; + if (menu.activeHeader.config && typeof menu.activeHeader.config.getRelationFilter === "function") { + filterByRelationMenu.show(); + } else { + filterByRelationMenu.hide(); + } + // no batch for system properties if (Ext.Array.contains(this.systemColumns, columnDataIndex) || Ext.Array.contains(this.noBatchColumns, columnDataIndex)) { batchAllMenu.hide(); @@ -362,6 +380,84 @@ pimcore.element.helpers.gridColumnConfig = { }.bind(this, batchAllMenu, batchSelectedMenu, grid)); }, + filterPrepare: function (column) { + var dataIndexName = column.dataIndex + var gridColumns = this.grid.getColumns(); + var columnIndex = -1; + for (let i = 0; i < gridColumns.length; i++) { + let dataIndex = gridColumns[i].dataIndex; + if (dataIndex == dataIndexName) { + columnIndex = i; + break; + } + } + if (columnIndex < 0) { + return; + } + + if (this.systemColumns.indexOf(gridColumns[columnIndex].dataIndex) > -1) { + return; + } + + var fieldInfo = this.grid.getColumns()[columnIndex].config; + + if((this.objecttype === "object") || (this.objecttype === "variant")) { + if (!fieldInfo.layout || !fieldInfo.layout.layout) { + return; + } + + var tagType = fieldInfo.layout.type; + var editor = new pimcore.object.tags[tagType](null, fieldInfo.layout.layout); + editor.setObject(this.object); + } + + editor.updateContext({ + containerType: "filterByRelationWindow" + }); + + var formPanel = Ext.create('Ext.form.Panel', { + xtype: "form", + border: false, + items: [editor.getLayoutEdit()], + bodyStyle: "padding: 10px;", + buttons: [ + { + text: t("clear_relation_filter"), + iconCls: "pimcore_icon_filter_condition pimcore_icon_overlay_delete", + handler: function () { + this.filterByRelationWindow.close(); + this.grid.store.filters.removeByKey("x-gridfilter-"+fieldInfo.dataIndex); + }.bind(this) + }, + { + text: t("apply_filter"), + iconCls: "pimcore_icon_filter pimcore_icon_overlay_add", + handler: function () { + if (formPanel.isValid() && typeof fieldInfo.getRelationFilter === "function") { + this.grid.filters.getStore().addFilter( + fieldInfo.getRelationFilter(fieldInfo.dataIndex, editor) + ); + this.filterByRelationWindow.close(); + } + }.bind(this) + } + ] + }); + + var title = t("filter_by_relation_field") + " " + fieldInfo.text; + this.filterByRelationWindow = new Ext.Window({ + autoScroll: true, + modal: false, + title: title, + items: [formPanel], + bodyStyle: "background: #fff;", + width: 700, + maxHeight: 650 + }); + this.filterByRelationWindow.show(); + this.filterByRelationWindow.updateLayout(); + }, + batchPrepare: function (column, onlySelected, append, remove) { var dataIndexName = column.dataIndex var gridColumns = this.grid.getColumns(); diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/helpers.js b/bundles/AdminBundle/Resources/public/js/pimcore/helpers.js index cea7b82b65c..b99e984dd50 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/helpers.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/helpers.js @@ -2538,7 +2538,7 @@ pimcore.helpers.requestNicePathDataGridDecorator = function (gridView, targets) }; pimcore.helpers.requestNicePathData = function (source, targets, config, fieldConfig, context, decorator, responseHandler) { - if (context && context['containerType'] == "batch") { + if (context && (context['containerType'] == "batch" || context['containerType'] == "filterByRelationWindow")) { return; } diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/object/helpers/gridTabAbstract.js b/bundles/AdminBundle/Resources/public/js/pimcore/object/helpers/gridTabAbstract.js index 7ece2a15d1f..63b288f5a6b 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/helpers/gridTabAbstract.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/helpers/gridTabAbstract.js @@ -207,6 +207,7 @@ pimcore.object.helpers.gridTabAbstract = Class.create({ } ); + this.grid.getStore().clearFilter(); this.grid.filters.clearFilters(); this.pagingtoolbar.moveFirst(); @@ -232,6 +233,7 @@ pimcore.object.helpers.gridTabAbstract = Class.create({ ); proxy.setExtraParam("condition", this.sqlEditor.getValue()); if (this.grid && this.grid.filters) { + this.grid.getStore().clearFilter(); this.grid.filters.clearFilters(); } @@ -295,6 +297,7 @@ pimcore.object.helpers.gridTabAbstract = Class.create({ text: t("clear_filters"), tooltip: t("clear_filters"), handler: function (button) { + this.grid.getStore().clearFilter(); this.grid.filters.clearFilters(); this.toolbarFilterInfo.hide(); this.clearFilterButton.hide(); @@ -312,6 +315,7 @@ pimcore.object.helpers.gridTabAbstract = Class.create({ listeners: { "change": function (field, checked) { this.grid.getStore().setRemoteFilter(false); + this.grid.getStore().clearFilter(); this.grid.filters.clearFilters(); this.store.getProxy().setExtraParam("only_direct_children", checked); diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyObjectRelation.js b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyObjectRelation.js index 6a8aa174338..6a516f21780 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyObjectRelation.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyObjectRelation.js @@ -598,6 +598,7 @@ pimcore.object.tags.advancedManyToManyObjectRelation = Class.create(pimcore.obje return { text: t(field.label), width: 150, sortable: false, dataIndex: field.key, getEditor: this.getWindowCellEditor.bind(this, field), + getRelationFilter: this.getRelationFilter, renderer: pimcore.object.helpers.grid.prototype.advancedRelationGridRenderer.bind(this, field, "fullpath") }; }, diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyRelation.js b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyRelation.js index 8f37c31fe69..211e8c761d9 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyRelation.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyRelation.js @@ -940,10 +940,31 @@ pimcore.object.tags.advancedManyToManyRelation = Class.create(pimcore.object.tag return { text: t(field.label), width: 150, sortable: false, dataIndex: field.key, getEditor: this.getWindowCellEditor.bind(this, field), - renderer: pimcore.object.helpers.grid.prototype.advancedRelationGridRenderer.bind(this, field, "path") + getRelationFilter: this.getRelationFilter, + renderer: pimcore.object.helpers.grid.prototype.advancedRelationGridRenderer.bind(this, field, "path"), }; }, + getRelationFilter: function (dataIndex, editor) { + var filterValues = editor.store.getData().items; + if (!filterValues || !Array.isArray(filterValues) || !filterValues.length) { + filterValues = null; + } else { + filterValues = filterValues.map(function (item) { + return item.data.type + "|" + item.data.id; + }).join(','); + } + + return new Ext.util.Filter({ + operator: "like", + type: "string", + id: "x-gridfilter-" + dataIndex, + property: dataIndex, + dataIndex: dataIndex, + value: filterValues + }); + }, + getCellEditValue: function () { return this.getValue(); } diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/hotspotimage.js b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/hotspotimage.js index 196ec33e281..e38411bceff 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/hotspotimage.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/hotspotimage.js @@ -54,6 +54,7 @@ pimcore.object.tags.hotspotimage = Class.create(pimcore.object.tags.image, { return { text: t(field.label), width: 100, sortable: false, dataIndex: field.key, getEditor: this.getWindowCellEditor.bind(this, field), + getRelationFilter: this.getRelationFilter, renderer: function (key, value, metaData, record, rowIndex, colIndex, store, view) { this.applyPermissionStyle(key, value, metaData, record); @@ -92,6 +93,18 @@ pimcore.object.tags.hotspotimage = Class.create(pimcore.object.tags.image, { }; }, + getRelationFilter: function (dataIndex, editor) { + var filterValue = editor.data && editor.data.id !== undefined ? editor.data.id : null; + return new Ext.util.Filter({ + operator: "=", + type: "int", + id: "x-gridfilter-" + dataIndex, + property: dataIndex, + dataIndex: dataIndex, + value: filterValue + }); + }, + getLayoutEdit: function () { if (intval(this.fieldConfig.width) < 1) { diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/image.js b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/image.js index b5acc4c43cf..3ffac32eb57 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/image.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/image.js @@ -32,6 +32,7 @@ pimcore.object.tags.image = Class.create(pimcore.object.tags.abstract, { return { text: t(field.label), width: 100, sortable: false, dataIndex: field.key, getEditor: this.getWindowCellEditor.bind(this, field), + getRelationFilter: this.getRelationFilter, renderer: function (key, value, metaData, record, rowIndex, colIndex, store, view) { this.applyPermissionStyle(key, value, metaData, record); @@ -67,6 +68,18 @@ pimcore.object.tags.image = Class.create(pimcore.object.tags.abstract, { }; }, + getRelationFilter: function (dataIndex, editor) { + var filterValue = editor.data && editor.data.id !== undefined ? editor.data.id : null; + return new Ext.util.Filter({ + operator: "=", + type: "int", + id: "x-gridfilter-" + dataIndex, + property: dataIndex, + dataIndex: dataIndex, + value: filterValue + }); + }, + getLayoutEdit: function () { if (!this.fieldConfig.width) { this.fieldConfig.width = 300; diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToManyObjectRelation.js b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToManyObjectRelation.js index 6c3f3aa87de..79ab8a0415a 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToManyObjectRelation.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToManyObjectRelation.js @@ -88,10 +88,31 @@ pimcore.object.tags.manyToManyObjectRelation = Class.create(pimcore.object.tags. return result.join("
"); } }.bind(this, field.key), + getRelationFilter: this.getRelationFilter, getEditor: this.getWindowCellEditor.bind(this, field) }; }, + getRelationFilter: function (dataIndex, editor) { + var filterValues = editor.store.getData().items; + if (!filterValues || !Array.isArray(filterValues) || !filterValues.length) { + filterValues = null; + } else { + filterValues = filterValues.map(function (item) { + return item.data.id; + }).join(','); + } + + return new Ext.util.Filter({ + operator: "like", + type: "string", + id: "x-gridfilter-" + dataIndex, + property: dataIndex, + dataIndex: dataIndex, + value: filterValues + }); + }, + openParentSearchEditor: function () { pimcore.helpers.itemselector(false, function (selection) { this.parentField.setValue(selection.fullpath); diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToManyRelation.js b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToManyRelation.js index ac56874fa9d..fecbb96c3c0 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToManyRelation.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToManyRelation.js @@ -76,6 +76,7 @@ pimcore.object.tags.manyToManyRelation = Class.create(pimcore.object.tags.abstra return { text: t(field.label), width: 150, sortable: false, dataIndex: field.key, getEditor: this.getWindowCellEditor.bind(this, field), + getRelationFilter: this.getRelationFilter, renderer: function (key, value, metaData, record) { this.applyPermissionStyle(key, value, metaData, record); @@ -100,6 +101,26 @@ pimcore.object.tags.manyToManyRelation = Class.create(pimcore.object.tags.abstra }; }, + getRelationFilter: function (dataIndex, editor) { + var filterValues = editor.store.getData().items; + if (!filterValues || !Array.isArray(filterValues) || !filterValues.length) { + filterValues = null; + } else { + filterValues = filterValues.map(function (item) { + return item.data.type + "|" + item.data.id; + }).join(','); + } + + return new Ext.util.Filter({ + operator: "like", + type: "string", + id: "x-gridfilter-" + dataIndex, + property: dataIndex, + dataIndex: dataIndex, + value: filterValues + }); + }, + getLayoutEdit: function () { var autoHeight = false; if (!this.fieldConfig.height) { diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToOneRelation.js b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToOneRelation.js index fff6dac7d42..739d08dc87d 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToOneRelation.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToOneRelation.js @@ -55,10 +55,22 @@ pimcore.object.tags.manyToOneRelation = Class.create(pimcore.object.tags.abstrac return { text: t(field.label), sortable: false, dataIndex: field.key, renderer: renderer, + getRelationFilter: this.getRelationFilter, getEditor: this.getWindowCellEditor.bind(this, field) }; }, + getRelationFilter: function (dataIndex, editor) { + var filterValue = editor.data && editor.data.id !== undefined ? editor.data.type + "|" + editor.data.id : null; + return new Ext.util.Filter({ + operator: "=", + type: "int", + id: "x-gridfilter-" + dataIndex, + property: dataIndex, + dataIndex: dataIndex, + value: filterValue + }); + }, getLayoutEdit: function () { diff --git a/bundles/CoreBundle/Resources/translations/en.json b/bundles/CoreBundle/Resources/translations/en.json index 74f5a671407..204c9240416 100644 --- a/bundles/CoreBundle/Resources/translations/en.json +++ b/bundles/CoreBundle/Resources/translations/en.json @@ -953,6 +953,10 @@ "selected_grid_columns": "Selected grid columns", "ungrouped": "ungrouped", "reindex_warning": "Changing it from alphabetical sort order to index will reindex all items. Do you want to continue?", + "filter_by_relation": "Filter by relation", + "filter_by_relation_field": "Filter by relation field", + "clear_relation_filter": "Clear active relation filter", + "apply_filter": "Apply filter", "config_not_writeable": "Not writable, this can have several reasons. For details please have a look into the docs.", "sites": "Sites" -} \ No newline at end of file +} diff --git a/models/DataObject/ClassDefinition/Data/Extension/RelationFilterConditionParser.php b/models/DataObject/ClassDefinition/Data/Extension/RelationFilterConditionParser.php new file mode 100644 index 00000000000..74d0df8903b --- /dev/null +++ b/models/DataObject/ClassDefinition/Data/Extension/RelationFilterConditionParser.php @@ -0,0 +1,53 @@ +name; + $name .= '__image'; + return $this->getRelationFilterCondition($value, $operator, $name); + } + } diff --git a/models/DataObject/ClassDefinition/Data/Image.php b/models/DataObject/ClassDefinition/Data/Image.php index 2e2ecd5c154..5fa0e1c1599 100644 --- a/models/DataObject/ClassDefinition/Data/Image.php +++ b/models/DataObject/ClassDefinition/Data/Image.php @@ -26,6 +26,7 @@ class Image extends Data implements ResourcePersistenceAwareInterface, QueryReso use Extension\ColumnType; use ImageTrait; use Extension\QueryColumnType; + use Data\Extension\RelationFilterConditionParser; /** * Static type of this element @@ -391,4 +392,17 @@ public function denormalize($value, $params = []) return Asset\Image::getById($value['id']); } } + + /** + * Filter by relation feature + * @param array|string|null $value + * @param string $operator + * @param array $params + * @return string + */ + public function getFilterConditionExt($value, $operator, $params = []) + { + $name = $params['name'] ?: $this->name; + return $this->getRelationFilterCondition($value, $operator, $name); + } } diff --git a/models/DataObject/ClassDefinition/Data/ManyToManyObjectRelation.php b/models/DataObject/ClassDefinition/Data/ManyToManyObjectRelation.php index 53d5ca8956e..958a3b2e9bb 100644 --- a/models/DataObject/ClassDefinition/Data/ManyToManyObjectRelation.php +++ b/models/DataObject/ClassDefinition/Data/ManyToManyObjectRelation.php @@ -28,7 +28,7 @@ class ManyToManyObjectRelation extends AbstractRelations implements QueryResourc use Extension\QueryColumnType; use DataObject\ClassDefinition\Data\Relations\AllowObjectRelationTrait; use DataObject\ClassDefinition\Data\Relations\ManyToManyRelationTrait; - + use DataObject\ClassDefinition\Data\Extension\RelationFilterConditionParser; /** * Static type of this element * @@ -897,4 +897,17 @@ public function addListingFilter(DataObject\Listing $listing, $data, $operator = return parent::addListingFilter($listing, $data, $operator); } + + /** + * Filter by relation feature + * @param array|string|null $value + * @param string $operator + * @param array $params + * @return string + */ + public function getFilterConditionExt($value, $operator, $params = []) + { + $name = $params['name'] ?: $this->name; + return $this->getRelationFilterCondition($value, $operator, $name); + } } diff --git a/models/DataObject/ClassDefinition/Data/ManyToManyRelation.php b/models/DataObject/ClassDefinition/Data/ManyToManyRelation.php index 2309b55b180..cd15ff95bff 100644 --- a/models/DataObject/ClassDefinition/Data/ManyToManyRelation.php +++ b/models/DataObject/ClassDefinition/Data/ManyToManyRelation.php @@ -31,7 +31,7 @@ class ManyToManyRelation extends AbstractRelations implements QueryResourcePersi use DataObject\ClassDefinition\Data\Relations\AllowAssetRelationTrait; use DataObject\ClassDefinition\Data\Relations\AllowDocumentRelationTrait; use DataObject\ClassDefinition\Data\Relations\ManyToManyRelationTrait; - + use DataObject\ClassDefinition\Data\Extension\RelationFilterConditionParser; /** * Static type of this element * @@ -965,4 +965,17 @@ public function addListingFilter(DataObject\Listing $listing, $data, $operator = throw new \InvalidArgumentException('Filtering '.__CLASS__.' does only support "=" operator'); } + + /** + * Filter by relation feature + * @param array|string|null $value + * @param string $operator + * @param array $params + * @return string + */ + public function getFilterConditionExt($value, $operator, $params = []) + { + $name = $params['name'] ?: $this->name; + return $this->getRelationFilterCondition($value, $operator, $name); + } } diff --git a/models/DataObject/ClassDefinition/Data/ManyToOneRelation.php b/models/DataObject/ClassDefinition/Data/ManyToOneRelation.php index 00957f4e631..3a90d7449a4 100644 --- a/models/DataObject/ClassDefinition/Data/ManyToOneRelation.php +++ b/models/DataObject/ClassDefinition/Data/ManyToOneRelation.php @@ -31,6 +31,7 @@ class ManyToOneRelation extends AbstractRelations implements QueryResourcePersis use DataObject\ClassDefinition\Data\Relations\AllowObjectRelationTrait; use DataObject\ClassDefinition\Data\Relations\AllowAssetRelationTrait; use DataObject\ClassDefinition\Data\Relations\AllowDocumentRelationTrait; + use DataObject\ClassDefinition\Data\Extension\RelationFilterConditionParser; /** * Static type of this element @@ -661,4 +662,25 @@ public function getPhpdocReturnType(): ?string return null; } + + /** + * Filter by relation feature + * @param array|string|null $value + * @param string $operator + * @param array $params + * @return string + */ + public function getFilterConditionExt($value, $operator, $params = []) + { + $name = $params['name'] . '__id'; + if (preg_match('/^(asset|object|document)\|(\d+)/', $value, $matches)) { + $typeField = $params['name'] . '__type'; + $typeCondition = '`' . $typeField . '` = ' . "'" . $matches[1] . "'"; + $value = $matches[2]; + + return '(' . $typeCondition . ' AND ' . $this->getRelationFilterCondition($value, $operator, $name) . ')'; + } + + return $this->getRelationFilterCondition($value, $operator, $name); + } }