From 1d162f219a3caabd09ef5248e565bf4ee020e9e5 Mon Sep 17 00:00:00 2001 From: Blackbit Date: Fri, 2 Dec 2022 10:16:46 +0200 Subject: [PATCH 01/24] [Docs] Mention migrating document editable naming strategy (#13713) * Mention migrating document editable naming strategy * Mention migrating document editable naming strategy * How to check document editable naming strategy --- .../07_Updating_Pimcore/10_V6_to_V10.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Development_Documentation/23_Installation_and_Upgrade/07_Updating_Pimcore/10_V6_to_V10.md b/doc/Development_Documentation/23_Installation_and_Upgrade/07_Updating_Pimcore/10_V6_to_V10.md index 73172ca77bd..74c8ca47b75 100644 --- a/doc/Development_Documentation/23_Installation_and_Upgrade/07_Updating_Pimcore/10_V6_to_V10.md +++ b/doc/Development_Documentation/23_Installation_and_Upgrade/07_Updating_Pimcore/10_V6_to_V10.md @@ -17,7 +17,7 @@ - If you're using REST Webservices or the PHP templating engine, upgrade to [Datahub](https://github.com/pimcore/data-hub) and [Twig templates](https://twig.symfony.com/doc/3.x/) or consider using our [LTS offering](https://pimcore.com/en/services/lts) which brings back this functionality as optional commercial bundles. -- In case you did not adapt to the new editable naming strategy since Pimcore 5, you need to do this in Pimcore 6 before updating to Pimcore X. Follow [this guide](https://pimcore.com/docs/pimcore/6.9/Development_Documentation/Documents/Editable_Naming_Strategies.html) +- In case `bin/console debug:config pimcore documents.editables.naming_strategy` returns `legacy`, you did not adapt to the new editable naming strategy since Pimcore 5. Please migrate in Pimcore 6 before updating to Pimcore X, otherwise all your document editables will lose their data. Follow [this guide](https://pimcore.com/docs/pimcore/6.9/Development_Documentation/Documents/Editable_Naming_Strategies.html) ## IMPORTANT CHANGES TO DO PRIOR THE UPDATE! (TO DO WITH PHP < 8.0) @@ -97,7 +97,7 @@ mv app/AppKernel.php src/Kernel.php ### Add .env environment file Add [.env](https://github.com/pimcore/skeleton/blob/10.2/.env) file to project root, if not exists already. -### Migrate Php templates to Twig +### Migrate PHP templates to Twig Since Pimcore X supports only Symfony 5, which dropped the support for Php templates, it is required to update your php templates to Twig. If you are using enterprise edition, then it is still possible to support Php templates by installing `pimcore/php-templating-engine-bundle`. From d4af20f2a1f2a6a37f101f91742f53023c3daa9d Mon Sep 17 00:00:00 2001 From: Divesh Pahuja Date: Fri, 2 Dec 2022 13:57:01 +0100 Subject: [PATCH 02/24] Fix test env session config (#13643) --- bundles/CoreBundle/Resources/config/pimcore/test.yaml | 2 -- composer.json | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bundles/CoreBundle/Resources/config/pimcore/test.yaml b/bundles/CoreBundle/Resources/config/pimcore/test.yaml index dc98a7a4049..e47df1f7987 100644 --- a/bundles/CoreBundle/Resources/config/pimcore/test.yaml +++ b/bundles/CoreBundle/Resources/config/pimcore/test.yaml @@ -1,7 +1,5 @@ framework: test: ~ - session: - storage_factory_id: session.storage.factory.mock_file profiler: false mailer: transports: diff --git a/composer.json b/composer.json index 1e0fc1b7d1f..41372c7f21b 100644 --- a/composer.json +++ b/composer.json @@ -166,7 +166,8 @@ "symfony/symfony": "*", "symfony/monolog-bundle": "3.6.0", "doctrine/doctrine-migrations-bundle": "3.1.0", - "sabre/dav": "4.2.2" + "sabre/dav": "4.2.2", + "thecodingmachine/safe": "<2.0" }, "require-dev": { "codeception/codeception": "^4.1.12", From f13ceb9c1170153c02a4b47f46b35e51c40d48a3 Mon Sep 17 00:00:00 2001 From: Sebastian Blank Date: Wed, 30 Nov 2022 09:04:00 +0100 Subject: [PATCH 03/24] Fix: Saving an unpublished snippet results in HTTP 500 --- .../Admin/Document/NewsletterController.php | 13 ++++++++----- .../Controller/Admin/Document/SnippetController.php | 13 ++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/bundles/AdminBundle/Controller/Admin/Document/NewsletterController.php b/bundles/AdminBundle/Controller/Admin/Document/NewsletterController.php index 6370555eca9..c2f41fd4fc5 100644 --- a/bundles/AdminBundle/Controller/Admin/Document/NewsletterController.php +++ b/bundles/AdminBundle/Controller/Admin/Document/NewsletterController.php @@ -115,11 +115,14 @@ public function saveAction(Request $request): JsonResponse 'treeData' => $treeData, ]); } else { - $draftData = [ - 'id' => $version->getId(), - 'modificationDate' => $version->getDate(), - 'isAutoSave' => $version->isAutoSave(), - ]; + $draftData = []; + if ($version) { + $draftData = [ + 'id' => $version->getId(), + 'modificationDate' => $version->getDate(), + 'isAutoSave' => $version->isAutoSave(), + ]; + } return $this->adminJson(['success' => true, 'draft' => $draftData]); } diff --git a/bundles/AdminBundle/Controller/Admin/Document/SnippetController.php b/bundles/AdminBundle/Controller/Admin/Document/SnippetController.php index b4248e6909a..205f1f0b4ad 100644 --- a/bundles/AdminBundle/Controller/Admin/Document/SnippetController.php +++ b/bundles/AdminBundle/Controller/Admin/Document/SnippetController.php @@ -129,11 +129,14 @@ public function saveAction(Request $request) } else { $this->saveToSession($snippet); - $draftData = [ - 'id' => $version->getId(), - 'modificationDate' => $version->getDate(), - 'isAutoSave' => $version->isAutoSave(), - ]; + $draftData = []; + if ($version) { + $draftData = [ + 'id' => $version->getId(), + 'modificationDate' => $version->getDate(), + 'isAutoSave' => $version->isAutoSave(), + ]; + } return $this->adminJson(['success' => true, 'draft' => $draftData]); } From 4f476328570738270892bda7d785d21617012dbe Mon Sep 17 00:00:00 2001 From: Matthias S <38959016+mattamon@users.noreply.github.com> Date: Tue, 6 Dec 2022 12:10:35 +0100 Subject: [PATCH 04/24] [Fix] adding validation message for wrong upload type (#13726) * [Fix] adding validation message for wrong upload type * Fix: adjusting exception message Co-authored-by: Divesh Pahuja * Adding type and failure call to uploadDialog * Use getType() method instead of property Co-authored-by: Divesh Pahuja * Removing type property Co-authored-by: Divesh Pahuja Co-authored-by: Divesh Pahuja --- .../Controller/Admin/Asset/AssetController.php | 11 +++++++++++ .../public/js/pimcore/document/editables/image.js | 12 +++++++++++- .../Resources/public/js/pimcore/helpers.js | 6 +++++- .../Resources/public/js/pimcore/object/tags/image.js | 2 +- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/bundles/AdminBundle/Controller/Admin/Asset/AssetController.php b/bundles/AdminBundle/Controller/Admin/Asset/AssetController.php index 1d03fa4655a..2f047dcfdd0 100644 --- a/bundles/AdminBundle/Controller/Admin/Asset/AssetController.php +++ b/bundles/AdminBundle/Controller/Admin/Asset/AssetController.php @@ -527,6 +527,17 @@ protected function addAsset(Request $request, Config $config) throw new \Exception('Something went wrong, please check upload_max_filesize and post_max_size in your php.ini as well as the write permissions of your temporary directories.'); } + // check if there is a requested type and if matches the asset type of the uploaded file + $type = $request->get('type'); + if($type) { + $mimetype = MimeTypes::getDefault()->guessMimeType($sourcePath); + $assetType = Asset::getTypeFromMimeMapping($mimetype, $filename); + + if($type !== $assetType) { + throw new \Exception("Mime type does not match with asset type: $type"); + } + } + if ($request->get('allowOverwrite') && Asset\Service::pathExists($parentAsset->getRealFullPath().'/'.$filename)) { $asset = Asset::getByPath($parentAsset->getRealFullPath().'/'.$filename); $asset->setStream(fopen($sourcePath, 'rb', false, File::getContext())); diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/document/editables/image.js b/bundles/AdminBundle/Resources/public/js/pimcore/document/editables/image.js index 4b915b4d1fe..4788c3a7f39 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/document/editables/image.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/document/editables/image.js @@ -242,7 +242,17 @@ pimcore.document.editables.image = Class.create(pimcore.document.editable, { } catch (e) { console.log(e); } - }.bind(this)); + }.bind(this), + function (res) { + const response = Ext.decode(res.response.responseText); + if (response && response.success === false) { + pimcore.helpers.showNotification(t("error"), response.message, "error", + res.response.responseText); + } else { + pimcore.helpers.showNotification(t("error"), res, "error", + res.response.responseText); + } + }.bind(this), [] ,this.getType()); }, onNodeOver: function(target, dd, e, data) { diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/helpers.js b/bundles/AdminBundle/Resources/public/js/pimcore/helpers.js index b1b4a88cc93..21cbfac30b6 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/helpers.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/helpers.js @@ -949,7 +949,7 @@ pimcore.helpers.openMemorizedTabs = function () { } }; -pimcore.helpers.assetSingleUploadDialog = function (parent, parentType, success, failure, context) { +pimcore.helpers.assetSingleUploadDialog = function (parent, parentType, success, failure, context, type) { var params = {}; params['parent' + ucfirst(parentType)] = parent; @@ -959,6 +959,10 @@ pimcore.helpers.assetSingleUploadDialog = function (parent, parentType, success, url += "&context=" + Ext.encode(context); } + if(type) { + url += "&type=" + type; + } + pimcore.helpers.uploadDialog(url, 'Filedata', success, failure); }; 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 79fdc6fc867..fedd81ed67f 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/image.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/image.js @@ -275,7 +275,7 @@ pimcore.object.tags.image = Class.create(pimcore.object.tags.abstract, { pimcore.helpers.showNotification(t("error"), res, "error", res.response.responseText); } - }.bind(this), this.context); + }.bind(this), this.context, this.type); }, addDataFromSelector: function (item) { From 235dbc84a5541a0bdb5d028e23d3c82720d400d6 Mon Sep 17 00:00:00 2001 From: dvesh3 Date: Tue, 6 Dec 2022 11:12:24 +0000 Subject: [PATCH 05/24] Apply php-cs-fixer changes --- .../AdminBundle/Controller/Admin/Asset/AssetController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/AdminBundle/Controller/Admin/Asset/AssetController.php b/bundles/AdminBundle/Controller/Admin/Asset/AssetController.php index 2f047dcfdd0..76003ac26a0 100644 --- a/bundles/AdminBundle/Controller/Admin/Asset/AssetController.php +++ b/bundles/AdminBundle/Controller/Admin/Asset/AssetController.php @@ -529,11 +529,11 @@ protected function addAsset(Request $request, Config $config) // check if there is a requested type and if matches the asset type of the uploaded file $type = $request->get('type'); - if($type) { + if ($type) { $mimetype = MimeTypes::getDefault()->guessMimeType($sourcePath); $assetType = Asset::getTypeFromMimeMapping($mimetype, $filename); - if($type !== $assetType) { + if ($type !== $assetType) { throw new \Exception("Mime type does not match with asset type: $type"); } } From c711ae1a84679b8d4fe6575c39c28fe41e59fcff Mon Sep 17 00:00:00 2001 From: dvesh3 Date: Tue, 6 Dec 2022 14:38:39 +0100 Subject: [PATCH 06/24] [Documents] Fix non-reloading areabricks editable to have correct translation - resovles #13695 --- .../Controller/Admin/Document/PageController.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bundles/AdminBundle/Controller/Admin/Document/PageController.php b/bundles/AdminBundle/Controller/Admin/Document/PageController.php index 07fdfc06892..a776855aae8 100644 --- a/bundles/AdminBundle/Controller/Admin/Document/PageController.php +++ b/bundles/AdminBundle/Controller/Admin/Document/PageController.php @@ -22,6 +22,7 @@ use Pimcore\Document\StaticPageGenerator; use Pimcore\Http\Request\Resolver\DocumentResolver; use Pimcore\Http\Request\Resolver\EditmodeResolver; +use Pimcore\Localization\LocaleService; use Pimcore\Messenger\GeneratePagePreviewMessage; use Pimcore\Model\Document; use Pimcore\Model\Document\Targeting\TargetingDocumentInterface; @@ -390,6 +391,7 @@ public function qrCodeAction(Request $request): BinaryFileResponse * @param Environment $twig * @param EditableRenderer $editableRenderer * @param DocumentResolver $documentResolver + * @param LocaleService $localeService * * @return JsonResponse * @@ -402,7 +404,8 @@ public function areabrickRenderIndexEditmode( EditmodeEditableDefinitionCollector $definitionCollector, Environment $twig, EditableRenderer $editableRenderer, - DocumentResolver $documentResolver + DocumentResolver $documentResolver, + LocaleService $localeService ): JsonResponse { $blockStateStackData = json_decode($request->get('blockStateStack'), true); $blockStateStack->loadArray($blockStateStackData); @@ -423,6 +426,9 @@ public function areabrickRenderIndexEditmode( // so we use the attribute as a workaround $request->attributes->set(EditmodeResolver::ATTRIBUTE_EDITMODE, true); + // setting locale manually here before rendering, to make sure editables use the right locale from document + $localeService->setLocale($document->getProperty('language')); + $areaBlockConfig = json_decode($request->get('areablockConfig'), true); /** @var Document\Editable\Areablock $areablock */ $areablock = $editableRenderer->getEditable($document, 'areablock', $request->get('realName'), $areaBlockConfig, true); From 365f89cb60f3be34d11700a7279a2faef567bd7b Mon Sep 17 00:00:00 2001 From: Divesh Pahuja Date: Tue, 6 Dec 2022 15:26:11 +0100 Subject: [PATCH 07/24] Fix translate button doesn't work in DocumentTypes & PredefinedProperties UI - resolves #13712 (#13745) --- .../Resources/public/js/pimcore/settings/docTypes.js | 6 +++--- .../public/js/pimcore/settings/properties/predefined.js | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/settings/docTypes.js b/bundles/AdminBundle/Resources/public/js/pimcore/settings/docTypes.js index 93e4cdb9f93..b57353f933e 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/settings/docTypes.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/settings/docTypes.js @@ -243,11 +243,11 @@ pimcore.settings.document.doctypes = Class.create({ handler: function (grid, rowIndex) { var rec = grid.getStore().getAt(rowIndex); try { - pimcore.globalmanager.get("translationadminmanager").activate(rec.data.name); + pimcore.globalmanager.get("translationdomainmanager").activate(rec.data.name); } catch (e) { - pimcore.globalmanager.add("translationadminmanager", - new pimcore.settings.translation.admin(rec.data.name)); + pimcore.globalmanager.add("translationdomainmanager", + new pimcore.settings.translation.domain("admin",rec.data.name)); } }.bind(this) }] diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/settings/properties/predefined.js b/bundles/AdminBundle/Resources/public/js/pimcore/settings/properties/predefined.js index 7f16ad3c0e7..0316ee055e6 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/settings/properties/predefined.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/settings/properties/predefined.js @@ -180,11 +180,10 @@ pimcore.settings.properties.predefined = Class.create({ handler: function(grid, rowIndex){ var rec = grid.getStore().getAt(rowIndex); try { - pimcore.globalmanager.get("translationadminmanager").activate(rec.data.name); - } - catch (e) { - pimcore.globalmanager.add("translationadminmanager", - new pimcore.settings.translation.admin(rec.data.name)); + pimcore.globalmanager.get("translationdomainmanager").activate(rec.data.name); + } catch (e) { + pimcore.globalmanager.add("translationdomainmanager", + new pimcore.settings.translation.domain("admin", rec.data.name)); } }.bind(this) }] From 9afb57b111d9b0d7eec2e50df9affc47661fd0ba Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 7 Dec 2022 09:27:50 +0100 Subject: [PATCH 08/24] fixed undefined array key issue when exporting localized objectbricks (#13747) --- models/DataObject/Service.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/models/DataObject/Service.php b/models/DataObject/Service.php index 7884bd75dd5..e69091e011e 100644 --- a/models/DataObject/Service.php +++ b/models/DataObject/Service.php @@ -2062,6 +2062,10 @@ protected static function getCsvFieldData($fallbackLanguage, $field, $object, $r if ($brickDescriptor) { $innerContainer = $brickDescriptor['innerContainer'] ?? 'localizedfields'; $value = $brick->{'get' . ucfirst($innerContainer)}(); + + if($value instanceof Localizedfield){ + $params['language'] = $requestedLanguage; + } } return $fieldDefinition->getForCsvExport($value, $params); From 78da1fb194bf53f646c6a8de9bb51e7b8f53861c Mon Sep 17 00:00:00 2001 From: mcop1 Date: Wed, 7 Dec 2022 08:29:40 +0000 Subject: [PATCH 09/24] Apply php-cs-fixer changes --- models/DataObject/Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/DataObject/Service.php b/models/DataObject/Service.php index e69091e011e..b9632246d5f 100644 --- a/models/DataObject/Service.php +++ b/models/DataObject/Service.php @@ -2063,7 +2063,7 @@ protected static function getCsvFieldData($fallbackLanguage, $field, $object, $r $innerContainer = $brickDescriptor['innerContainer'] ?? 'localizedfields'; $value = $brick->{'get' . ucfirst($innerContainer)}(); - if($value instanceof Localizedfield){ + if ($value instanceof Localizedfield) { $params['language'] = $requestedLanguage; } } From df53663ad46ae1e92022e808b16d085072f23b7c Mon Sep 17 00:00:00 2001 From: storbahn Date: Wed, 7 Dec 2022 09:30:56 +0100 Subject: [PATCH 10/24] [BUGFIX] Check of advancedManyToManyRelation and manyToManyRelation Upload Limit (validate maxItems) (#13703) --- .../public/js/pimcore/object/tags/advancedManyToManyRelation.js | 2 +- .../public/js/pimcore/object/tags/manyToManyRelation.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 85fbdf8e8ff..65680fab440 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyRelation.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyRelation.js @@ -789,7 +789,7 @@ pimcore.object.tags.advancedManyToManyRelation = Class.create(pimcore.object.tag uploadDialog: function () { if (!this.fieldConfig.allowMultipleAssignments || (this.fieldConfig["maxItems"] && this.fieldConfig["maxItems"] >= 1)) { - if ((this.store.getData().getSource() || this.store.getData()).count() >= this.fieldConfig.maxItems) { + if (this.fieldConfig.maxItems && (this.store.getData().getSource() || this.store.getData()).count() >= this.fieldConfig.maxItems) { Ext.Msg.alert(t("error"), t("limit_reached")); return true; } 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 161a7ca6218..4605bce981b 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToManyRelation.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/manyToManyRelation.js @@ -452,7 +452,7 @@ pimcore.object.tags.manyToManyRelation = Class.create(pimcore.object.tags.abstra uploadDialog: function () { if (!this.fieldConfig.allowMultipleAssignments || (this.fieldConfig["maxItems"] && this.fieldConfig["maxItems"] >= 1)) { - if ((this.store.getData().getSource() || this.store.getData()).count() >= this.fieldConfig.maxItems) { + if (this.fieldConfig.maxItems && (this.store.getData().getSource() || this.store.getData()).count() >= this.fieldConfig.maxItems) { Ext.Msg.alert(t("error"), t("limit_reached")); return true; } From 7e0e6e17df7aa2f35d9e06e531cfdc2d6e7f1745 Mon Sep 17 00:00:00 2001 From: Blackbit Date: Wed, 7 Dec 2022 12:16:44 +0200 Subject: [PATCH 11/24] render "number" meta fields of advanced relations with correct decimal separator (#13714) --- .../object/tags/advancedManyToManyObjectRelation.js | 12 ++++++++---- .../object/tags/advancedManyToManyRelation.js | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) 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 a51ebe60a3d..22e2e5b7b96 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyObjectRelation.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyObjectRelation.js @@ -155,10 +155,14 @@ pimcore.object.tags.advancedManyToManyObjectRelation = Class.create(pimcore.obje var renderer = null; var listeners = null; - if (this.fieldConfig.columns[i].type == "number" && !readOnly) { - cellEditor = function() { - return new Ext.form.NumberField({}); - }.bind(); + if (this.fieldConfig.columns[i].type == "number") { + if(!readOnly) { + cellEditor = function () { + return new Ext.form.NumberField({}); + }.bind(); + } + + renderer = Ext.util.Format.numberRenderer(); } else if (this.fieldConfig.columns[i].type == "text" && !readOnly) { cellEditor = function() { return new Ext.form.TextField({}); 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 65680fab440..602ad11508b 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyRelation.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/advancedManyToManyRelation.js @@ -113,10 +113,14 @@ pimcore.object.tags.advancedManyToManyRelation = Class.create(pimcore.object.tag var renderer = null; var listeners = null; - if (this.fieldConfig.columns[i].type == "number" && !readOnly) { - cellEditor = function () { - return new Ext.form.NumberField({}); - }; + if (this.fieldConfig.columns[i].type == "number") { + if(!readOnly) { + cellEditor = function () { + return new Ext.form.NumberField({}); + }; + } + + renderer = Ext.util.Format.numberRenderer(); } else if (this.fieldConfig.columns[i].type == "text" && !readOnly) { cellEditor = function () { return new Ext.form.TextField({}); From 9fe902e06261a403ee7b983208b6c2801811ea0f Mon Sep 17 00:00:00 2001 From: vg <64212374+bgpack@users.noreply.github.com> Date: Wed, 7 Dec 2022 11:41:02 +0100 Subject: [PATCH 12/24] handle the case where the 'containerType' of context is not set (#13697) --- models/DataObject/Localizedfield/Dao.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/DataObject/Localizedfield/Dao.php b/models/DataObject/Localizedfield/Dao.php index d73592f5dc8..b909d50b0cb 100644 --- a/models/DataObject/Localizedfield/Dao.php +++ b/models/DataObject/Localizedfield/Dao.php @@ -76,7 +76,7 @@ public function getQueryTableName() { $context = $this->model->getContext(); if ($context) { - $containerType = $context['containerType']; + $containerType = $context['containerType'] ?? null; if ($containerType == 'objectbrick') { $containerKey = $context['containerKey']; From a090365a51268d153b60abd0f798422600e5578d Mon Sep 17 00:00:00 2001 From: robertSt7 <104770750+robertSt7@users.noreply.github.com> Date: Wed, 7 Dec 2022 11:45:11 +0100 Subject: [PATCH 13/24] Fix: Wrongfully inheritance of object bricks (#13690) --- .../public/js/pimcore/object/tags/objectbricks.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/objectbricks.js b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/objectbricks.js index 29d0cad544a..9877591b7d5 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/objectbricks.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/tags/objectbricks.js @@ -18,6 +18,7 @@ pimcore.object.tags.objectbricks = Class.create(pimcore.object.tags.abstract, { dirty: false, addedTypes: {}, preventDelete: {}, + inheritedCount: 0, initialize: function (data, fieldConfig) { @@ -236,7 +237,12 @@ pimcore.object.tags.objectbricks = Class.create(pimcore.object.tags.abstract, { if (!this.layoutDefinitions[type]) { return; } - if (this.fieldConfig.maxItems && this.getCurrentElementsCount() >= this.fieldConfig.maxItems) { + + if(blockData && blockData.inherited) { + this.inheritedCount++; + } + + if (this.fieldConfig.maxItems && this.getCurrentElementsCount() >= this.fieldConfig.maxItems + this.inheritedCount) { Ext.Msg.alert(t("error"), t("limit_reached")); return; } From 20ed632f4544fc5bd56cdceae50f17962d179e7f Mon Sep 17 00:00:00 2001 From: Sebastian Blank Date: Wed, 7 Dec 2022 12:10:02 +0100 Subject: [PATCH 14/24] [Bug]: strip_tags needs empty string as default value, cannot handle null (#13757) * [Bug]: strip_tags needs empty string as default value, cannot handle null * Fix code style --- .../Controller/Admin/LoginController.php | 2 +- .../Controller/Admin/TagsController.php | 16 ++++++++-------- .../Authenticator/AdminAbstractAuthenticator.php | 2 +- .../Authenticator/AdminLoginAuthenticator.php | 2 +- .../Security/Guard/AdminAuthenticator.php | 2 +- .../09_Working_with_Prices/07_Vouchers.md | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bundles/AdminBundle/Controller/Admin/LoginController.php b/bundles/AdminBundle/Controller/Admin/LoginController.php index 358950f3d92..c15996b83fe 100644 --- a/bundles/AdminBundle/Controller/Admin/LoginController.php +++ b/bundles/AdminBundle/Controller/Admin/LoginController.php @@ -242,7 +242,7 @@ public function deeplinkAction(Request $request, EventDispatcherInterface $event if (preg_match('/(document|asset|object)_([0-9]+)_([a-z]+)/', $queryString, $deeplink)) { $deeplink = $deeplink[0]; - $perspective = strip_tags($request->get('perspective')); + $perspective = strip_tags($request->get('perspective', '')); if (strpos($queryString, 'token')) { $event = new LoginRedirectEvent('pimcore_admin_login', [ diff --git a/bundles/AdminBundle/Controller/Admin/TagsController.php b/bundles/AdminBundle/Controller/Admin/TagsController.php index bd05ca90b56..df6ae3a7a57 100644 --- a/bundles/AdminBundle/Controller/Admin/TagsController.php +++ b/bundles/AdminBundle/Controller/Admin/TagsController.php @@ -42,7 +42,7 @@ public function addAction(Request $request) { try { $tag = new Tag(); - $tag->setName(strip_tags($request->get('text'))); + $tag->setName(strip_tags($request->get('text', ''))); $tag->setParentId((int)$request->get('parentId')); $tag->save(); @@ -91,7 +91,7 @@ public function updateAction(Request $request) $tag->setParentId((int)$parentId); } if ($request->get('text')) { - $tag->setName(strip_tags($request->get('text'))); + $tag->setName(strip_tags($request->get('text', ''))); } $tag->save(); @@ -113,7 +113,7 @@ public function treeGetChildrenByIdAction(Request $request) { $showSelection = $request->get('showSelection') == 'true'; $assignmentCId = (int)$request->get('assignmentCId'); - $assignmentCType = strip_tags($request->get('assignmentCType')); + $assignmentCType = strip_tags($request->get('assignmentCType', '')); $recursiveChildren = false; $assignedTagIds = []; @@ -209,7 +209,7 @@ protected function convertTagToArray(Tag $tag, $showSelection, $assignedTagIds, public function loadTagsForElementAction(Request $request) { $assginmentCId = (int)$request->get('assignmentCId'); - $assginmentCType = strip_tags($request->get('assignmentCType')); + $assginmentCType = strip_tags($request->get('assignmentCType', '')); $assignedTagArray = []; if ($assginmentCId && $assginmentCType) { @@ -233,7 +233,7 @@ public function loadTagsForElementAction(Request $request) public function addTagToElementAction(Request $request) { $assginmentCId = (int)$request->get('assignmentElementId'); - $assginmentCType = strip_tags($request->get('assignmentElementType')); + $assginmentCType = strip_tags($request->get('assignmentElementType', '')); $tagId = (int)$request->get('tagId'); $tag = Tag::getById($tagId); @@ -256,7 +256,7 @@ public function addTagToElementAction(Request $request) public function removeTagFromElementAction(Request $request) { $assginmentCId = (int)$request->get('assignmentElementId'); - $assginmentCType = strip_tags($request->get('assignmentElementType')); + $assginmentCType = strip_tags($request->get('assignmentElementType', '')); $tagId = (int)$request->get('tagId'); $tag = Tag::getById($tagId); @@ -280,7 +280,7 @@ public function removeTagFromElementAction(Request $request) public function getBatchAssignmentJobsAction(Request $request, EventDispatcherInterface $eventDispatcher) { $elementId = (int)$request->get('elementId'); - $elementType = strip_tags($request->get('elementType')); + $elementType = strip_tags($request->get('elementType', '')); $idList = []; switch ($elementType) { @@ -423,7 +423,7 @@ private function getSubDocumentIds(\Pimcore\Model\Document $document, EventDispa */ public function doBatchAssignmentAction(Request $request) { - $cType = strip_tags($request->get('elementType')); + $cType = strip_tags($request->get('elementType', '')); $assignedTags = json_decode($request->get('assignedTags')); $elementIds = json_decode($request->get('childrenIds')); $doCleanupTags = $request->get('removeAndApply') == 'true'; diff --git a/bundles/AdminBundle/Security/Authenticator/AdminAbstractAuthenticator.php b/bundles/AdminBundle/Security/Authenticator/AdminAbstractAuthenticator.php index 32b06909d01..e934588a6d1 100644 --- a/bundles/AdminBundle/Security/Authenticator/AdminAbstractAuthenticator.php +++ b/bundles/AdminBundle/Security/Authenticator/AdminAbstractAuthenticator.php @@ -116,7 +116,7 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token, } else { $url = $this->router->generate('pimcore_admin_index', [ '_dc' => time(), - 'perspective' => strip_tags($request->get('perspective')), + 'perspective' => strip_tags($request->get('perspective', '')), ]); } diff --git a/bundles/AdminBundle/Security/Authenticator/AdminLoginAuthenticator.php b/bundles/AdminBundle/Security/Authenticator/AdminLoginAuthenticator.php index 8d88a641f4a..a73a6434fbc 100644 --- a/bundles/AdminBundle/Security/Authenticator/AdminLoginAuthenticator.php +++ b/bundles/AdminBundle/Security/Authenticator/AdminLoginAuthenticator.php @@ -57,7 +57,7 @@ public function start(Request $request, AuthenticationException $authException = return $response; } - $event = new LoginRedirectEvent(self::PIMCORE_ADMIN_LOGIN, ['perspective' => strip_tags($request->get('perspective'))]); + $event = new LoginRedirectEvent(self::PIMCORE_ADMIN_LOGIN, ['perspective' => strip_tags($request->get('perspective', ''))]); $this->dispatcher->dispatch($event, AdminEvents::LOGIN_REDIRECT); $url = $this->router->generate($event->getRouteName(), $event->getRouteParams()); diff --git a/bundles/AdminBundle/Security/Guard/AdminAuthenticator.php b/bundles/AdminBundle/Security/Guard/AdminAuthenticator.php index 12f9755307b..bfc4c761952 100644 --- a/bundles/AdminBundle/Security/Guard/AdminAuthenticator.php +++ b/bundles/AdminBundle/Security/Guard/AdminAuthenticator.php @@ -331,7 +331,7 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token, } else { $url = $this->router->generate('pimcore_admin_index', [ '_dc' => time(), - 'perspective' => strip_tags($request->get('perspective')), + 'perspective' => strip_tags($request->get('perspective', '')), ]); } diff --git a/doc/Development_Documentation/10_E-Commerce_Framework/09_Working_with_Prices/07_Vouchers.md b/doc/Development_Documentation/10_E-Commerce_Framework/09_Working_with_Prices/07_Vouchers.md index 054b5bdec0b..bc3b5a7f59d 100644 --- a/doc/Development_Documentation/10_E-Commerce_Framework/09_Working_with_Prices/07_Vouchers.md +++ b/doc/Development_Documentation/10_E-Commerce_Framework/09_Working_with_Prices/07_Vouchers.md @@ -48,10 +48,10 @@ A voucher token is always applied to a cart. To do so, use following snippet. ```php get('voucher-code'))) { +if ($token = strip_tags($request->get('voucher-code', ''))) { try { $success = $cart->addVoucherToken($token); - if($success) { + if ($success) { $this->addFlash('success', $translator->trans('cart.voucher-code-added')); } else { $this->addFlash('danger', $translator->trans('cart.voucher-code-cound-not-be-added')); @@ -100,7 +100,7 @@ See an sample snippet to display the voucher information to the customer: ```twig
- {% if(cart.pricingManagerTokenInformationDetails | length > 0) %} + {% if (cart.pricingManagerTokenInformationDetails | length > 0) %}
    From 63e3213119fb3d772244ffd82e6ea0386aa68738 Mon Sep 17 00:00:00 2001 From: dvesh3 Date: Wed, 7 Dec 2022 12:09:13 +0100 Subject: [PATCH 15/24] Revert "Remove codeception/module-symfony (#11756)" This reverts commit fb48c4b0 --- composer.json | 1 + tests/_bootstrap.php | 1 - tests/_support/Helper/Pimcore.php | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 41372c7f21b..635cbdd63e2 100644 --- a/composer.json +++ b/composer.json @@ -171,6 +171,7 @@ }, "require-dev": { "codeception/codeception": "^4.1.12", + "codeception/module-symfony": "^1.6.0", "codeception/phpunit-wrapper": "^9", "phpstan/phpstan": "^1.9", "phpstan/phpstan-symfony": "^1.2.14", diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php index 02ea737b325..5a8430d4665 100644 --- a/tests/_bootstrap.php +++ b/tests/_bootstrap.php @@ -43,7 +43,6 @@ include PIMCORE_PROJECT_ROOT . '/vendor/autoload.php'; \Pimcore\Bootstrap::setProjectRoot(); \Pimcore\Bootstrap::bootstrap(); -\Pimcore\Bootstrap::kernel(); Autoloader::addNamespace('Pimcore\Model\DataObject', __DIR__ . '/_output/var/classes/DataObject'); Autoloader::addNamespace('Pimcore\Tests', __DIR__); diff --git a/tests/_support/Helper/Pimcore.php b/tests/_support/Helper/Pimcore.php index 7fda6b0ccf6..7327a97af2b 100644 --- a/tests/_support/Helper/Pimcore.php +++ b/tests/_support/Helper/Pimcore.php @@ -33,7 +33,7 @@ use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\Filesystem\Filesystem; -class Pimcore extends Module +class Pimcore extends Module\Symfony { /** * @var null|ContainerInterface @@ -77,7 +77,7 @@ public function getPimcoreModule() } /** - * @return \Symfony\Component\HttpKernel\KernelInterface|null + * @return \Symfony\Component\HttpKernel\Kernel|Kernel */ public function getKernel() { From 090a1670970ec99786662844c3378dab9aa68d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20G=C3=B6tsch?= Date: Mon, 28 Nov 2022 22:37:08 +0100 Subject: [PATCH 16/24] encode comma in thumbnail path as srcset is a comma separated list --- models/Asset/Image/Thumbnail.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/Asset/Image/Thumbnail.php b/models/Asset/Image/Thumbnail.php index 553ef7439b9..35b4bed896d 100644 --- a/models/Asset/Image/Thumbnail.php +++ b/models/Asset/Image/Thumbnail.php @@ -466,7 +466,8 @@ private function getSrcset(Image\Thumbnail\Config $thumbConfig, Image $image, ar $thumb = $image->getThumbnail($thumbConfigRes, true); $descriptor = $highRes . 'x'; - $srcSetValues[] = $this->addCacheBuster($thumb . ' ' . $descriptor, $options, $image); + # encode comma in thumbnail path as srcset is a comma separated list + $srcSetValues[] = str_replace(',', '%2C', $this->addCacheBuster($thumb . ' ' . $descriptor, $options, $image)); if ($this->useOriginalFile($this->asset->getFilename()) && $this->getConfig()->isSvgTargetFormatPossible()) { break; From 2f2ef018f97c53aea4778fde43be46730aa39768 Mon Sep 17 00:00:00 2001 From: dvesh3 Date: Wed, 7 Dec 2022 14:24:13 +0000 Subject: [PATCH 17/24] Apply php-cs-fixer changes --- models/Asset/Image/Thumbnail.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Asset/Image/Thumbnail.php b/models/Asset/Image/Thumbnail.php index 35b4bed896d..8a73594aa7b 100644 --- a/models/Asset/Image/Thumbnail.php +++ b/models/Asset/Image/Thumbnail.php @@ -466,7 +466,7 @@ private function getSrcset(Image\Thumbnail\Config $thumbConfig, Image $image, ar $thumb = $image->getThumbnail($thumbConfigRes, true); $descriptor = $highRes . 'x'; - # encode comma in thumbnail path as srcset is a comma separated list + // encode comma in thumbnail path as srcset is a comma separated list $srcSetValues[] = str_replace(',', '%2C', $this->addCacheBuster($thumb . ' ' . $descriptor, $options, $image)); if ($this->useOriginalFile($this->asset->getFilename()) && $this->getConfig()->isSvgTargetFormatPossible()) { From a067c313fed7ff36707df37c0458934473a65667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Filipkowski?= <110021871+pfilipkowskilemon@users.noreply.github.com> Date: Wed, 7 Dec 2022 15:55:58 +0100 Subject: [PATCH 18/24] fix sql condition being skipped when exporting objects in grid (#13663) * fix sql condition being skipped when exporting objects in grid * revert previous changes and add value to sqlFilter --- .../public/js/pimcore/object/helpers/gridTabAbstract.js | 1 + 1 file changed, 1 insertion(+) 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 0ba488f3e72..871ee5b59d1 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/helpers/gridTabAbstract.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/helpers/gridTabAbstract.js @@ -178,6 +178,7 @@ pimcore.object.helpers.gridTabAbstract = Class.create({ }.bind(this), "keydown": function (field, key) { if (key.getKey() == key.ENTER) { + this.sqlFilter = field.getValue(); this.updateSqlFilter(); } }.bind(this) From 107bdcc1554e94ed5475098b2f00fe75d78ab970 Mon Sep 17 00:00:00 2001 From: Christian Fasching Date: Wed, 7 Dec 2022 17:55:09 +0100 Subject: [PATCH 19/24] Integrated Elasticsearch 8 and deprecated Elasticsearch 7 integration (#13410) * integrated pimcore/elasticsearch-client and elasticsearch 8, deprecated elasticsearch 7 * updated docs and phpstan * updated dependencies * added upgrade notes * updated dependencies * phpstan fixes * phpstan fixes * phpstan fixes * phpstan fixes * phpstan fixes * phpstan fixes * phpstan fixes * phpstan fixes * phpstan fixes * phpstan fixes * phpstan fixes * phpstan fixes --- .../PimcoreEcommerceFrameworkExtension.php | 8 + .../IndexService/Config/ElasticSearch.php | 6 + .../ElasticSearch/DefaultElasticSearch7.php | 3 + .../ElasticSearch/DefaultElasticSearch8.php | 68 +++ .../ElasticSearch/DefaultElasticSearch7.php | 3 +- .../ElasticSearch/DefaultElasticSearch8.php | 542 ++++++++++++++++++ .../config/index_service_configs_workers.yaml | 3 + composer.json | 3 +- .../01_Configuration_Details.md | 32 +- .../07_Elastic_Search/README.md | 7 +- .../09_Upgrade_Notes/README.md | 2 + phpstan-baseline.neon | 49 ++ 12 files changed, 719 insertions(+), 7 deletions(-) create mode 100644 bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php create mode 100644 bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php diff --git a/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php b/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php index b36be98670b..a850541bded 100644 --- a/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php +++ b/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php @@ -30,6 +30,7 @@ use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceSystemLocator; use Pimcore\Bundle\EcommerceFrameworkBundle\PricingManager\PricingManagerLocator; use Pimcore\Bundle\EcommerceFrameworkBundle\PricingManager\PricingManagerLocatorInterface; +use Pimcore\Bundle\ElasticsearchClientBundle\DependencyInjection\PimcoreElasticsearchClientExtension; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ChildDefinition; @@ -465,6 +466,13 @@ private function registerIndexServiceConfig(ContainerBuilder $container, array $ $worker->setArgument('$tenantConfig', new Reference($configId)); $worker->addTag('pimcore_ecommerce.index_service.worker', ['tenant' => $tenant]); + if(!empty($tenantConfig['config_options']['es_client_name'])) { + $worker->addMethodCall( + 'setElasticSearchClient', + [new Reference(PimcoreElasticsearchClientExtension::CLIENT_SERVICE_PREFIX . $tenantConfig['config_options']['es_client_name'])] + ); + } + $container->setDefinition($configId, $config); $container->setDefinition($workerId, $worker); } diff --git a/bundles/EcommerceFrameworkBundle/IndexService/Config/ElasticSearch.php b/bundles/EcommerceFrameworkBundle/IndexService/Config/ElasticSearch.php index 917b629513f..9c0109001a4 100644 --- a/bundles/EcommerceFrameworkBundle/IndexService/Config/ElasticSearch.php +++ b/bundles/EcommerceFrameworkBundle/IndexService/Config/ElasticSearch.php @@ -168,6 +168,12 @@ protected function configureOptionsResolver(string $resolverName, OptionsResolve $resolver->setDefault('store', true); $resolver->setAllowedTypes('store', 'bool'); + + $resolver->setDefined('es_client_name'); + $resolver->setAllowedTypes('es_client_name', 'string'); + + //set options to deprecated + $resolver->setDeprecated('es_client_params'); } /** diff --git a/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch7.php b/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch7.php index 99266fdfa90..d56debb44a1 100644 --- a/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch7.php +++ b/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch7.php @@ -15,6 +15,9 @@ namespace Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ElasticSearch; +/** + * @deprecated + */ class DefaultElasticSearch7 extends AbstractElasticSearch { } diff --git a/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php b/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php new file mode 100644 index 00000000000..984dc1d52cc --- /dev/null +++ b/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php @@ -0,0 +1,68 @@ +tenantConfig->getTenantWorker()->getElasticSearchClient(); + $result = []; + + if ($esClient instanceof Client) { + if ($this->doScrollRequest) { + $params = array_merge(['scroll' => $this->scrollRequestKeepAlive], $params); + //kind of dirty hack :/ + $params['body']['size'] = $this->getLimit(); + } + + $result = $esClient->search($params)->asArray(); + + if ($this->doScrollRequest) { + $additionalHits = []; + $scrollId = $result['_scroll_id']; + + while (true) { + $additionalResult = $esClient->scroll(['scroll_id' => $scrollId, 'scroll' => $this->scrollRequestKeepAlive])->asArray(); + + if (count($additionalResult['hits']['hits'])) { + $additionalHits = array_merge($additionalHits, $additionalResult['hits']['hits']); + $scrollId = $additionalResult['_scroll_id']; + } else { + break; + } + } + $result['hits']['hits'] = array_merge($result['hits']['hits'], $additionalHits); + } + } + + return $result; + } + +} diff --git a/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php index 2edad43d8ac..073d9ad20f5 100644 --- a/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php +++ b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php @@ -16,7 +16,8 @@ namespace Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\ElasticSearch; /** - * Use this for ES Version >= 7 + * Use this for ES Version = 7 + * @deprecated */ class DefaultElasticSearch7 extends AbstractElasticSearch { diff --git a/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php new file mode 100644 index 00000000000..2fdb579460d --- /dev/null +++ b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php @@ -0,0 +1,542 @@ +tenantConfig); + } + + /** + * @return int + */ + public function getIndexVersion() + { + if ($this->indexVersion === null) { + $this->indexVersion = 0; + $esClient = $this->getElasticSearchClient(); + + try { + $result = $esClient->indices()->getAlias([ + 'name' => $this->indexName, + ])->asArray(); + + if (is_array($result)) { + $aliasIndexName = array_key_first($result); + preg_match('/'.$this->indexName.'-(\d+)/', $aliasIndexName, $matches); + if (is_array($matches) && count($matches) > 1) { + $version = (int)$matches[1]; + if ($version > $this->indexVersion) { + $this->indexVersion = $version; + } + } + } + } catch (ClientResponseException $e) { + if($e->getCode() === 404) { + $this->indexVersion = 0; + } else { + throw $e; + } + } + } + + return $this->indexVersion; + } + + /** + * @param Client|null $elasticSearchClient + */ + public function setElasticSearchClient(?Client $elasticSearchClient): void + { + $this->elasticSearchClient = $elasticSearchClient; + } + + /** + * @return Client|null + */ + public function getElasticSearchClient() + { + return $this->elasticSearchClient; + } + + /** + * actually sending data to elastic search + */ + public function commitBatchToIndex(): void + { + if (count($this->bulkIndexData)) { + $esClient = $this->getElasticSearchClient(); + $responses = $esClient->bulk([ + 'body' => $this->bulkIndexData, + ])->asArray(); + + // save update status + foreach ($responses['items'] as $response) { + $operation = null; + if (isset($response['index'])) { + $operation = 'index'; + } elseif (isset($response['delete'])) { + $operation = 'delete'; + } + + if ($operation) { + $data = [ + 'update_status' => $response[$operation]['status'], + 'update_error' => null, + 'metadata' => isset($this->indexStoreMetaData[$response[$operation]['_id']]) ? $this->indexStoreMetaData[$response[$operation]['_id']] : null, + ]; + if (isset($response[$operation]['error']) && $response[$operation]['error']) { + $data['update_error'] = json_encode($response[$operation]['error']); + $data['crc_index'] = 0; + Logger::error( + 'Failed to Index Object with Id:' . $response[$operation]['_id'], + json_decode($data['update_error'], true) + ); + + $this->db->update( + $this->getStoreTableName(), + $data, + ['o_id' => $response[$operation]['_id'], 'tenant' => $this->name] + ); + } else { + //update crc sums in store table to mark element as indexed + $this->db->executeQuery( + 'UPDATE ' . $this->getStoreTableName() . ' SET crc_index = crc_current, update_status = ?, update_error = ?, metadata = ? WHERE o_id = ? and tenant = ?', + [$data['update_status'], $data['update_error'], $data['metadata'], $response[$operation]['_id'], $this->name] + ); + } + } else { + throw new \Exception('Unkown operation in response: ' . print_r($response, true)); + } + } + } + + // reset + $this->bulkIndexData = []; + $this->indexStoreMetaData = []; + } + + /** + * Sets the alias to the current index-version and deletes the old indices + * + * @throws \Exception + */ + public function switchIndexAlias() + { + Logger::info('Index-Actions - Switching Alias'); + $esClient = $this->getElasticSearchClient(); + + $params['body'] = [ + 'actions' => [ + [ + 'remove' => [ + 'index' => '*', + 'alias' => $this->indexName, + ], + ], + [ + 'add' => [ + 'index' => $this->getIndexNameVersion(), + 'alias' => $this->indexName, + ], + ], + ], + ]; + $result = $esClient->indices()->updateAliases($params)->asArray(); + if (!$result['acknowledged']) { + //set current index version + throw new \Exception('Switching Alias failed for ' . $this->getIndexNameVersion()); + } + + //delete old indices + $this->cleanupUnusedEsIndices(); + } + + + protected function cleanupUnusedEsIndices(): void + { + $esClient = $this->getElasticSearchClient(); + $stats = $esClient->indices()->stats()->asArray(); + foreach ($stats['indices'] as $key => $data) { + preg_match('/'.$this->indexName.'-(\d+)/', $key, $matches); + if (is_array($matches) && count($matches) > 1) { + $version = (int)$matches[1]; + if ($version != $this->indexVersion) { + $indexNameVersion = $this->getIndexNameVersion($version); + Logger::info('Index-Actions - Delete old Index ' . $indexNameVersion); + $this->deleteEsIndexIfExisting($indexNameVersion); + } + } + } + } + + /** + * @param int $objectId + * @param IndexableInterface|null $object + * + * @throws \Exception + */ + protected function doDeleteFromIndex($objectId, IndexableInterface $object = null) + { + $esClient = $this->getElasticSearchClient(); + + $storeEntry = \Pimcore\Db::get()->fetchAssociative('SELECT * FROM ' . $this->getStoreTableName() . ' WHERE o_id=? AND tenant=? ', [$objectId, $this->getTenantConfig()->getTenantName()]); + if ($storeEntry) { + $isLocked = $this->checkIndexLock(false); + if ($isLocked) { + throw new \Exception('Delete not possible due to product index lock. Please re-try later.'); + } + + try { + $tenantConfig = $this->getTenantConfig(); + if (!$tenantConfig instanceof ElasticSearchConfigInterface) { + throw new \Exception('Expected a ElasticSearchConfigInterface'); + } + $esClient->delete([ + 'index' => $this->getIndexNameVersion(), + 'type' => $tenantConfig->getElasticSearchClientParams()['indexType'], + 'id' => $objectId, + $this->routingParamName => $storeEntry['o_virtualProductId'], + ]); + } catch (ClientResponseException $e) { + if($e->getCode() !== 404) { + throw $e; + } + } + $this->deleteFromStoreTable($objectId); + } + } + + protected function doCreateOrUpdateIndexStructures() + { + $this->checkIndexLock(true); + + $this->createOrUpdateStoreTable(); + + $esClient = $this->getElasticSearchClient(); + + $result = $esClient->indices()->exists(['index' => $this->getIndexNameVersion()])->asBool(); + + if (!$result) { + $indexName = $this->getIndexNameVersion(); + $this->createEsIndex($indexName); + + //index didn't exist -> reset index queue to make sure all products get re-indexed + $this->resetIndexingQueue(); + + $this->createEsAliasIfMissing(); + } + + try { + $this->putIndexMapping($this->getIndexNameVersion()); + + $configuredSettings = $this->tenantConfig->getIndexSettings(); + $synonymSettings = $this->extractMinimalSynonymFiltersTreeFromTenantConfig(); + if (isset($synonymSettings['analysis'])) { + $configuredSettings['analysis']['filter'] = array_replace_recursive($configuredSettings['analysis']['filter'], $synonymSettings['analysis']['filter']); + } + + $currentSettings = $esClient->indices()->getSettings([ + 'index' => $this->getIndexNameVersion(), + ])->asArray(); + $currentSettings = $currentSettings[$this->getIndexNameVersion()]['settings']['index']; + + $settingsIntersection = array_intersect_key($currentSettings, $configuredSettings); + if ($settingsIntersection != $configuredSettings) { + $esClient->indices()->putSettings([ + 'index' => $this->getIndexNameVersion(), + 'body' => [ + 'index' => $this->tenantConfig->getIndexSettings(), + ], + ]); + Logger::info('Index-Actions - updated settings for Index: ' . $this->getIndexNameVersion()); + } else { + Logger::info('Index-Actions - no settings update necessary for Index: ' . $this->getIndexNameVersion()); + } + } catch (\Exception $e) { + Logger::info("Index-Actions - can't create Mapping - trying reindexing " . $e->getMessage()); + Logger::info('Index-Actions - Perform native reindexing for Index: ' . $this->getIndexNameVersion()); + + $this->startReindexMode(); + } + } + + /** + * Retrieve the currently active index name from ES based on the alias. + * + * @return string|null null if no index is found. + */ + public function fetchEsActiveIndex(): ?string + { + $esClient = $this->getElasticSearchClient(); + + try { + $result = $esClient->indices()->getAlias(['index' => $this->indexName])->asArray(); + } catch (\Exception $e) { + Logger::error((string) $e); + + return null; + } + + reset($result); + $currentIndexName = key($result); + + return $currentIndexName; + } + + /** + * Create the index alias on demand. + * + * @throws \Exception if alias could not be created. + */ + protected function createEsAliasIfMissing() + { + $esClient = $this->getElasticSearchClient(); + //create alias for new index if alias doesn't exist so far + $aliasExists = $esClient->indices()->existsAlias(['name' => $this->indexName])->asBool(); + if (!$aliasExists) { + Logger::info("Index-Actions - create alias for index since it doesn't exist at all. Name: " . $this->indexName); + $params['body'] = [ + 'actions' => [ + [ + 'add' => [ + 'index' => $this->getIndexNameVersion(), + 'alias' => $this->indexName, + ], + ], + ], + ]; + $result = $esClient->indices()->updateAliases($params)->asArray(); + if (!$result) { + throw new \Exception('Alias '.$this->indexName.' could not be created.'); + } + } + } + + + /** + * Create an ES index with the specified version. + * + * @param string $indexName the name of the index. + * + * @throws \Exception is thrown if index cannot be created, for instance if connection fails or index is already existing. + */ + protected function createEsIndex(string $indexName) + { + $esClient = $this->getElasticSearchClient(); + + Logger::info('Index-Actions - creating new Index. Name: ' . $indexName); + + $configuredSettings = $this->tenantConfig->getIndexSettings(); + $synonymSettings = $this->extractMinimalSynonymFiltersTreeFromTenantConfig(); + if (isset($synonymSettings['analysis'])) { + $configuredSettings['analysis']['filter'] = array_replace_recursive($configuredSettings['analysis']['filter'], $synonymSettings['analysis']['filter']); + } + + $result = $esClient->indices()->create([ + 'index' => $indexName, + 'body' => ['settings' => $configuredSettings], + ])->asArray(); + + if (!$result['acknowledged']) { + throw new \Exception('Index creation failed. IndexName: ' . $indexName); + } + } + + + + /** + * puts current mapping to index with given name + * + * @param string $indexName + * + * @throws \Exception + */ + protected function putIndexMapping(string $indexName) + { + $esClient = $this->getElasticSearchClient(); + + $params = $this->getMappingParams(); + $params['index'] = $indexName; + $result = $esClient->indices()->putMapping($params)->asArray(); + + if (!$result['acknowledged']) { + throw new \Exception('Putting mapping to index failed. IndexName: ' . $indexName); + } + + Logger::info('Index-Actions - updated Mapping for Index: ' . $indexName); + } + + /** + * Delete an ES index if existing. + * + * @param string $indexName the name of the index. + */ + protected function deleteEsIndexIfExisting(string $indexName) + { + $esClient = $this->getElasticSearchClient(); + $result = $esClient->indices()->exists(['index' => $indexName])->asBool(); + if ($result) { + Logger::info('Deleted index '.$indexName.'.'); + $result = $esClient->indices()->delete(['index' => $indexName])->asArray(); + if (!array_key_exists('acknowledged', $result) && !$result['acknowledged']) { + Logger::error("Could not delete index {$indexName} while cleanup. Please remove the index manually."); + } + } + } + + + /** + * Blocks all write operations + * + * @param string $indexName the name of the index. + */ + protected function blockIndexWrite(string $indexName) + { + $esClient = $this->getElasticSearchClient(); + $result = $esClient->indices()->exists(['index' => $indexName])->asBool(); + if ($result) { + Logger::info('Block write index '.$indexName.'.'); + $esClient->indices()->putSettings([ + 'index' => $indexName, + 'body' => [ + 'index.blocks.write' => true, + ], + ]); + + $esClient->indices()->refresh([ + 'index' => $indexName, + ]); + } + } + + + + /** + * Unblocks all write operations + * + * @param string $indexName the name of the index. + */ + protected function unblockIndexWrite(string $indexName) + { + $esClient = $this->getElasticSearchClient(); + $result = $esClient->indices()->exists(['index' => $indexName])->asBool(); + if ($result) { + Logger::info('Unlock write index '.$indexName.'.'); + $esClient->indices()->putSettings([ + 'index' => $indexName, + 'body' => [ + 'index.blocks.write' => false, + ], + ]); + + $esClient->indices()->refresh([ + 'index' => $indexName, + ]); + } + } + + + /** + * + * Perform a synonym update on the currently selected ES index, if necessary. + * + * Attention: the current index will be closed and opened, so it won't be available for a tiny moment (typically some milliseconds). + * + * @param string $indexNameOverride if given, then that index will be used instead of the current index. + * @param bool $skipComparison if explicitly set to true, then the comparison whether the synonyms between the current index settings + * and the local index settings vary, will be skipped, and the index settings will be updated regardless. + * @param bool $skipLocking if explictly set to true, then no global lock will be activated / released. + * + * @throws \Exception is thrown if the synonym transmission fails. + */ + public function updateSynonyms(string $indexNameOverride = '', bool $skipComparison = false, bool $skipLocking = true) + { + try { + if (!$skipLocking) { + $this->activateIndexLock(); //lock all other processes + } + + $indexName = $indexNameOverride ?: $this->getIndexNameVersion(); + + $indexSettingsSynonymPartLocalConfig = $this->extractMinimalSynonymFiltersTreeFromTenantConfig(); + if (empty($indexSettingsSynonymPartLocalConfig)) { + Logger::info('No index update required, as no synonym providers are configured. '. + 'If filters have been removed, then reindexing will help to get rid of old configurations.' + ); + + return; + } + + $esClient = $this->getElasticSearchClient(); + + if (!$skipComparison) { + $settings = $esClient->indices()->getSettings(['index' => $indexName])->asArray(); + $indexSettingsCurrentEs = $settings[$indexName]['settings']['index']; + $indexSettingsSynonymPartEs = $this->extractMinimalSynonymFiltersTreeFromIndexSettings($indexSettingsCurrentEs); + + if ($indexSettingsSynonymPartEs == $indexSettingsSynonymPartLocalConfig) { + Logger::info(sprintf('The synonyms in ES index "%s" are identical with those of the local configuration. '. + 'No update required.', $indexName)); + + return; + } + } + + Logger::info(sprintf('Update synonyms in "%s"...', $indexName)); + $esClient->indices()->close(['index' => $indexName]); + + $result = $esClient->indices()->putSettings([ + 'index' => $indexName, + 'body' => [ + 'index' => $indexSettingsSynonymPartLocalConfig, + ], + ])->asArray(); + + $esClient->indices()->open(['index' => $indexName]); + + if (!$result['acknowledged']) { + //exception must be thrown after re-opening the index! + throw new \Exception('Index synonym settings update failed. IndexName: ' . $indexName); + } + } finally { + if (!$skipLocking) { + $this->releaseIndexLock(); + } + } + } + + +} diff --git a/bundles/EcommerceFrameworkBundle/Resources/config/index_service_configs_workers.yaml b/bundles/EcommerceFrameworkBundle/Resources/config/index_service_configs_workers.yaml index 8b996a771a5..c82a490f4c6 100644 --- a/bundles/EcommerceFrameworkBundle/Resources/config/index_service_configs_workers.yaml +++ b/bundles/EcommerceFrameworkBundle/Resources/config/index_service_configs_workers.yaml @@ -68,5 +68,8 @@ services: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\ElasticSearch\DefaultElasticSearch7: parent: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\AbstractWorker + Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\ElasticSearch\DefaultElasticSearch8: + parent: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\AbstractWorker + Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\DefaultFindologic: parent: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\AbstractWorker diff --git a/composer.json b/composer.json index 375e316621d..0ded957c0e8 100644 --- a/composer.json +++ b/composer.json @@ -174,11 +174,12 @@ "codeception/codeception": "^4.1.12", "codeception/module-symfony": "^1.6.0", "codeception/phpunit-wrapper": "^9", + "pimcore/elasticsearch-client": "^1.0.0", "phpstan/phpstan": "^1.9", "phpstan/phpstan-symfony": "^1.2.14", "phpunit/phpunit": "^9.3", "spiritix/php-chrome-html2pdf": "^1.6", - "elasticsearch/elasticsearch": "^7.11", + "elasticsearch/elasticsearch": "^8.0", "composer/composer": "*", "chrome-php/chrome": "^1.4.0" }, diff --git a/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/01_Configuration_Details.md b/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/01_Configuration_Details.md index 8bf256a1b3f..27804c4b5fc 100644 --- a/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/01_Configuration_Details.md +++ b/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/01_Configuration_Details.md @@ -6,7 +6,7 @@ Following aspects need to be considered in index configuration: In the `config_options` area general elasticsearch settings can be made - like hosts, index settings, etc. ##### `client_config` -- `logging`: `true`/`false` to activate logging of elasticsearch client +- `logging`: (deprecated, for Elasticsearch 7 only) `true`/`false` to activate logging of elasticsearch client - `indexName`: index name to be used, if not provided tenant name is used as index name ##### `index_settings` @@ -14,8 +14,26 @@ Index settings that are used when creating a new index. They are passed 1:1 as settings param to the body of the create index command. Details see also [elasticsearch Docs](https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/_index_management_operations.html). +#### `es_client_name` (for Elasticsearch 8 only) +Elasticsearch 8 client configuration takes place via +[Pimcore Elasticsearch Client Bundle](https://github.com/pimcore/elasticsearch-client) and has two parts. + +1) Configuring an elasticsearch client in separate configuration +```yaml +# Configure an elasticsearch client +pimcore_elasticsearch_client: + es_clients: + default: + hosts: ['elastic:9200'] + username: 'elastic' + password: 'somethingsecret' + logger_channel: 'pimcore.elasticsearch' +``` + +2) Define the client name to be used by an elasticsearch tenant. This will be done via the `es_client_name` configuration + in the `config_options`. -##### `es_client_params` +##### `es_client_params` (deprecated, for Elasticsearch 7 only) - `hosts`: Array of hosts of the elasticsearch cluster to use. - `timeoutMs`: optional parameter for setting the client timeout (frontend) in milliseconds. - `timeoutMsBackend`: optional parameter for setting the client timeout (CLI) in milliseconds. This value is typically higher than ``timeoutMs``. @@ -30,11 +48,18 @@ pimcore_ecommerce_framework: index_service: tenants: MyEsTenant: + worker_id: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\ElasticSearch\DefaultElasticSearch8 + config_id: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Config\ElasticSearch + config_options: client_config: logging: false indexName: 'ecommerce-demo-elasticsearch' + # elasticsearch client name, for Elasticsearch 8 only + es_client_name: default + + # deprecated, for Elasticsearch 7 only es_client_params: hosts: - '%elasticsearch.host%' @@ -69,8 +94,7 @@ pimcore_ecommerce_framework: ## Data Types for attributes -The type of the data attributes needs to be set to elasticsearch data types. Be careful, some types changed between -elasticsearch 5 and 6 (like string vs. keyword/text). +The type of the data attributes needs to be set to elasticsearch data types.. ```yml pimcore_ecommerce_framework: diff --git a/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/README.md b/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/README.md index 180e6b57b86..b7fa247b704 100644 --- a/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/README.md +++ b/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/README.md @@ -1,10 +1,15 @@ # Special Aspects for Elasticsearch Basically Elasticsearch worker works as described in the [optimized architecture](../README.md). -Currently, Elasticsearch 7 is supported. +Currently, Elasticsearch 7 (deprecated) and Elasticsearch 8 are supported. ## Installation + +### Elasticsearch 7 (deprecated) To work properly Pimcore requires the Elasticsearch bindings, install them with: `composer require elasticsearch/elasticsearch`. +### Elasticsearch 8 +To work properly Pimcore requires the Elasticsearch client, install them with: `composer require pimcore/elasticsearch-client`. + ## Index Configuration Elasticsearch provides a couple of additional configuration options for the index to utilize elasticsearch features. See [Configuration Details](01_Configuration_Details.md) for more information. diff --git a/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md b/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md index 18d0fa61ce7..e04e5638415 100644 --- a/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md +++ b/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md @@ -7,6 +7,8 @@ - [DataObject]: Since the `o_` prefix will be removed in Pimcore 11, a new method has been added: `DataObject\Service::getVersionDependentDatabaseColumnName()`. This method will return the field/column name for the current version and provide a way to support both version for bundles. E.g. passing `o_id` in Pimcore 10 will return `o_id`, but `id` in Pimcore 11. +- [Ecommerce] Elasticsearch 7 support has been deprecated, elasticsearch 8 supported was added. + ## 10.5.10 - [DataObject] Deprecated: Loading non-Concrete objects with the Concrete class will not be possible in Pimcore 11. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 8d049a96237..d7abf87562c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -260,3 +260,52 @@ parameters: count: 1 path: models/Element/Service.php + - + message: "#^Class Elasticsearch\\\\Client not found\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^PHPDoc tag (.*) contains unknown class Elasticsearch\\\\Client\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^Call to method (.*) on an unknown class Elasticsearch\\\\Client\\.$#" + count: 2 + path: bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^Property (.*) has unknown class Elasticsearch\\\\Client as its type\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^(.*) Elasticsearch\\\\Common\\\\Exceptions\\\\Missing404Exception not found\\.$#" + count: 2 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^Method (.*) has invalid return type Elasticsearch\\\\Client\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^Call to method (.*) on an unknown class Elasticsearch\\\\Client\\.$#" + count: 26 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^Call to static method (.*) on an unknown class Elasticsearch\\\\ClientBuilder\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^PHPDoc tag \\@throws with type Elasticsearch\\\\Common\\\\Exceptions\\\\BadRequest400Exception\\|Elasticsearch\\\\Common\\\\Exceptions\\\\NoNodesAvailableException is not subtype of Throwable$#" + count: 2 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^PHPDoc type Elastic\\\\Elasticsearch\\\\Client\\|null of property Pimcore\\\\Bundle\\\\EcommerceFrameworkBundle\\\\IndexService\\\\Worker\\\\ElasticSearch\\\\DefaultElasticSearch8\\:\\:\\$elasticSearchClient is not covariant with PHPDoc type Elasticsearch\\\\Client\\|null of overridden property Pimcore\\\\Bundle\\\\EcommerceFrameworkBundle\\\\IndexService\\\\Worker\\\\ElasticSearch\\\\AbstractElasticSearch\\:\\:\\$elasticSearchClient\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php From 0a77e92285b90b1cca9c9eb32a81e8b26708abb1 Mon Sep 17 00:00:00 2001 From: fashxp Date: Wed, 7 Dec 2022 16:56:42 +0000 Subject: [PATCH 20/24] Apply php-cs-fixer changes --- .../PimcoreEcommerceFrameworkExtension.php | 2 +- .../ElasticSearch/DefaultElasticSearch8.php | 2 -- .../Worker/ElasticSearch/DefaultElasticSearch7.php | 1 + .../Worker/ElasticSearch/DefaultElasticSearch8.php | 14 ++------------ 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php b/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php index a850541bded..e68f71baad7 100644 --- a/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php +++ b/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php @@ -466,7 +466,7 @@ private function registerIndexServiceConfig(ContainerBuilder $container, array $ $worker->setArgument('$tenantConfig', new Reference($configId)); $worker->addTag('pimcore_ecommerce.index_service.worker', ['tenant' => $tenant]); - if(!empty($tenantConfig['config_options']['es_client_name'])) { + if (!empty($tenantConfig['config_options']['es_client_name'])) { $worker->addMethodCall( 'setElasticSearchClient', [new Reference(PimcoreElasticsearchClientExtension::CLIENT_SERVICE_PREFIX . $tenantConfig['config_options']['es_client_name'])] diff --git a/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php b/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php index 984dc1d52cc..1d97281ae66 100644 --- a/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php +++ b/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php @@ -19,7 +19,6 @@ class DefaultElasticSearch8 extends AbstractElasticSearch { - /** * send a request to elasticsearch * @@ -64,5 +63,4 @@ protected function sendRequest(array $params) return $result; } - } diff --git a/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php index 073d9ad20f5..6c242aaad9b 100644 --- a/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php +++ b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php @@ -17,6 +17,7 @@ /** * Use this for ES Version = 7 + * * @deprecated */ class DefaultElasticSearch7 extends AbstractElasticSearch diff --git a/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php index 2fdb579460d..e7552ac4ed2 100644 --- a/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php +++ b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php @@ -64,7 +64,7 @@ public function getIndexVersion() } } } catch (ClientResponseException $e) { - if($e->getCode() === 404) { + if ($e->getCode() === 404) { $this->indexVersion = 0; } else { throw $e; @@ -184,7 +184,6 @@ public function switchIndexAlias() $this->cleanupUnusedEsIndices(); } - protected function cleanupUnusedEsIndices(): void { $esClient = $this->getElasticSearchClient(); @@ -231,7 +230,7 @@ protected function doDeleteFromIndex($objectId, IndexableInterface $object = nul $this->routingParamName => $storeEntry['o_virtualProductId'], ]); } catch (ClientResponseException $e) { - if($e->getCode() !== 404) { + if ($e->getCode() !== 404) { throw $e; } } @@ -345,7 +344,6 @@ protected function createEsAliasIfMissing() } } - /** * Create an ES index with the specified version. * @@ -375,8 +373,6 @@ protected function createEsIndex(string $indexName) } } - - /** * puts current mapping to index with given name * @@ -417,7 +413,6 @@ protected function deleteEsIndexIfExisting(string $indexName) } } - /** * Blocks all write operations * @@ -442,8 +437,6 @@ protected function blockIndexWrite(string $indexName) } } - - /** * Unblocks all write operations * @@ -468,7 +461,6 @@ protected function unblockIndexWrite(string $indexName) } } - /** * * Perform a synonym update on the currently selected ES index, if necessary. @@ -537,6 +529,4 @@ public function updateSynonyms(string $indexNameOverride = '', bool $skipCompari } } } - - } From f2fa96ee1db3e0ef5ca4cd8a47c4d8aabe5d9d1b Mon Sep 17 00:00:00 2001 From: Ashan Ghimire Date: Mon, 12 Dec 2022 15:55:57 +0545 Subject: [PATCH 21/24] Updated grid export prepare http method to POST --- .../Controller/Admin/Asset/AssetHelperController.php | 2 +- .../Controller/Admin/DataObject/DataObjectHelperController.php | 2 +- .../public/js/pimcore/element/helpers/gridColumnConfig.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bundles/AdminBundle/Controller/Admin/Asset/AssetHelperController.php b/bundles/AdminBundle/Controller/Admin/Asset/AssetHelperController.php index a3a3e2fffbd..12908f5a1ba 100644 --- a/bundles/AdminBundle/Controller/Admin/Asset/AssetHelperController.php +++ b/bundles/AdminBundle/Controller/Admin/Asset/AssetHelperController.php @@ -706,7 +706,7 @@ protected function updateGridConfigFavourites($gridConfig, $metadata) } /** - * @Route("/get-export-jobs", name="pimcore_admin_asset_assethelper_getexportjobs", methods={"GET"}) + * @Route("/get-export-jobs", name="pimcore_admin_asset_assethelper_getexportjobs", methods={"POST"}) * * @param Request $request * @param GridHelperService $gridHelperService diff --git a/bundles/AdminBundle/Controller/Admin/DataObject/DataObjectHelperController.php b/bundles/AdminBundle/Controller/Admin/DataObject/DataObjectHelperController.php index 209ffffcb05..371fa2ccb88 100644 --- a/bundles/AdminBundle/Controller/Admin/DataObject/DataObjectHelperController.php +++ b/bundles/AdminBundle/Controller/Admin/DataObject/DataObjectHelperController.php @@ -1297,7 +1297,7 @@ protected function getCsvFile($fileHandle) } /** - * @Route("/get-export-jobs", name="getexportjobs", methods={"GET"}) + * @Route("/get-export-jobs", name="getexportjobs", methods={"POST"}) * * @param Request $request * @param GridHelperService $gridHelperService 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 73874483e83..86a8603a0ec 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/element/helpers/gridColumnConfig.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/element/helpers/gridColumnConfig.js @@ -713,6 +713,7 @@ pimcore.element.helpers.gridColumnConfig = { settings = Ext.encode(settings); params["settings"] = settings; Ext.Ajax.request({ + method: 'POST', url: this.exportPrepareUrl, params: params, success: function (response) { From 2e5b81b2a976de35d010d75705f0b40ed36ddd67 Mon Sep 17 00:00:00 2001 From: Sebastian Blank Date: Tue, 13 Dec 2022 15:50:00 +0100 Subject: [PATCH 22/24] Fix new PhpStan errors with version 1.9.3 (#13785) * Fix new PhpStan errors with version 1.9.3 * Remove unnecessary $allLanguagesAllowed * Remove unnecessary !$user->isAdmin() check --- .../Admin/DataObject/DataObjectActionsTrait.php | 11 +++++------ composer.json | 2 +- lib/Image/Adapter/Imagick.php | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/bundles/AdminBundle/Controller/Admin/DataObject/DataObjectActionsTrait.php b/bundles/AdminBundle/Controller/Admin/DataObject/DataObjectActionsTrait.php index ede61b173e0..efed7a10ecd 100644 --- a/bundles/AdminBundle/Controller/Admin/DataObject/DataObjectActionsTrait.php +++ b/bundles/AdminBundle/Controller/Admin/DataObject/DataObjectActionsTrait.php @@ -185,14 +185,13 @@ private function prepareObjectData( LocaleServiceInterface $localeService ): array { $user = Tool\Admin::getCurrentUser(); - $allLanguagesAllowed = false; $languagePermissions = []; if (!$user->isAdmin()) { $languagePermissions = $object->getPermissions('lEdit', $user); - //sets allowed all languages modification when the lEdit column is empty - $allLanguagesAllowed = $languagePermissions['lEdit'] == ''; - $languagePermissions = explode(',', $languagePermissions['lEdit']); + if ($languagePermissions['lEdit']) { + $languagePermissions = explode(',', $languagePermissions['lEdit']); + } } $class = $object->getClass(); @@ -280,7 +279,7 @@ private function prepareObjectData( $brick->$valueSetter($value); } } else { - if (!$user->isAdmin() && $languagePermissions) { + if ($languagePermissions) { $fd = $class->getFieldDefinition($key); if (!$fd) { // try to get via localized fields @@ -289,7 +288,7 @@ private function prepareObjectData( $field = $localized->getFieldDefinition($key); if ($field) { $currentLocale = $localeService->findLocale(); - if (!$allLanguagesAllowed && !in_array($currentLocale, $languagePermissions)) { + if (!in_array($currentLocale, $languagePermissions)) { continue; } } diff --git a/composer.json b/composer.json index 635cbdd63e2..f97cd7c3428 100644 --- a/composer.json +++ b/composer.json @@ -173,7 +173,7 @@ "codeception/codeception": "^4.1.12", "codeception/module-symfony": "^1.6.0", "codeception/phpunit-wrapper": "^9", - "phpstan/phpstan": "^1.9", + "phpstan/phpstan": "^1.9.3", "phpstan/phpstan-symfony": "^1.2.14", "phpunit/phpunit": "^9.3", "spiritix/php-chrome-html2pdf": "^1.6", diff --git a/lib/Image/Adapter/Imagick.php b/lib/Image/Adapter/Imagick.php index b8bc6658d69..bf48c3aed6f 100644 --- a/lib/Image/Adapter/Imagick.php +++ b/lib/Image/Adapter/Imagick.php @@ -349,7 +349,7 @@ private function hasAlphaChannel() for ($i = 0; $i < $width; $i++) { for ($j = 0; $j < $height; $j++) { $pixel = $this->resource->getImagePixelColor($i, $j); - $color = $pixel->getColor(true); // get the real alpha not just 1/0 + $color = $pixel->getColor(1); // get the real alpha not just 1/0 if ($color['a'] < 1) { // if there's an alpha pixel, return true return true; } From 5a73b21d4718ba4dd19f24f2e2136f8ebfd5e5d3 Mon Sep 17 00:00:00 2001 From: frithjof Date: Wed, 14 Dec 2022 07:54:39 +0100 Subject: [PATCH 23/24] fix problem in grid configuration with block (#13763) An invisible Block in class breaks the grid configuration for folder grid --- .../Resources/public/js/pimcore/object/helpers/classTree.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/object/helpers/classTree.js b/bundles/AdminBundle/Resources/public/js/pimcore/object/helpers/classTree.js index 9515f7a35ae..2ef6df013dc 100644 --- a/bundles/AdminBundle/Resources/public/js/pimcore/object/helpers/classTree.js +++ b/bundles/AdminBundle/Resources/public/js/pimcore/object/helpers/classTree.js @@ -192,7 +192,7 @@ pimcore.object.helpers.classTree = Class.create({ newNode = fn(); - if (con.children) { + if (con.children && newNode) { for (var i = 0; i < con.children.length; i++) { this.recursiveAddNode(con.children[i], newNode, brickDescriptor, config); } From e1a51fcd8e6fc9a41493d3873ab8a65d018e5ba6 Mon Sep 17 00:00:00 2001 From: Jacob Dreesen Date: Wed, 14 Dec 2022 08:39:00 +0100 Subject: [PATCH 24/24] Fix typo in the docs (#13787) --- .../18_Tools_and_Features/29_Custom_Reports.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Development_Documentation/18_Tools_and_Features/29_Custom_Reports.md b/doc/Development_Documentation/18_Tools_and_Features/29_Custom_Reports.md index d545bf5930a..fb9c3430815 100644 --- a/doc/Development_Documentation/18_Tools_and_Features/29_Custom_Reports.md +++ b/doc/Development_Documentation/18_Tools_and_Features/29_Custom_Reports.md @@ -47,5 +47,5 @@ pimcore: ## Custom JS Class for Report Visualization If you need to fully customize the appearance of the report, you can specify a custom java script class that should be used when opening the report in Pimcore Backend. This class can be specified in `Report Class` option and should extend -the default java script class for the reports which is `pimcore.report.custom.report``. +the default java script class for the reports which is `pimcore.report.custom.report`.