From d5cf0f1a9d9124720fb24ce01508e9448fdec926 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Tue, 13 Nov 2018 10:23:01 +0200 Subject: [PATCH 001/296] Updated license --- LICENSE.txt | 49 ++++--------------------------------------------- LICENSE_AFL.txt | 48 ------------------------------------------------ composer.json | 1 + 3 files changed, 5 insertions(+), 93 deletions(-) delete mode 100644 LICENSE_AFL.txt diff --git a/LICENSE.txt b/LICENSE.txt index 49525fd9..5ef82b5c 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,48 +1,7 @@ +Copyright 2018 Cloudinary -Open Software License ("OSL") v. 3.0 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSE_AFL.txt b/LICENSE_AFL.txt deleted file mode 100644 index f39d641b..00000000 --- a/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/composer.json b/composer.json index 4d0b24e9..1f1945b0 100644 --- a/composer.json +++ b/composer.json @@ -4,6 +4,7 @@ "type": "magento2-module", "version": "1.6.0", "minimum-stability": "dev", + "license": "MIT", "repositories": { "0": { "type": "git", From 3609912f43c0e11d519738f67290e96f3782beea Mon Sep 17 00:00:00 2001 From: pini-girit Date: Tue, 13 Nov 2018 10:29:45 +0200 Subject: [PATCH 002/296] Updated github repository on composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1f1945b0..482453f7 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "repositories": { "0": { "type": "git", - "url": "git@github.com:cloudinary/cloudinary_magento2.git" + "url": "https://github.com/cloudinary/cloudinary_magento2" } }, "require": { From a33ebe8ad167b4bb370a9dd812bc5f58058be534 Mon Sep 17 00:00:00 2001 From: Pini Date: Thu, 22 Nov 2018 15:55:15 +0200 Subject: [PATCH 003/296] Update README.md --- README.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/README.md b/README.md index d8cb7fb6..0c001a45 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,6 @@ Magento 2 module for integration with Cloudinary. --- -## ✓ Install via composer (recommended) -Run the following command under your Magento 2 root dir: - -``` -composer require cloudinary/magento2-module-cloudinary -php bin/magento setup:upgrade -php bin/magento setup:di:compile -php bin/magento setup:static-content:deploy -php bin/magento cache:flush -``` - ---- - https://www.cloudinary.com/ Copyright © 2018 Cloudinary. All rights reserved. From 64b02b9d063e415af3c9a3bc19eff02e749663ab Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 14 Nov 2018 15:33:47 +0200 Subject: [PATCH 004/296] CLOUDINARY-15,CLOUDINARY-11,CLOUDINARY-18: Added support for Use-Root-Path & Version-less URLs (+New 'Advanced' configuration section) --- Core/CloudinaryImageProvider.php | 24 +++++++++++++++--------- Model/Configuration.php | 23 ++++++++++++++++++++++- composer.json | 2 +- etc/adminhtml/system.xml | 13 +++++++++++++ etc/config.xml | 4 ++++ etc/module.xml | 2 +- 6 files changed, 56 insertions(+), 12 deletions(-) diff --git a/Core/CloudinaryImageProvider.php b/Core/CloudinaryImageProvider.php index cbfe3560..f54bc3ee 100644 --- a/Core/CloudinaryImageProvider.php +++ b/Core/CloudinaryImageProvider.php @@ -3,12 +3,9 @@ namespace Cloudinary\Cloudinary\Core; use Cloudinary; -use Cloudinary\Uploader; use Cloudinary\Cloudinary\Core\Exception\ApiError; use Cloudinary\Cloudinary\Core\Image\Transformation; -use Cloudinary\Cloudinary\Core\Security; -use Cloudinary\Cloudinary\Core\Image\Transformation\Format; -use Cloudinary\Cloudinary\Core\Image\Transformation\FetchFormat; +use Cloudinary\Uploader; class CloudinaryImageProvider implements ImageProvider { @@ -57,7 +54,8 @@ public function __construct( * @param ConfigurationInterface $configuration * @return CloudinaryImageProvider */ - public static function fromConfiguration(ConfigurationInterface $configuration){ + public static function fromConfiguration(ConfigurationInterface $configuration) + { return new CloudinaryImageProvider( $configuration, new ConfigurationBuilder($configuration), @@ -94,10 +92,18 @@ public function upload(Image $image) */ public function retrieveTransformed(Image $image, Transformation $transformation) { - return Image::fromPath( - \cloudinary_url($image->getId(), ['transformation' => $transformation->build(), 'secure' => true]), - $image->getRelativePath() - ); + $imagePath = \cloudinary_url($image->getId(), ['transformation' => $transformation->build(), 'secure' => true]); + + if ($this->configuration->getUseRootPath()) { + $imagePath = str_replace(".com/{$this->configuration->getCloud()}/image/upload/", ".com/{$this->configuration->getCloud()}/", $imagePath); + } + + if ($this->configuration->getRemoveVersionNumber()) { + $regex = '/\/v[0-9]+\/' . preg_quote(ltrim($image->getId(), '/'), '/') . '$/'; + $imagePath = preg_replace($regex, '/' . ltrim($image->getId(), '/'), $imagePath); + } + + return Image::fromPath($imagePath, $image->getRelativePath()); } /** diff --git a/Model/Configuration.php b/Model/Configuration.php index fad16c2c..f0b217d8 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -26,11 +26,16 @@ class Configuration implements ConfigurationInterface const USER_PLATFORM_TEMPLATE = 'CloudinaryMagento/%s (Magento %s)'; const CONFIG_PATH_ENVIRONMENT_VARIABLE = 'cloudinary/setup/cloudinary_environment_variable'; const CONFIG_CDN_SUBDOMAIN = 'cloudinary/configuration/cloudinary_cdn_subdomain'; + // Transformations const CONFIG_DEFAULT_GRAVITY = 'cloudinary/transformations/cloudinary_gravity'; const CONFIG_DEFAULT_QUALITY = 'cloudinary/transformations/cloudinary_image_quality'; const CONFIG_DEFAULT_DPR = 'cloudinary/transformations/cloudinary_image_dpr'; const CONFIG_DEFAULT_FETCH_FORMAT = 'cloudinary/transformations/cloudinary_fetch_format'; const CONFIG_GLOBAL_FREEFORM = 'cloudinary/transformations/cloudinary_free_transform_global'; + // Advanced + const CONFIG_PATH_REMOVE_VERSION_NUMBER = 'cloudinary/advanced/remove_version_number'; + const CONFIG_PATH_USE_ROOT_PATH = 'cloudinary/advanced/use_root_path'; + const USE_FILENAME = true; const UNIQUE_FILENAME = false; const OVERWRITE = false; @@ -132,7 +137,7 @@ public function getCdnSubdomainStatus() */ public function getUserPlatform() { - return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.0', '2.0.0'); + return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.2', '2.0.0'); } /** @@ -236,4 +241,20 @@ private function getEnvironmentVariable() } return $this->environmentVariable; } + + /** + * @return bool + */ + public function getRemoveVersionNumber() + { + return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER); + } + + /** + * @return bool + */ + public function getUseRootPath() + { + return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER); + } } diff --git a/composer.json b/composer.json index 482453f7..52c5480a 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "cloudinary/magento2-module-cloudinary", "description": "Cloudinary Magento 2 Integration.", "type": "magento2-module", - "version": "1.6.0", + "version": "1.6.2", "minimum-stability": "dev", "license": "MIT", "repositories": { diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 5175e4a0..c87ea355 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -62,6 +62,19 @@ Cloudinary\Cloudinary\Block\Adminhtml\Form\Field\Free + + + + + Remove version number (e.g '/v1/') from URLs + Magento\Config\Model\Config\Source\Yesno + + + + Remove '/image/upload/' from URLs + Magento\Config\Model\Config\Source\Yesno + + diff --git a/etc/config.xml b/etc/config.xml index 7c0f7bdd..251673ae 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -11,6 +11,10 @@ 1 + + 0 + 0 + diff --git a/etc/module.xml b/etc/module.xml index b3507862..c432a3c3 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + From 78b9f065a66f10e5a5d54785b0b6b21d277e93e0 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 14 Nov 2018 16:13:09 +0200 Subject: [PATCH 005/296] CLOUDINARY-15,CLOUDINARY-11,CLOUDINARY-18: Added support for Use-Root-Path & Version-less URLs (+New 'Advanced' configuration section) --- etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index c87ea355..bc640125 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -62,7 +62,7 @@ Cloudinary\Cloudinary\Block\Adminhtml\Form\Field\Free - + From 0471dff6ab4ad1b326bfbfb0bf9d4c2fcd8b1769 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 14 Nov 2018 16:48:41 +0200 Subject: [PATCH 006/296] CLOUDINARY-15,CLOUDINARY-11,CLOUDINARY-18: Added support for Use-Root-Path & Version-less URLs (+New 'Advanced' configuration section) --- etc/adminhtml/system.xml | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index bc640125..56dafa9b 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -2,76 +2,76 @@ -
+
service Cloudinary_Cloudinary::config_cloudinary - + - + Magento\Config\Model\Config\Source\Yesno - + - + Set the credentials of your Cloudinary account. Copy the "Environment variable" string from the dashboard of Cloudinary's Management Console. Cloudinary\Cloudinary\Model\Config\Backend\Credentials - + - + Enable multiple sub-domains of image delivery URLs for faster page load speed. Magento\Config\Model\Config\Source\Yesno - + When enabled, Cloudinary will fetch images it does not have from your site automatically without requiring the manual migration process. Magento\Config\Model\Config\Source\Yesno - + - + Automatically deliver images converted to modern image formats based on viewing device and browser. For example, deliver WebP on Chrome and JPEG-XR on Internet Explorer for better performance and user experience. Magento\Config\Model\Config\Source\Yesno - + Adjust quality of generated images to balance between visual quality and file size minimization. The quality is relevant for JPEG and WebP compression levels for example. Cloudinary\Cloudinary\Model\Config\Source\Dropdown\Quality - + Define the part of the image to focus on when cropping images in order to better match your graphic design. Cloudinary\Cloudinary\Model\Config\Source\Dropdown\Gravity - + Use DPR value higher than 1.0 to generate and deliver hi-res images for better visual result on HiDPI devices, such as Retina Display devices (e.g., 2.0). Cloudinary\Cloudinary\Model\Config\Source\Dropdown\Dpr - + Cloudinary\Cloudinary\Model\Config\Backend\Free Cloudinary\Cloudinary\Block\Adminhtml\Form\Field\Free - + - + - Remove version number (e.g '/v1/') from URLs + Remove version number (e.g., ".../v1/...") from URLs Magento\Config\Model\Config\Source\Yesno - + - Remove '/image/upload/' from URLs + Remove "/image/upload/" from URLs Magento\Config\Model\Config\Source\Yesno From 660b8ac8f342465794a5aa82a2d593db1efe02f6 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Thu, 15 Nov 2018 09:51:58 +0200 Subject: [PATCH 007/296] CLOUDINARY-7: Fixed 'Use auto mapping...' observer on admin system configuration + Added some logics for multistore cases (not sure if it's needed or globally supported) --- Core/AutoUploadMapping/RequestProcessor.php | 54 ++++++- .../AutoUploadConfiguration.php | 23 +-- Model/Configuration.php | 141 +++++++++++++----- Model/Observer/Configuration.php | 80 +++++++--- 4 files changed, 223 insertions(+), 75 deletions(-) diff --git a/Core/AutoUploadMapping/RequestProcessor.php b/Core/AutoUploadMapping/RequestProcessor.php index b5508150..63e644a1 100644 --- a/Core/AutoUploadMapping/RequestProcessor.php +++ b/Core/AutoUploadMapping/RequestProcessor.php @@ -2,6 +2,8 @@ namespace Cloudinary\Cloudinary\Core\AutoUploadMapping; +use Magento\Store\Model\ScopeInterface; + class RequestProcessor { /** @@ -14,6 +16,10 @@ class RequestProcessor */ private $apiClient; + protected $scope = ScopeInterface::SCOPE_STORE; + + protected $scopeId = null; + /** * @param AutoUploadConfigurationInterface $configuration * @param ApiClient $apiClient @@ -26,6 +32,46 @@ public function __construct( $this->apiClient = $apiClient; } + /** + * @method setScopeId + * @param string|null $scopeId + * @return $this + */ + public function setScope($scope) + { + $this->scope = $scope; + return $this; + } + + /** + * @method getScope + * @return string|null + */ + public function getScope() + { + return $this->scope; + } + + /** + * @method setScopeId + * @param integer|null $scopeId + * @return $this + */ + public function setScopeId($scopeId) + { + $this->scopeId = $scopeId; + return $this; + } + + /** + * @method getScopeId + * @return integer|null + */ + public function getScopeId($scope) + { + return $this->scopeId; + } + /** * @param string $folder * @param string $url @@ -33,11 +79,11 @@ public function __construct( */ public function handle($folder, $url) { - if ($this->configuration->isActive() == $this->configuration->getRequestState()) { + if ($this->configuration->isActive($this->scope, $this->scopeId) == $this->configuration->getRequestState($this->scope, $this->scopeId)) { return true; } - if ($this->configuration->getRequestState() == AutoUploadConfigurationInterface::ACTIVE) { + if ($this->configuration->getRequestState($this->scope, $this->scopeId) == AutoUploadConfigurationInterface::ACTIVE) { return $this->handleActiveRequest($folder, $url); } @@ -56,9 +102,9 @@ private function handleActiveRequest($folder, $url) $result = $this->apiClient->prepareMapping($folder, $url); if ($result) { - $this->configuration->setState(AutoUploadConfigurationInterface::ACTIVE); + $this->configuration->setState(AutoUploadConfigurationInterface::ACTIVE, $this->scope, $this->scopeId); } else { - $this->configuration->setRequestState(AutoUploadConfigurationInterface::INACTIVE); + $this->configuration->setRequestState(AutoUploadConfigurationInterface::INACTIVE, $this->scope, $this->scopeId); } return $result; diff --git a/Model/AutoUploadMapping/AutoUploadConfiguration.php b/Model/AutoUploadMapping/AutoUploadConfiguration.php index 697c8a52..29aaffe8 100644 --- a/Model/AutoUploadMapping/AutoUploadConfiguration.php +++ b/Model/AutoUploadMapping/AutoUploadConfiguration.php @@ -2,9 +2,10 @@ namespace Cloudinary\Cloudinary\Model\AutoUploadMapping; +use Cloudinary\Cloudinary\Core\AutoUploadMapping\AutoUploadConfigurationInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Config\Storage\WriterInterface; -use Cloudinary\Cloudinary\Core\AutoUploadMapping\AutoUploadConfigurationInterface; +use Magento\Store\Model\ScopeInterface; class AutoUploadConfiguration implements AutoUploadConfigurationInterface { @@ -38,41 +39,41 @@ public function __construct( /** * @return bool */ - public function isActive() + public function isActive($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return $this->configReader->isSetFlag(self::STATE_PATH); + return $this->configReader->isSetFlag(self::STATE_PATH, $scope, $scopeId); } /** * @param bool $state */ - public function setState($state) + public function setState($state, $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - $this->setFlag(self::STATE_PATH, $state); + $this->setFlag(self::STATE_PATH, $state, $scope, $scopeId); } /** * @return bool */ - public function getRequestState() + public function getRequestState($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return $this->configReader->isSetFlag(self::REQUEST_PATH); + return $this->configReader->isSetFlag(self::REQUEST_PATH, $scope, $scopeId); } /** * @param bool $state */ - public function setRequestState($state) + public function setRequestState($state, $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - $this->setFlag(self::REQUEST_PATH, $state); + $this->setFlag(self::REQUEST_PATH, $state, $scope, $scopeId); } /** * @param string $key * @param bool $state */ - private function setFlag($key, $state) + private function setFlag($key, $state, $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - $this->configWriter->save($key, $state ? self::CONFIG_TRUE : self::CONFIG_FALSE); + $this->configWriter->save($key, $state ? self::CONFIG_TRUE : self::CONFIG_FALSE, $scope, $scopeId); } } diff --git a/Model/Configuration.php b/Model/Configuration.php index f0b217d8..0b86a899 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -19,6 +19,7 @@ use Magento\Framework\App\Config\Storage\WriterInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Store\Model\ScopeInterface; class Configuration implements ConfigurationInterface { @@ -26,15 +27,19 @@ class Configuration implements ConfigurationInterface const USER_PLATFORM_TEMPLATE = 'CloudinaryMagento/%s (Magento %s)'; const CONFIG_PATH_ENVIRONMENT_VARIABLE = 'cloudinary/setup/cloudinary_environment_variable'; const CONFIG_CDN_SUBDOMAIN = 'cloudinary/configuration/cloudinary_cdn_subdomain'; - // Transformations + //= Transformations const CONFIG_DEFAULT_GRAVITY = 'cloudinary/transformations/cloudinary_gravity'; const CONFIG_DEFAULT_QUALITY = 'cloudinary/transformations/cloudinary_image_quality'; const CONFIG_DEFAULT_DPR = 'cloudinary/transformations/cloudinary_image_dpr'; const CONFIG_DEFAULT_FETCH_FORMAT = 'cloudinary/transformations/cloudinary_fetch_format'; const CONFIG_GLOBAL_FREEFORM = 'cloudinary/transformations/cloudinary_free_transform_global'; - // Advanced + //= Advanced const CONFIG_PATH_REMOVE_VERSION_NUMBER = 'cloudinary/advanced/remove_version_number'; const CONFIG_PATH_USE_ROOT_PATH = 'cloudinary/advanced/use_root_path'; + //= Others + const CONFIG_PATH_SECURE_BASE_URL = "web/secure/base_url"; + const CONFIG_PATH_UNSECURE_BASE_URL = "web/unsecure/base_url"; + const CONFIG_PATH_USE_SECURE_IN_FRONTEND = "web/secure/use_in_frontend"; const USE_FILENAME = true; const UNIQUE_FILENAME = false; @@ -90,46 +95,46 @@ public function __construct( /** * @return Cloud */ - public function getCloud() + public function getCloud($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return $this->getEnvironmentVariable()->getCloud(); + return $this->getEnvironmentVariable($scope, $scopeId)->getCloud(); } /** * @return Credentials */ - public function getCredentials() + public function getCredentials($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return $this->getEnvironmentVariable()->getCredentials(); + return $this->getEnvironmentVariable($scope, $scopeId)->getCredentials(); } /** * @return Transformation */ - public function getDefaultTransformation() + public function getDefaultTransformation($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { return Transformation::builder() - ->withGravity(Gravity::fromString($this->getDefaultGravity())) - ->withQuality(Quality::fromString($this->getImageQuality())) - ->withFetchFormat(FetchFormat::fromString($this->getFetchFormat())) - ->withFreeform(Freeform::fromString($this->getDefaultGlobalFreeform())) - ->withDpr(Dpr::fromString($this->getImageDpr())); + ->withGravity(Gravity::fromString($this->getDefaultGravity($scope, $scopeId))) + ->withQuality(Quality::fromString($this->getImageQuality($scope, $scopeId))) + ->withFetchFormat(FetchFormat::fromString($this->getFetchFormat($scope, $scopeId))) + ->withFreeform(Freeform::fromString($this->getDefaultGlobalFreeform($scope, $scopeId))) + ->withDpr(Dpr::fromString($this->getImageDpr($scope, $scopeId))); } /** * @return string */ - private function getDefaultGlobalFreeform() + private function getDefaultGlobalFreeform($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return (string) $this->configReader->getValue(self::CONFIG_GLOBAL_FREEFORM); + return (string) $this->configReader->getValue(self::CONFIG_GLOBAL_FREEFORM, $scope, $scopeId); } /** * @return boolean */ - public function getCdnSubdomainStatus() + public function getCdnSubdomainStatus($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return $this->configReader->isSetFlag(self::CONFIG_CDN_SUBDOMAIN); + return $this->configReader->isSetFlag(self::CONFIG_CDN_SUBDOMAIN, $scope, $scopeId); } /** @@ -151,19 +156,19 @@ public function getUploadConfig() /** * @return boolean */ - public function isEnabled() + public function isEnabled($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return $this->hasEnvironmentVariable() && $this->configReader->isSetFlag(self::CONFIG_PATH_ENABLED); + return $this->hasEnvironmentVariable() && $this->configReader->isSetFlag(self::CONFIG_PATH_ENABLED, $scope, $scopeId); } - public function enable() + public function enable($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - $this->configWriter->save(self::CONFIG_PATH_ENABLED, self::SCOPE_ID_ONE); + $this->configWriter->save(self::CONFIG_PATH_ENABLED, self::SCOPE_ID_ONE, $scope, $scopeId); } - public function disable() + public function disable($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - $this->configWriter->save(self::CONFIG_PATH_ENABLED, self::SCOPE_ID_ZERO); + $this->configWriter->save(self::CONFIG_PATH_ENABLED, self::SCOPE_ID_ZERO, $scope, $scopeId); } /** @@ -186,53 +191,53 @@ public function getMigratedPath($file) /** * @return string */ - public function getDefaultGravity() + public function getDefaultGravity($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return (string) $this->configReader->getValue(self::CONFIG_DEFAULT_GRAVITY); + return (string) $this->configReader->getValue(self::CONFIG_DEFAULT_GRAVITY, $scope, $scopeId); } /** * @return string */ - public function getFetchFormat() + public function getFetchFormat($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return $this->configReader->isSetFlag(self::CONFIG_DEFAULT_FETCH_FORMAT) ? FetchFormat::FETCH_FORMAT_AUTO : ''; + return $this->configReader->isSetFlag(self::CONFIG_DEFAULT_FETCH_FORMAT, $scope, $scopeId) ? FetchFormat::FETCH_FORMAT_AUTO : ''; } /** * @return string */ - public function getImageQuality() + public function getImageQuality($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return $this->configReader->getValue(self::CONFIG_DEFAULT_QUALITY); + return $this->configReader->getValue(self::CONFIG_DEFAULT_QUALITY, $scope, $scopeId); } /** * @return string */ - public function getImageDpr() + public function getImageDpr($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return $this->configReader->getValue(self::CONFIG_DEFAULT_DPR); + return $this->configReader->getValue(self::CONFIG_DEFAULT_DPR, $scope, $scopeId); } /** * @return bool */ - public function hasEnvironmentVariable() + public function hasEnvironmentVariable($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return (bool)$this->configReader->getValue(self::CONFIG_PATH_ENVIRONMENT_VARIABLE); + return (bool)$this->configReader->getValue(self::CONFIG_PATH_ENVIRONMENT_VARIABLE, $scope, $scopeId); } /** * @return CloudinaryEnvironmentVariable */ - private function getEnvironmentVariable() + private function getEnvironmentVariable($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { if (is_null($this->environmentVariable)) { try { $this->environmentVariable = CloudinaryEnvironmentVariable::fromString( $this->decryptor->decrypt( - $this->configReader->getValue(self::CONFIG_PATH_ENVIRONMENT_VARIABLE) + $this->configReader->getValue(self::CONFIG_PATH_ENVIRONMENT_VARIABLE, $scope, $scopeId) ) ); } catch (InvalidCredentials $invalidConfigException) { @@ -245,16 +250,76 @@ private function getEnvironmentVariable() /** * @return bool */ - public function getRemoveVersionNumber() + public function getRemoveVersionNumber($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER); + return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER, $scope, $scopeId); } /** * @return bool */ - public function getUseRootPath() + public function getUseRootPath($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) { - return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER); + return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER, $scope, $scopeId); + } + + /** + * @method getUseSecureInFrontend + * @param string $scope + * @param integer|null $scopeId + * @return string + */ + public function getUseSecureInFrontend($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + { + return ($this->configReader->getValue(self::CONFIG_PATH_USE_SECURE_IN_FRONTEND, $scope, $scopeId)) ? true : false; + } + + /** + * @method getSecureBaseUrl + * @param string $path + * @param string $scope + * @param integer|null $scopeId + * @return string + */ + public function getSecureBaseUrl($path = "", $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + { + $return = (string) $this->configReader->getValue(self::CONFIG_PATH_SECURE_BASE_URL, $scope, $scopeId); + return rtrim($return, "/") . "/" . ltrim($path, "/"); + } + + /** + * @method getUnsecureBaseUrl + * @param string $path + * @param string $scope + * @param integer|null $scopeId + * @return string + */ + public function getUnsecureBaseUrl($path = "", $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + { + $return = (string) $this->configReader->getValue(self::CONFIG_PATH_UNSECURE_BASE_URL, $scope, $scopeId); + return rtrim($return, "/") . "/" . ltrim($path, "/"); + } + + /** + * @method getBaseUrl + * @param string $path + * @param string $scope + * @param integer|null $scopeId + * @return string + */ + public function getBaseUrl($path = "", $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + { + return ($this->getUseSecureInFrontend($scope, $scopeId)) ? $this->getSecureBaseUrl($path, $scope, $scopeId) : $this->getUnsecureBaseUrl($path, $scope, $scopeId); + } + + /** + * @method getMediaBaseUrl + * @param string $scope + * @param integer|null $scopeId + * @return string + */ + public function getMediaBaseUrl($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + { + return $this->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA, $scope, $scopeId); } } diff --git a/Model/Observer/Configuration.php b/Model/Observer/Configuration.php index b0a72746..9372cb69 100644 --- a/Model/Observer/Configuration.php +++ b/Model/Observer/Configuration.php @@ -2,13 +2,12 @@ namespace Cloudinary\Cloudinary\Model\Observer; -use Cloudinary\Cloudinary\Core\CloudinaryImageManager; -use Magento\Framework\App\ObjectManager; +use Cloudinary\Cloudinary\Core\AutoUploadMapping\RequestProcessor; +use Magento\Framework\App\Cache\TypeListInterface; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; -use Cloudinary\Cloudinary\Core\AutoUploadMapping\RequestProcessor; use Magento\Framework\Message\ManagerInterface; -use Magento\Framework\UrlInterface; +use Magento\Store\Model\ScopeInterface; class Configuration implements ObserverInterface { @@ -17,23 +16,67 @@ class Configuration implements ObserverInterface /** * @var RequestProcessor */ - private $requestProcessor; + protected $requestProcessor; /** * @var ManagerInterface */ - private $messageManager; + protected $messageManager; + + /** + * @var \Cloudinary\Cloudinary\Model\Configuration + */ + protected $configuration; + + /** + * @var TypeListInterface + */ + protected $cacheTypeList; + + protected $scope = ScopeInterface::SCOPE_STORE; + protected $scopeId = null; + protected $changedPaths = []; /** * @param RequestProcessor $requestProcessor * @param ManagerInterface $messageManager + * @param \Cloudinary\Cloudinary\Model\Configuration $configuration + * @param TypeListInterface $cacheTypeList */ public function __construct( RequestProcessor $requestProcessor, - ManagerInterface $messageManager + ManagerInterface $messageManager, + \Cloudinary\Cloudinary\Model\Configuration $configuration, + TypeListInterface $cacheTypeList ) { $this->requestProcessor = $requestProcessor; $this->messageManager = $messageManager; + $this->configuration = $configuration; + $this->cacheTypeList = $cacheTypeList; + } + + protected function _init(Observer $observer) + { + //Clear config cache if needed + $this->changedPaths = (array) $observer->getEvent()->getChangedPaths(); + if (in_array($this->changedPaths, [ + \Cloudinary\Cloudinary\Model\Configuration::CONFIG_PATH_ENABLED, + \Cloudinary\Cloudinary\Model\Configuration::CONFIG_PATH_ENVIRONMENT_VARIABLE, + \Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration::REQUEST_PATH + ])) { + $this->cleanConfigCache(); + } + + //Get current configuration scope from request & inject to requestProcessor. + $this->scopeId = $observer->getEvent()->getStore(); + $this->scope = ScopeInterface::SCOPE_STORE; + if (!$this->scopeId && ($this->scopeId = $observer->getEvent()->getWebsite())) { + $this->scope = ScopeInterface::SCOPE_WEBSITE; + } + if (!$this->scopeId) { + $this->scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT; + } + $this->requestProcessor->setScope($this->scope)->setScopeId($this->scopeId); } /** @@ -41,24 +84,17 @@ public function __construct( */ public function execute(Observer $observer) { - if (!$this->requestProcessor->handle('media', $this->getMediaBaseUrl())) { + $this->_init($observer); + + if (!$this->requestProcessor->handle('media', $this->configuration->getMediaBaseUrl($this->scope, $this->scopeId))) { $this->messageManager->addErrorMessage(self::AUTO_UPLOAD_SETUP_FAIL_MESSAGE); } } - /** - * @return string - */ - function getMediaBaseUrl() { - /** @var \Magento\Framework\ObjectManagerInterface $om */ - $om = ObjectManager::getInstance(); - - /** @var \Magento\Store\Model\StoreManagerInterface $storeManager */ - $storeManager = $om->get('Magento\Store\Model\StoreManagerInterface'); - - /** @var \Magento\Store\Api\Data\StoreInterface|\Magento\Store\Model\Store $currentStore */ - $currentStore = $storeManager->getStore(); - - return $currentStore->getBaseUrl(UrlInterface::URL_TYPE_MEDIA); + protected function cleanConfigCache() + { + $this->_cacheTypeList->cleanType(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER); + $this->_cacheTypeList->cleanType(\Magento\PageCache\Model\Cache\Type::TYPE_IDENTIFIER); + return $this; } } From db1915af68236894ab4ee571a0ddb460476bbd17 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Thu, 22 Nov 2018 12:09:37 +0200 Subject: [PATCH 008/296] CLOUDINARY-3: Removed website & store scopes from system.xml & cleaned store/website specific configurations (only supports default at the moment). Moved Cloudinary tab outside of Services & added Cloudinary's icon. Made sure that the config cache will be automatically deleted before mapping the media dir after changing the value of cloudinary_auto_upload_mapping_request --- .../AutoUploadConfiguration.php | 21 ++-- Model/Configuration.php | 105 ++++++++---------- Model/Observer/Configuration.php | 29 +---- Model/Template/Filter.php | 4 +- Setup/UpgradeData.php | 52 +++++++++ composer.json | 2 +- etc/adminhtml/system.xml | 37 +++--- etc/module.xml | 2 +- view/adminhtml/web/css/source/_module.less | 11 ++ view/base/web/images/cloudinary_icon.png | Bin 0 -> 2714 bytes 10 files changed, 149 insertions(+), 114 deletions(-) create mode 100644 Setup/UpgradeData.php create mode 100644 view/adminhtml/web/css/source/_module.less create mode 100644 view/base/web/images/cloudinary_icon.png diff --git a/Model/AutoUploadMapping/AutoUploadConfiguration.php b/Model/AutoUploadMapping/AutoUploadConfiguration.php index 29aaffe8..55d3cdbb 100644 --- a/Model/AutoUploadMapping/AutoUploadConfiguration.php +++ b/Model/AutoUploadMapping/AutoUploadConfiguration.php @@ -5,7 +5,6 @@ use Cloudinary\Cloudinary\Core\AutoUploadMapping\AutoUploadConfigurationInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Config\Storage\WriterInterface; -use Magento\Store\Model\ScopeInterface; class AutoUploadConfiguration implements AutoUploadConfigurationInterface { @@ -39,41 +38,41 @@ public function __construct( /** * @return bool */ - public function isActive($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function isActive() { - return $this->configReader->isSetFlag(self::STATE_PATH, $scope, $scopeId); + return $this->configReader->isSetFlag(self::STATE_PATH); } /** * @param bool $state */ - public function setState($state, $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function setState($state) { - $this->setFlag(self::STATE_PATH, $state, $scope, $scopeId); + $this->setFlag(self::STATE_PATH, $state); } /** * @return bool */ - public function getRequestState($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getRequestState() { - return $this->configReader->isSetFlag(self::REQUEST_PATH, $scope, $scopeId); + return $this->configReader->isSetFlag(self::REQUEST_PATH); } /** * @param bool $state */ - public function setRequestState($state, $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function setRequestState($state) { - $this->setFlag(self::REQUEST_PATH, $state, $scope, $scopeId); + $this->setFlag(self::REQUEST_PATH, $state); } /** * @param string $key * @param bool $state */ - private function setFlag($key, $state, $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + private function setFlag($key, $state) { - $this->configWriter->save($key, $state ? self::CONFIG_TRUE : self::CONFIG_FALSE, $scope, $scopeId); + $this->configWriter->save($key, $state ? self::CONFIG_TRUE : self::CONFIG_FALSE); } } diff --git a/Model/Configuration.php b/Model/Configuration.php index 0b86a899..c666cbc8 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -19,7 +19,6 @@ use Magento\Framework\App\Config\Storage\WriterInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Encryption\EncryptorInterface; -use Magento\Store\Model\ScopeInterface; class Configuration implements ConfigurationInterface { @@ -95,46 +94,46 @@ public function __construct( /** * @return Cloud */ - public function getCloud($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getCloud() { - return $this->getEnvironmentVariable($scope, $scopeId)->getCloud(); + return $this->getEnvironmentVariable()->getCloud(); } /** * @return Credentials */ - public function getCredentials($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getCredentials() { - return $this->getEnvironmentVariable($scope, $scopeId)->getCredentials(); + return $this->getEnvironmentVariable()->getCredentials(); } /** * @return Transformation */ - public function getDefaultTransformation($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getDefaultTransformation() { return Transformation::builder() - ->withGravity(Gravity::fromString($this->getDefaultGravity($scope, $scopeId))) - ->withQuality(Quality::fromString($this->getImageQuality($scope, $scopeId))) - ->withFetchFormat(FetchFormat::fromString($this->getFetchFormat($scope, $scopeId))) - ->withFreeform(Freeform::fromString($this->getDefaultGlobalFreeform($scope, $scopeId))) - ->withDpr(Dpr::fromString($this->getImageDpr($scope, $scopeId))); + ->withGravity(Gravity::fromString($this->getDefaultGravity())) + ->withQuality(Quality::fromString($this->getImageQuality())) + ->withFetchFormat(FetchFormat::fromString($this->getFetchFormat())) + ->withFreeform(Freeform::fromString($this->getDefaultGlobalFreeform())) + ->withDpr(Dpr::fromString($this->getImageDpr())); } /** * @return string */ - private function getDefaultGlobalFreeform($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + private function getDefaultGlobalFreeform() { - return (string) $this->configReader->getValue(self::CONFIG_GLOBAL_FREEFORM, $scope, $scopeId); + return (string) $this->configReader->getValue(self::CONFIG_GLOBAL_FREEFORM); } /** * @return boolean */ - public function getCdnSubdomainStatus($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getCdnSubdomainStatus() { - return $this->configReader->isSetFlag(self::CONFIG_CDN_SUBDOMAIN, $scope, $scopeId); + return $this->configReader->isSetFlag(self::CONFIG_CDN_SUBDOMAIN); } /** @@ -142,7 +141,7 @@ public function getCdnSubdomainStatus($scope = ScopeInterface::SCOPE_STORE, $sco */ public function getUserPlatform() { - return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.2', '2.0.0'); + return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.3', '2.0.0'); } /** @@ -156,19 +155,19 @@ public function getUploadConfig() /** * @return boolean */ - public function isEnabled($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function isEnabled() { - return $this->hasEnvironmentVariable() && $this->configReader->isSetFlag(self::CONFIG_PATH_ENABLED, $scope, $scopeId); + return $this->hasEnvironmentVariable() && $this->configReader->isSetFlag(self::CONFIG_PATH_ENABLED); } - public function enable($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function enable() { - $this->configWriter->save(self::CONFIG_PATH_ENABLED, self::SCOPE_ID_ONE, $scope, $scopeId); + $this->configWriter->save(self::CONFIG_PATH_ENABLED, self::SCOPE_ID_ONE); } - public function disable($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function disable() { - $this->configWriter->save(self::CONFIG_PATH_ENABLED, self::SCOPE_ID_ZERO, $scope, $scopeId); + $this->configWriter->save(self::CONFIG_PATH_ENABLED, self::SCOPE_ID_ZERO); } /** @@ -191,53 +190,53 @@ public function getMigratedPath($file) /** * @return string */ - public function getDefaultGravity($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getDefaultGravity() { - return (string) $this->configReader->getValue(self::CONFIG_DEFAULT_GRAVITY, $scope, $scopeId); + return (string) $this->configReader->getValue(self::CONFIG_DEFAULT_GRAVITY); } /** * @return string */ - public function getFetchFormat($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getFetchFormat() { - return $this->configReader->isSetFlag(self::CONFIG_DEFAULT_FETCH_FORMAT, $scope, $scopeId) ? FetchFormat::FETCH_FORMAT_AUTO : ''; + return $this->configReader->isSetFlag(self::CONFIG_DEFAULT_FETCH_FORMAT) ? FetchFormat::FETCH_FORMAT_AUTO : ''; } /** * @return string */ - public function getImageQuality($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getImageQuality() { - return $this->configReader->getValue(self::CONFIG_DEFAULT_QUALITY, $scope, $scopeId); + return $this->configReader->getValue(self::CONFIG_DEFAULT_QUALITY); } /** * @return string */ - public function getImageDpr($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getImageDpr() { - return $this->configReader->getValue(self::CONFIG_DEFAULT_DPR, $scope, $scopeId); + return $this->configReader->getValue(self::CONFIG_DEFAULT_DPR); } /** * @return bool */ - public function hasEnvironmentVariable($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function hasEnvironmentVariable() { - return (bool)$this->configReader->getValue(self::CONFIG_PATH_ENVIRONMENT_VARIABLE, $scope, $scopeId); + return (bool)$this->configReader->getValue(self::CONFIG_PATH_ENVIRONMENT_VARIABLE); } /** * @return CloudinaryEnvironmentVariable */ - private function getEnvironmentVariable($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + private function getEnvironmentVariable() { if (is_null($this->environmentVariable)) { try { $this->environmentVariable = CloudinaryEnvironmentVariable::fromString( $this->decryptor->decrypt( - $this->configReader->getValue(self::CONFIG_PATH_ENVIRONMENT_VARIABLE, $scope, $scopeId) + $this->configReader->getValue(self::CONFIG_PATH_ENVIRONMENT_VARIABLE) ) ); } catch (InvalidCredentials $invalidConfigException) { @@ -250,76 +249,66 @@ private function getEnvironmentVariable($scope = ScopeInterface::SCOPE_STORE, $s /** * @return bool */ - public function getRemoveVersionNumber($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getRemoveVersionNumber() { - return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER, $scope, $scopeId); + return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER); } /** * @return bool */ - public function getUseRootPath($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getUseRootPath() { - return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER, $scope, $scopeId); + return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER); } /** * @method getUseSecureInFrontend - * @param string $scope - * @param integer|null $scopeId * @return string */ - public function getUseSecureInFrontend($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getUseSecureInFrontend() { - return ($this->configReader->getValue(self::CONFIG_PATH_USE_SECURE_IN_FRONTEND, $scope, $scopeId)) ? true : false; + return ($this->configReader->getValue(self::CONFIG_PATH_USE_SECURE_IN_FRONTEND)) ? true : false; } /** * @method getSecureBaseUrl * @param string $path - * @param string $scope - * @param integer|null $scopeId * @return string */ - public function getSecureBaseUrl($path = "", $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getSecureBaseUrl($path = "") { - $return = (string) $this->configReader->getValue(self::CONFIG_PATH_SECURE_BASE_URL, $scope, $scopeId); + $return = (string) $this->configReader->getValue(self::CONFIG_PATH_SECURE_BASE_URL); return rtrim($return, "/") . "/" . ltrim($path, "/"); } /** * @method getUnsecureBaseUrl * @param string $path - * @param string $scope - * @param integer|null $scopeId * @return string */ - public function getUnsecureBaseUrl($path = "", $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getUnsecureBaseUrl($path = "") { - $return = (string) $this->configReader->getValue(self::CONFIG_PATH_UNSECURE_BASE_URL, $scope, $scopeId); + $return = (string) $this->configReader->getValue(self::CONFIG_PATH_UNSECURE_BASE_URL); return rtrim($return, "/") . "/" . ltrim($path, "/"); } /** * @method getBaseUrl * @param string $path - * @param string $scope - * @param integer|null $scopeId * @return string */ - public function getBaseUrl($path = "", $scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getBaseUrl($path = "") { - return ($this->getUseSecureInFrontend($scope, $scopeId)) ? $this->getSecureBaseUrl($path, $scope, $scopeId) : $this->getUnsecureBaseUrl($path, $scope, $scopeId); + return ($this->getUseSecureInFrontend()) ? $this->getSecureBaseUrl($path) : $this->getUnsecureBaseUrl($path); } /** * @method getMediaBaseUrl - * @param string $scope - * @param integer|null $scopeId * @return string */ - public function getMediaBaseUrl($scope = ScopeInterface::SCOPE_STORE, $scopeId = null) + public function getMediaBaseUrl() { - return $this->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA, $scope, $scopeId); + return $this->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA); } } diff --git a/Model/Observer/Configuration.php b/Model/Observer/Configuration.php index 9372cb69..b060417d 100644 --- a/Model/Observer/Configuration.php +++ b/Model/Observer/Configuration.php @@ -7,7 +7,6 @@ use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Message\ManagerInterface; -use Magento\Store\Model\ScopeInterface; class Configuration implements ObserverInterface { @@ -33,8 +32,6 @@ class Configuration implements ObserverInterface */ protected $cacheTypeList; - protected $scope = ScopeInterface::SCOPE_STORE; - protected $scopeId = null; protected $changedPaths = []; /** @@ -55,7 +52,10 @@ public function __construct( $this->cacheTypeList = $cacheTypeList; } - protected function _init(Observer $observer) + /** + * @param Observer $observer + */ + public function execute(Observer $observer) { //Clear config cache if needed $this->changedPaths = (array) $observer->getEvent()->getChangedPaths(); @@ -67,26 +67,7 @@ protected function _init(Observer $observer) $this->cleanConfigCache(); } - //Get current configuration scope from request & inject to requestProcessor. - $this->scopeId = $observer->getEvent()->getStore(); - $this->scope = ScopeInterface::SCOPE_STORE; - if (!$this->scopeId && ($this->scopeId = $observer->getEvent()->getWebsite())) { - $this->scope = ScopeInterface::SCOPE_WEBSITE; - } - if (!$this->scopeId) { - $this->scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT; - } - $this->requestProcessor->setScope($this->scope)->setScopeId($this->scopeId); - } - - /** - * @param Observer $observer - */ - public function execute(Observer $observer) - { - $this->_init($observer); - - if (!$this->requestProcessor->handle('media', $this->configuration->getMediaBaseUrl($this->scope, $this->scopeId))) { + if (!$this->requestProcessor->handle('media', $this->configuration->getMediaBaseUrl())) { $this->messageManager->addErrorMessage(self::AUTO_UPLOAD_SETUP_FAIL_MESSAGE); } } diff --git a/Model/Template/Filter.php b/Model/Template/Filter.php index 316c187c..c916eac6 100644 --- a/Model/Template/Filter.php +++ b/Model/Template/Filter.php @@ -2,9 +2,9 @@ namespace Cloudinary\Cloudinary\Model\Template; -use Magento\Widget\Model\Template\Filter as WidgetFilter; use Cloudinary\Cloudinary\Core\Image\ImageFactory; use Cloudinary\Cloudinary\Core\UrlGenerator; +use Magento\Widget\Model\Template\Filter as WidgetFilter; class Filter extends WidgetFilter { @@ -92,7 +92,7 @@ public function mediaDirective($construction) $image = $this->imageFactory->build( $params['url'], - function() use ($storeManager, $params) { + function () use ($storeManager, $params) { return sprintf( '%s%s', $storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA), diff --git a/Setup/UpgradeData.php b/Setup/UpgradeData.php new file mode 100644 index 00000000..e499e6ac --- /dev/null +++ b/Setup/UpgradeData.php @@ -0,0 +1,52 @@ +_resourceConnection = $resourceConnection; + } + + /** + * {@inheritdoc} + */ + public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + { + $setup->startSetup(); + + if (version_compare($context->getVersion(), '0.6.3') < 0) { + echo "- Reseting configurations for 'website' & 'store' scopes (only supports 'default' at the moment).\n"; + $this->_resourceConnection->getConnection()->delete( + $this->_resourceConnection->getTableName('core_config_data'), + "path LIKE 'cloudinary/%' AND scope != 'default'" + ); + } + + $setup->endSetup(); + } +} diff --git a/composer.json b/composer.json index 52c5480a..947b51bb 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "cloudinary/magento2-module-cloudinary", "description": "Cloudinary Magento 2 Integration.", "type": "magento2-module", - "version": "1.6.2", + "version": "1.6.3", "minimum-stability": "dev", "license": "MIT", "repositories": { diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 56dafa9b..58e36b6b 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -2,9 +2,12 @@ -
- - service + + + +
+ + cloudinary Cloudinary_Cloudinary::config_cloudinary @@ -13,63 +16,63 @@ Magento\Config\Model\Config\Source\Yesno - + - + Set the credentials of your Cloudinary account. Copy the "Environment variable" string from the dashboard of Cloudinary's Management Console. Cloudinary\Cloudinary\Model\Config\Backend\Credentials - + - + Enable multiple sub-domains of image delivery URLs for faster page load speed. Magento\Config\Model\Config\Source\Yesno - + When enabled, Cloudinary will fetch images it does not have from your site automatically without requiring the manual migration process. Magento\Config\Model\Config\Source\Yesno - + - + Automatically deliver images converted to modern image formats based on viewing device and browser. For example, deliver WebP on Chrome and JPEG-XR on Internet Explorer for better performance and user experience. Magento\Config\Model\Config\Source\Yesno - + Adjust quality of generated images to balance between visual quality and file size minimization. The quality is relevant for JPEG and WebP compression levels for example. Cloudinary\Cloudinary\Model\Config\Source\Dropdown\Quality - + Define the part of the image to focus on when cropping images in order to better match your graphic design. Cloudinary\Cloudinary\Model\Config\Source\Dropdown\Gravity - + Use DPR value higher than 1.0 to generate and deliver hi-res images for better visual result on HiDPI devices, such as Retina Display devices (e.g., 2.0). Cloudinary\Cloudinary\Model\Config\Source\Dropdown\Dpr - + Cloudinary\Cloudinary\Model\Config\Backend\Free Cloudinary\Cloudinary\Block\Adminhtml\Form\Field\Free - + - + Remove version number (e.g., ".../v1/...") from URLs Magento\Config\Model\Config\Source\Yesno - + Remove "/image/upload/" from URLs Magento\Config\Model\Config\Source\Yesno diff --git a/etc/module.xml b/etc/module.xml index c432a3c3..68142632 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + diff --git a/view/adminhtml/web/css/source/_module.less b/view/adminhtml/web/css/source/_module.less new file mode 100644 index 00000000..0a1e9b70 --- /dev/null +++ b/view/adminhtml/web/css/source/_module.less @@ -0,0 +1,11 @@ +.cloudinary-icon { + background-image: url('Cloudinary_Cloudinary::images/cloudinary_icon.png'); + background-size: contain; + background-repeat: no-repeat; + background-position: center center; + display: inline-block; + width: 22px; + height: 22px; + margin-right: 5px; + vertical-align: middle; +} diff --git a/view/base/web/images/cloudinary_icon.png b/view/base/web/images/cloudinary_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..36f40545b123a05048d57da40d48d0abb7a1bd2e GIT binary patch literal 2714 zcmaJ@c|4T)AD`NvJ0WS=wmfF6vpJ2)3`5L}D1&AcTG1R2#xXNyCI+dWB2+@fqEc3= zoH<&OtEgmkU}H4oNF?h@uFdfqZMDBYe*K=;>v_J%>-~OzuJ<2Lf~)gRRplR)ArOeF zqXU^HTcNA3qP*-&*r{?~w&;i`9wIu=R}{t&fDl_Yj|l>f!3;l;1~S;;A&)^T2;@5h zj+=+bgX% z1OLhQe+moT!ucSQ1`2tH1T5L$d||7h_ym#wWQceIHy$tOOBY@Jc_N*F^uius7^=RUL`swxNB}! zo!zxZf7)*V|K_(OA3?{(icOkQS1W>+CEfe_>_=;@MHmiN2ZAE)n%C`IqHQ4L#ocZ%If%*j;0Ly>5qfdBDn=sq4G- zN^9QwZJ#Qs#7Ro^7E$};Lfs!whPpJL{&5YjZPVIseNYPZX_8k@*wojmW&F_5b5I}m z9NxVDFA=gZYX;D;8!_YqQV)$V z=Oo^We%%)9BsKcai6(tjYcu=D)Lcx+dW{jMsT$0bTV=hJTiw(I6Jj{-gY-|=uJP3A zqz%aA4b-ER%KvyF(tcq=b zFx=Vx2v<3C>ic@d-vYVgIH~5T6CYp46||qLix_(`#*ETrnQNuB@$!pJkoJS%wVBO) zmiYG!`2)Kf@IEc@W!u!5)>%rhRcWr2KPJCFUmvK()Mi9qG?o__fG2ekI$@J4>fwnN zcGoIM>@@4F1JUQ4;U<)3ZNr})(v1+8pm(x?g5`?r3#|3iZ=3lKo~OYThAY+T1aP7C zlB0DNJ&*3-qR(u8`4h1ZZd#)w>9}xtu27*c_1-Qxe%mC^epg16-CTTI&3?VIdV*$0 z=bxTNslBQVI`9Rb>VvmVI+cp@90n|6W;Mp8&uGhiMT_0BI!H;esn3nnw!?zg%u$<1 zG-h&#_Atf=d%Q4iRO-B?Fj6^T@Eo5~B}pY#D4aM7O)0HG0;O}|+K+ns&`bsO6<1ZK zl_yVpe-3m#9baOh@!&%ZetUs}r^`#Wkv2AFwc{*vRpQ8{q1%LERJx_ zesnCROSran{7gxkxqV|!hLw(Ck>>lbt>ue5eMq5>y5eqzT?v!*9GZT{F^IqrPolOhqf3BQSr|Q`s_56Oir+IuzwdjhXIckO|9e%I??kvLdm zR#^Dd9)3(K+QH>pGB{HV5{oQhMUPSMpd|_t&kpGoQ;&RqA zX4o$QayHkrLYuQ}Gu9TD#{IDe*IeJj`m6Dh;y`+vP&09bQGA|?X+J_bOeHWy`WC;$ zx7?k$v1TEcx%4lC{4N(~g6+n*jo5s=2z&YN-e(T#;pRuJ9B)zkya#4zDP%c4mDi^N z>b;$t4jVabi2lIkE^MsY?CRg5BgL6q JWOwk`e*l5er;Pvr literal 0 HcmV?d00001 From 359e85567be6aca22b36f13f68dc2f2594c22065 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 25 Nov 2018 14:16:50 +0200 Subject: [PATCH 009/296] CLOUDINARY-3: Fixed media dir mapping --- Model/Configuration.php | 54 +++++++++-------------------------------- 1 file changed, 11 insertions(+), 43 deletions(-) diff --git a/Model/Configuration.php b/Model/Configuration.php index c666cbc8..3a9447a8 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -19,6 +19,7 @@ use Magento\Framework\App\Config\Storage\WriterInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Store\Model\StoreManagerInterface; class Configuration implements ConfigurationInterface { @@ -71,24 +72,32 @@ class Configuration implements ConfigurationInterface */ private $autoUploadConfiguration; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * @param ScopeConfigInterface $configReader * @param WriterInterface $configWriter * @param EncryptorInterface $decryptor * @param AutoUploadConfigurationInterface $autoUploadConfiguration + * @param StoreManagerInterface $storeManager */ public function __construct( ScopeConfigInterface $configReader, WriterInterface $configWriter, EncryptorInterface $decryptor, AutoUploadConfigurationInterface $autoUploadConfiguration, - \Psr\Log\LoggerInterface $logger + \Psr\Log\LoggerInterface $logger, + StoreManagerInterface $storeManager ) { $this->configReader = $configReader; $this->configWriter = $configWriter; $this->decryptor = $decryptor; $this->autoUploadConfiguration = $autoUploadConfiguration; $this->logger = $logger; + $this->storeManager = $storeManager; } /** @@ -262,53 +271,12 @@ public function getUseRootPath() return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER); } - /** - * @method getUseSecureInFrontend - * @return string - */ - public function getUseSecureInFrontend() - { - return ($this->configReader->getValue(self::CONFIG_PATH_USE_SECURE_IN_FRONTEND)) ? true : false; - } - - /** - * @method getSecureBaseUrl - * @param string $path - * @return string - */ - public function getSecureBaseUrl($path = "") - { - $return = (string) $this->configReader->getValue(self::CONFIG_PATH_SECURE_BASE_URL); - return rtrim($return, "/") . "/" . ltrim($path, "/"); - } - - /** - * @method getUnsecureBaseUrl - * @param string $path - * @return string - */ - public function getUnsecureBaseUrl($path = "") - { - $return = (string) $this->configReader->getValue(self::CONFIG_PATH_UNSECURE_BASE_URL); - return rtrim($return, "/") . "/" . ltrim($path, "/"); - } - - /** - * @method getBaseUrl - * @param string $path - * @return string - */ - public function getBaseUrl($path = "") - { - return ($this->getUseSecureInFrontend()) ? $this->getSecureBaseUrl($path) : $this->getUnsecureBaseUrl($path); - } - /** * @method getMediaBaseUrl * @return string */ public function getMediaBaseUrl() { - return $this->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA); + return $this->storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA); } } From b3c123672c7d3721a18f1e2132d9edb801cdb32e Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 25 Nov 2018 14:38:08 +0200 Subject: [PATCH 010/296] Updated README & replaced Cloudinary logos --- Block/Adminhtml/Product/Edit/NewVideo.php | 2 +- README.md | 16 +- view/adminhtml/web/css/source/_module.less | 2 +- view/base/web/images/cloudinary_icon.png | Bin 2714 -> 0 bytes .../images/cloudinary_icon_for_white_bg.svg | 69 ++++++ .../images/cloudinary_logo_for_white_bg.jpg | Bin 74836 -> 0 bytes .../images/cloudinary_logo_for_white_bg.svg | 188 ++++++++++++++++ .../cloudinary_vertical_logo_for_white_bg.svg | 203 ++++++++++++++++++ 8 files changed, 477 insertions(+), 3 deletions(-) delete mode 100644 view/base/web/images/cloudinary_icon.png create mode 100644 view/base/web/images/cloudinary_icon_for_white_bg.svg delete mode 100644 view/base/web/images/cloudinary_logo_for_white_bg.jpg create mode 100644 view/base/web/images/cloudinary_logo_for_white_bg.svg create mode 100644 view/base/web/images/cloudinary_vertical_logo_for_white_bg.svg diff --git a/Block/Adminhtml/Product/Edit/NewVideo.php b/Block/Adminhtml/Product/Edit/NewVideo.php index d762b3a3..9dafd369 100644 --- a/Block/Adminhtml/Product/Edit/NewVideo.php +++ b/Block/Adminhtml/Product/Edit/NewVideo.php @@ -66,7 +66,7 @@ public function getWidgetOptions() 'htmlId' => $this->getHtmlId(), 'youTubeApiKey' => $this->mediaHelper->getYouTubeApiKey(), 'videoSelector' => $this->videoSelector, - 'cloudinaryPlaceholder' => $this->getViewFileUrl('Cloudinary_Cloudinary::images/cloudinary_logo_for_white_bg.jpg') + 'cloudinaryPlaceholder' => $this->getViewFileUrl('Cloudinary_Cloudinary::images/cloudinary_vertical_logo_for_white_bg.svg') ] ); } diff --git a/README.md b/README.md index 0c001a45..34bcf740 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,22 @@ Magento 2 module for integration with Cloudinary. --- +## ✓ Installation (using git & composer) +Run the following command under your Magento 2 root dir: + +``` +git clone https://github.com/cloudinary/cloudinary_magento2 app/code/Cloudinary/Cloudinary +composer require cloudinary/cloudinary_php:1.8.0 +php bin/magento setup:upgrade +php bin/magento setup:di:compile +php bin/magento setup:static-content:deploy +php bin/magento cache:flush +``` + +--- + https://www.cloudinary.com/ Copyright © 2018 Cloudinary. All rights reserved. -![Cloudinary Logo](https://cloudinary-res.cloudinary.com/image/upload/v1538583988/cloudinary_logo_for_white_bg.svg) +![Cloudinary Logo](https://cloudinary-res.cloudinary.com/image/upload/c_scale,w_300/v1/logo/for_white_bg/cloudinary_logo_for_white_bg.svg) diff --git a/view/adminhtml/web/css/source/_module.less b/view/adminhtml/web/css/source/_module.less index 0a1e9b70..75c8abd3 100644 --- a/view/adminhtml/web/css/source/_module.less +++ b/view/adminhtml/web/css/source/_module.less @@ -1,5 +1,5 @@ .cloudinary-icon { - background-image: url('Cloudinary_Cloudinary::images/cloudinary_icon.png'); + background-image: url('Cloudinary_Cloudinary::images/cloudinary_icon_for_white_bg.svg'); background-size: contain; background-repeat: no-repeat; background-position: center center; diff --git a/view/base/web/images/cloudinary_icon.png b/view/base/web/images/cloudinary_icon.png deleted file mode 100644 index 36f40545b123a05048d57da40d48d0abb7a1bd2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2714 zcmaJ@c|4T)AD`NvJ0WS=wmfF6vpJ2)3`5L}D1&AcTG1R2#xXNyCI+dWB2+@fqEc3= zoH<&OtEgmkU}H4oNF?h@uFdfqZMDBYe*K=;>v_J%>-~OzuJ<2Lf~)gRRplR)ArOeF zqXU^HTcNA3qP*-&*r{?~w&;i`9wIu=R}{t&fDl_Yj|l>f!3;l;1~S;;A&)^T2;@5h zj+=+bgX% z1OLhQe+moT!ucSQ1`2tH1T5L$d||7h_ym#wWQceIHy$tOOBY@Jc_N*F^uius7^=RUL`swxNB}! zo!zxZf7)*V|K_(OA3?{(icOkQS1W>+CEfe_>_=;@MHmiN2ZAE)n%C`IqHQ4L#ocZ%If%*j;0Ly>5qfdBDn=sq4G- zN^9QwZJ#Qs#7Ro^7E$};Lfs!whPpJL{&5YjZPVIseNYPZX_8k@*wojmW&F_5b5I}m z9NxVDFA=gZYX;D;8!_YqQV)$V z=Oo^We%%)9BsKcai6(tjYcu=D)Lcx+dW{jMsT$0bTV=hJTiw(I6Jj{-gY-|=uJP3A zqz%aA4b-ER%KvyF(tcq=b zFx=Vx2v<3C>ic@d-vYVgIH~5T6CYp46||qLix_(`#*ETrnQNuB@$!pJkoJS%wVBO) zmiYG!`2)Kf@IEc@W!u!5)>%rhRcWr2KPJCFUmvK()Mi9qG?o__fG2ekI$@J4>fwnN zcGoIM>@@4F1JUQ4;U<)3ZNr})(v1+8pm(x?g5`?r3#|3iZ=3lKo~OYThAY+T1aP7C zlB0DNJ&*3-qR(u8`4h1ZZd#)w>9}xtu27*c_1-Qxe%mC^epg16-CTTI&3?VIdV*$0 z=bxTNslBQVI`9Rb>VvmVI+cp@90n|6W;Mp8&uGhiMT_0BI!H;esn3nnw!?zg%u$<1 zG-h&#_Atf=d%Q4iRO-B?Fj6^T@Eo5~B}pY#D4aM7O)0HG0;O}|+K+ns&`bsO6<1ZK zl_yVpe-3m#9baOh@!&%ZetUs}r^`#Wkv2AFwc{*vRpQ8{q1%LERJx_ zesnCROSran{7gxkxqV|!hLw(Ck>>lbt>ue5eMq5>y5eqzT?v!*9GZT{F^IqrPolOhqf3BQSr|Q`s_56Oir+IuzwdjhXIckO|9e%I??kvLdm zR#^Dd9)3(K+QH>pGB{HV5{oQhMUPSMpd|_t&kpGoQ;&RqA zX4o$QayHkrLYuQ}Gu9TD#{IDe*IeJj`m6Dh;y`+vP&09bQGA|?X+J_bOeHWy`WC;$ zx7?k$v1TEcx%4lC{4N(~g6+n*jo5s=2z&YN-e(T#;pRuJ9B)zkya#4zDP%c4mDi^N z>b;$t4jVabi2lIkE^MsY?CRg5BgL6q JWOwk`e*l5er;Pvr diff --git a/view/base/web/images/cloudinary_icon_for_white_bg.svg b/view/base/web/images/cloudinary_icon_for_white_bg.svg new file mode 100644 index 00000000..0dae7c93 --- /dev/null +++ b/view/base/web/images/cloudinary_icon_for_white_bg.svg @@ -0,0 +1,69 @@ + + + + + + + + + + diff --git a/view/base/web/images/cloudinary_logo_for_white_bg.jpg b/view/base/web/images/cloudinary_logo_for_white_bg.jpg deleted file mode 100644 index b448103c3180ab16219c10915cda66206544f5ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74836 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 + + + + + + + + + + + + + + + + + + + + + + diff --git a/view/base/web/images/cloudinary_vertical_logo_for_white_bg.svg b/view/base/web/images/cloudinary_vertical_logo_for_white_bg.svg new file mode 100644 index 00000000..2e4d2e02 --- /dev/null +++ b/view/base/web/images/cloudinary_vertical_logo_for_white_bg.svg @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + From c1b440e5c988ff341f733d33e0a6f288a10e20ce Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 25 Nov 2018 14:51:47 +0200 Subject: [PATCH 011/296] Updated README & replaced Cloudinary logos --- view/adminhtml/web/css/source/_module.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/view/adminhtml/web/css/source/_module.less b/view/adminhtml/web/css/source/_module.less index 75c8abd3..5983d59b 100644 --- a/view/adminhtml/web/css/source/_module.less +++ b/view/adminhtml/web/css/source/_module.less @@ -5,7 +5,7 @@ background-position: center center; display: inline-block; width: 22px; - height: 22px; + height: 18px; margin-right: 5px; - vertical-align: middle; + vertical-align: text-bottom; } From 8ef1ca09870af5e258e30890f6771bcfeb294386 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 25 Nov 2018 15:13:19 +0200 Subject: [PATCH 012/296] CLOUDINARY-3,CLOUDINARY-7: Removed unnecessary logics --- Core/AutoUploadMapping/RequestProcessor.php | 54 ++------------------- 1 file changed, 4 insertions(+), 50 deletions(-) diff --git a/Core/AutoUploadMapping/RequestProcessor.php b/Core/AutoUploadMapping/RequestProcessor.php index 63e644a1..b5508150 100644 --- a/Core/AutoUploadMapping/RequestProcessor.php +++ b/Core/AutoUploadMapping/RequestProcessor.php @@ -2,8 +2,6 @@ namespace Cloudinary\Cloudinary\Core\AutoUploadMapping; -use Magento\Store\Model\ScopeInterface; - class RequestProcessor { /** @@ -16,10 +14,6 @@ class RequestProcessor */ private $apiClient; - protected $scope = ScopeInterface::SCOPE_STORE; - - protected $scopeId = null; - /** * @param AutoUploadConfigurationInterface $configuration * @param ApiClient $apiClient @@ -32,46 +26,6 @@ public function __construct( $this->apiClient = $apiClient; } - /** - * @method setScopeId - * @param string|null $scopeId - * @return $this - */ - public function setScope($scope) - { - $this->scope = $scope; - return $this; - } - - /** - * @method getScope - * @return string|null - */ - public function getScope() - { - return $this->scope; - } - - /** - * @method setScopeId - * @param integer|null $scopeId - * @return $this - */ - public function setScopeId($scopeId) - { - $this->scopeId = $scopeId; - return $this; - } - - /** - * @method getScopeId - * @return integer|null - */ - public function getScopeId($scope) - { - return $this->scopeId; - } - /** * @param string $folder * @param string $url @@ -79,11 +33,11 @@ public function getScopeId($scope) */ public function handle($folder, $url) { - if ($this->configuration->isActive($this->scope, $this->scopeId) == $this->configuration->getRequestState($this->scope, $this->scopeId)) { + if ($this->configuration->isActive() == $this->configuration->getRequestState()) { return true; } - if ($this->configuration->getRequestState($this->scope, $this->scopeId) == AutoUploadConfigurationInterface::ACTIVE) { + if ($this->configuration->getRequestState() == AutoUploadConfigurationInterface::ACTIVE) { return $this->handleActiveRequest($folder, $url); } @@ -102,9 +56,9 @@ private function handleActiveRequest($folder, $url) $result = $this->apiClient->prepareMapping($folder, $url); if ($result) { - $this->configuration->setState(AutoUploadConfigurationInterface::ACTIVE, $this->scope, $this->scopeId); + $this->configuration->setState(AutoUploadConfigurationInterface::ACTIVE); } else { - $this->configuration->setRequestState(AutoUploadConfigurationInterface::INACTIVE, $this->scope, $this->scopeId); + $this->configuration->setRequestState(AutoUploadConfigurationInterface::INACTIVE); } return $result; From 3cebb3faff2e3e264600d5dbb54d88b618f60c65 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Mon, 26 Nov 2018 09:22:33 +0200 Subject: [PATCH 013/296] Fixed version number on UpgradeData script --- Setup/UpgradeData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Setup/UpgradeData.php b/Setup/UpgradeData.php index e499e6ac..3b0ffcda 100644 --- a/Setup/UpgradeData.php +++ b/Setup/UpgradeData.php @@ -39,7 +39,7 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface { $setup->startSetup(); - if (version_compare($context->getVersion(), '0.6.3') < 0) { + if (version_compare($context->getVersion(), '1.6.3') < 0) { echo "- Reseting configurations for 'website' & 'store' scopes (only supports 'default' at the moment).\n"; $this->_resourceConnection->getConnection()->delete( $this->_resourceConnection->getTableName('core_config_data'), From ec2acf9d0f06321b5d5f1f5066c9b83ea41d22f0 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Tue, 27 Nov 2018 14:04:54 +0200 Subject: [PATCH 014/296] CLOUDINARY-17: Added option to configuration in order to allow the admin to bypass the quality transformation code --- Core/Image/Transformation.php | 6 +-- Model/Config/Source/Dropdown/Quality.php | 48 +++++++++++++----------- Model/Configuration.php | 2 +- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/Core/Image/Transformation.php b/Core/Image/Transformation.php index 8ed3b574..bbd9afad 100644 --- a/Core/Image/Transformation.php +++ b/Core/Image/Transformation.php @@ -2,13 +2,13 @@ namespace Cloudinary\Cloudinary\Core\Image; +use Cloudinary\Cloudinary\Core\Image\Transformation\Crop; use Cloudinary\Cloudinary\Core\Image\Transformation\Dimensions; use Cloudinary\Cloudinary\Core\Image\Transformation\Dpr; use Cloudinary\Cloudinary\Core\Image\Transformation\FetchFormat; +use Cloudinary\Cloudinary\Core\Image\Transformation\Freeform; use Cloudinary\Cloudinary\Core\Image\Transformation\Gravity; use Cloudinary\Cloudinary\Core\Image\Transformation\Quality; -use Cloudinary\Cloudinary\Core\Image\Transformation\Crop; -use Cloudinary\Cloudinary\Core\Image\Transformation\Freeform; class Transformation { @@ -87,7 +87,7 @@ public function build() ['raw_transformation' => (string)$this->freeform], [ 'fetch_format' => (string)$this->fetchFormat, - 'quality' => (string)$this->quality, + 'quality' => (string)$this->quality ?: null, 'crop' => (string)$this->crop, 'gravity' => (string)$this->gravity ?: null, 'width' => $this->dimensions ? $this->dimensions->getWidth() : null, diff --git a/Model/Config/Source/Dropdown/Quality.php b/Model/Config/Source/Dropdown/Quality.php index 1b0488da..8d40bcb5 100644 --- a/Model/Config/Source/Dropdown/Quality.php +++ b/Model/Config/Source/Dropdown/Quality.php @@ -8,47 +8,51 @@ class Quality implements OptionSourceInterface { public function toOptionArray() { - return array( - array( + return [ + [ + 'value' => '', + 'label' => 'Magento\'s Default', + ], + [ 'value' => '20', 'label' => '20%', - ), - array( + ], + [ 'value' => '30', 'label' => '30%', - ), - array( + ], + [ 'value' => '40', 'label' => '40%', - ), - array( + ], + [ 'value' => '50', 'label' => '50%', - ), - array( + ], + [ 'value' => '60', 'label' => '60%', - ), - array( + ], + [ 'value' => '70', 'label' => '70%', - ), - array( + ], + [ 'value' => '80', 'label' => '80%', - ), - array( + ], + [ 'value' => '90', 'label' => '90%', - ), - array( + ], + [ 'value' => '100', 'label' => '100%', - ), - array( + ], + [ 'value' => 'auto', 'label' => 'Auto', - ) - ); + ] + ]; } } diff --git a/Model/Configuration.php b/Model/Configuration.php index 3a9447a8..f9ed73c4 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -217,7 +217,7 @@ public function getFetchFormat() */ public function getImageQuality() { - return $this->configReader->getValue(self::CONFIG_DEFAULT_QUALITY); + return (string) $this->configReader->getValue(self::CONFIG_DEFAULT_QUALITY); } /** From fc62e2b5059ac33736c0123b5cd23ecc9453e8a4 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 28 Nov 2018 10:37:15 +0200 Subject: [PATCH 015/296] Updated composer.json & README --- README.md | 5 ++--- composer.json | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 34bcf740..a62b72f9 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,11 @@ Magento 2 module for integration with Cloudinary. --- -## ✓ Installation (using git & composer) +## ✓ Install via composer Run the following command under your Magento 2 root dir: ``` -git clone https://github.com/cloudinary/cloudinary_magento2 app/code/Cloudinary/Cloudinary -composer require cloudinary/cloudinary_php:1.8.0 +composer require cloudinary/cloudinary-magento2 php bin/magento setup:upgrade php bin/magento setup:di:compile php bin/magento setup:static-content:deploy diff --git a/composer.json b/composer.json index 947b51bb..f6290f8d 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "cloudinary/magento2-module-cloudinary", + "name": "cloudinary/cloudinary-magento2", "description": "Cloudinary Magento 2 Integration.", "type": "magento2-module", "version": "1.6.3", From 18ad1fe1812a04cd7065e08d49e2af9f1b96ef7b Mon Sep 17 00:00:00 2001 From: pini-girit Date: Thu, 6 Dec 2018 12:15:12 +0200 Subject: [PATCH 016/296] CLOUDINARY-33: v1.6.4, In order to support Magento 2.3 & also apply best practices - Removed the di override for \Magento\Widget\Model\Template\Filter & Built a plugin for that instead. --- Model/Configuration.php | 10 ++- Model/Template/Filter.php | 97 ++----------------------- Plugin/Widget/Model/Template/Filter.php | 82 +++++++++++++++++++++ composer.json | 2 +- etc/di.xml | 5 +- etc/module.xml | 2 +- 6 files changed, 101 insertions(+), 97 deletions(-) create mode 100644 Plugin/Widget/Model/Template/Filter.php diff --git a/Model/Configuration.php b/Model/Configuration.php index f9ed73c4..386afa78 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -100,6 +100,14 @@ public function __construct( $this->storeManager = $storeManager; } + /** + * @return Cloud + */ + public function getStoreManager() + { + return $this->storeManager; + } + /** * @return Cloud */ @@ -150,7 +158,7 @@ public function getCdnSubdomainStatus() */ public function getUserPlatform() { - return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.3', '2.0.0'); + return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.4', '2.0.0'); } /** diff --git a/Model/Template/Filter.php b/Model/Template/Filter.php index c916eac6..cca3bdc2 100644 --- a/Model/Template/Filter.php +++ b/Model/Template/Filter.php @@ -2,105 +2,18 @@ namespace Cloudinary\Cloudinary\Model\Template; -use Cloudinary\Cloudinary\Core\Image\ImageFactory; -use Cloudinary\Cloudinary\Core\UrlGenerator; use Magento\Widget\Model\Template\Filter as WidgetFilter; class Filter extends WidgetFilter { /** - * @var ImageFactory - */ - private $imageFactory; - - /** - * @var UrlGenerator - */ - private $urlGenerator; - - /** - * @param \Magento\Framework\Stdlib\StringUtils $string - * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Framework\Escaper $escaper - * @param \Magento\Framework\View\Asset\Repository $assetRepo - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Variable\Model\VariableFactory $coreVariableFactory - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\View\LayoutInterface $layout - * @param \Magento\Framework\View\LayoutFactory $layoutFactory - * @param \Magento\Framework\App\State $appState - * @param \Magento\Framework\UrlInterface $urlModel - * @param \Pelago\Emogrifier $emogrifier - * @param \Magento\Email\Model\Source\Variables $configVariables - * @param \Magento\Widget\Model\ResourceModel\Widget $widgetResource - * @param \Magento\Widget\Model\Widget $widget - * @param ImageFactory $imageFactory - * @param UrlGenerator $urlGenerator - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - public function __construct( - \Magento\Framework\Stdlib\StringUtils $string, - \Psr\Log\LoggerInterface $logger, - \Magento\Framework\Escaper $escaper, - \Magento\Framework\View\Asset\Repository $assetRepo, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Variable\Model\VariableFactory $coreVariableFactory, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\View\LayoutInterface $layout, - \Magento\Framework\View\LayoutFactory $layoutFactory, - \Magento\Framework\App\State $appState, - \Magento\Framework\UrlInterface $urlModel, - \Pelago\Emogrifier $emogrifier, - \Magento\Email\Model\Source\Variables $configVariables, - \Magento\Widget\Model\ResourceModel\Widget $widgetResource, - \Magento\Widget\Model\Widget $widget, - ImageFactory $imageFactory, - UrlGenerator $urlGenerator - ) { - $this->imageFactory = $imageFactory; - $this->urlGenerator = $urlGenerator; - - parent::__construct( - $string, - $logger, - $escaper, - $assetRepo, - $scopeConfig, - $coreVariableFactory, - $storeManager, - $layout, - $layoutFactory, - $appState, - $urlModel, - $emogrifier, - $configVariables, - $widgetResource, - $widget - ); - } - - /** - * Retrieve media file URL directive + * Return associative array of parameters *exposing $this->getParameters(). * - * @param string[] $construction - * @return string + * @param string $value raw parameters + * @return array */ - public function mediaDirective($construction) + public function getParams($value) { - $params = $this->getParameters($construction[2]); - $storeManager = $this->_storeManager; - - $image = $this->imageFactory->build( - $params['url'], - function () use ($storeManager, $params) { - return sprintf( - '%s%s', - $storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA), - $params['url'] - ); - } - ); - - return $this->urlGenerator->generateFor($image); + return $this->getParameters($value); } } diff --git a/Plugin/Widget/Model/Template/Filter.php b/Plugin/Widget/Model/Template/Filter.php new file mode 100644 index 00000000..2e67cba3 --- /dev/null +++ b/Plugin/Widget/Model/Template/Filter.php @@ -0,0 +1,82 @@ +_storeManager = $storeManager; + $this->_imageFactory = $imageFactory; + $this->_urlGenerator = $urlGenerator; + $this->_cloudinaryWidgetFilter = $cloudinaryWidgetFilter; + } + + /** + * Around retrieve media file URL directive + * + * @param \Magento\Widget\Model\Template\Filter $widgetFilter + * @param callable $proceed + * @param string[] $construction + * @return string + */ + public function aroundMediaDirective(\Magento\Widget\Model\Template\Filter $widgetFilter, callable $proceed, $construction) + { + $params = $this->_cloudinaryWidgetFilter->getParams($construction[2]); + if (!isset($params['url'])) { + return $proceed($construction); + } + + $storeManager = $this->_storeManager; + + $image = $this->_imageFactory->build( + $params['url'], + function () use ($storeManager, $params) { + return sprintf( + '%s%s', + $storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA), + $params['url'] + ); + } + ); + + return $this->_urlGenerator->generateFor($image); + } +} diff --git a/composer.json b/composer.json index f6290f8d..f8ecab81 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "cloudinary/cloudinary-magento2", "description": "Cloudinary Magento 2 Integration.", "type": "magento2-module", - "version": "1.6.3", + "version": "1.6.4", "minimum-stability": "dev", "license": "MIT", "repositories": { diff --git a/etc/di.xml b/etc/di.xml index de25382b..75f85a11 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -35,8 +35,9 @@ - + + + diff --git a/etc/module.xml b/etc/module.xml index 68142632..6f38d9fe 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + From 809ed46b7c3877f8650e221bc9917cafa86622d7 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Mon, 10 Dec 2018 15:38:59 +0200 Subject: [PATCH 017/296] CLOUDINARY-19: changed the default 'pad' cropping mode to 'lpad' --- Core/Image/Transformation.php | 4 ++-- Core/Image/Transformation/Crop.php | 2 +- Plugin/ImageHelper.php | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Core/Image/Transformation.php b/Core/Image/Transformation.php index bbd9afad..5a588f5e 100644 --- a/Core/Image/Transformation.php +++ b/Core/Image/Transformation.php @@ -23,14 +23,14 @@ class Transformation public function __construct() { - $this->crop = 'pad'; + $this->crop = 'lpad'; $this->flags = []; } public function withGravity(Gravity $gravity) { $this->gravity = $gravity; - $this->crop = ((string)$gravity) ? 'crop' : 'pad'; + $this->crop = ((string)$gravity) ? 'crop' : 'lpad'; return $this; } diff --git a/Core/Image/Transformation/Crop.php b/Core/Image/Transformation/Crop.php index 16095333..e48560ab 100644 --- a/Core/Image/Transformation/Crop.php +++ b/Core/Image/Transformation/Crop.php @@ -4,7 +4,7 @@ class Crop { - const PAD = 'pad'; + const PAD = 'lpad'; const FIT = 'fit'; private $value; diff --git a/Plugin/ImageHelper.php b/Plugin/ImageHelper.php index ebfdebb4..6cb8141d 100644 --- a/Plugin/ImageHelper.php +++ b/Plugin/ImageHelper.php @@ -2,16 +2,16 @@ namespace Cloudinary\Cloudinary\Plugin; -use Cloudinary\Cloudinary\Core\Image\Transformation; +use Cloudinary\Cloudinary\Core\ConfigurationInterface; use Cloudinary\Cloudinary\Core\Image\ImageFactory; -use Cloudinary\Cloudinary\Core\Image\Transformation\Dimensions; +use Cloudinary\Cloudinary\Core\Image\Transformation; use Cloudinary\Cloudinary\Core\Image\Transformation\Crop; +use Cloudinary\Cloudinary\Core\Image\Transformation\Dimensions; use Cloudinary\Cloudinary\Core\UrlGenerator; -use Cloudinary\Cloudinary\Core\ConfigurationInterface; -use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Catalog\Helper\Image as CatalogImageHelper; use Cloudinary\Cloudinary\Model\Transformation as TransformationModel; use Cloudinary\Cloudinary\Model\TransformationFactory; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Helper\Image as CatalogImageHelper; class ImageHelper { @@ -158,7 +158,7 @@ private function createTransformation(CatalogImageHelper $helper) $transform = $this->configuration->getDefaultTransformation()->withDimensions($dimensions); if ($this->keepFrame) { - $transform->withCrop(Crop::fromString('pad')) + $transform->withCrop(Crop::fromString('lpad')) ->withDimensions(Dimensions::squareMissingDimension($dimensions)); } else { $transform->withCrop(Crop::fromString('fit')); From a159bb3f572e0f6f7e42f8513bbe577fd7c7c3d0 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Thu, 13 Dec 2018 14:19:04 +0200 Subject: [PATCH 018/296] CLOUDINARY-4: Adde support for 'Use signed URLs' (dafault: false) --- Core/CloudinaryImageProvider.php | 6 +++++- Model/Configuration.php | 9 +++++++++ etc/adminhtml/system.xml | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Core/CloudinaryImageProvider.php b/Core/CloudinaryImageProvider.php index f54bc3ee..64aabeba 100644 --- a/Core/CloudinaryImageProvider.php +++ b/Core/CloudinaryImageProvider.php @@ -92,7 +92,11 @@ public function upload(Image $image) */ public function retrieveTransformed(Image $image, Transformation $transformation) { - $imagePath = \cloudinary_url($image->getId(), ['transformation' => $transformation->build(), 'secure' => true]); + $imagePath = \cloudinary_url($image->getId(), [ + 'transformation' => $transformation->build(), + 'secure' => true, + 'sign_url' => $this->configuration->getUseSignedUrls() + ]); if ($this->configuration->getUseRootPath()) { $imagePath = str_replace(".com/{$this->configuration->getCloud()}/image/upload/", ".com/{$this->configuration->getCloud()}/", $imagePath); diff --git a/Model/Configuration.php b/Model/Configuration.php index 386afa78..6e7bd5da 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -40,6 +40,7 @@ class Configuration implements ConfigurationInterface const CONFIG_PATH_SECURE_BASE_URL = "web/secure/base_url"; const CONFIG_PATH_UNSECURE_BASE_URL = "web/unsecure/base_url"; const CONFIG_PATH_USE_SECURE_IN_FRONTEND = "web/secure/use_in_frontend"; + const CONFIG_PATH_USE_SIGNED_URLS = 'cloudinary/advanced/use_signed_urls'; const USE_FILENAME = true; const UNIQUE_FILENAME = false; @@ -279,6 +280,14 @@ public function getUseRootPath() return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER); } + /** + * @return bool + */ + public function getUseSignedUrls() + { + return (bool) Mage::getStoreConfig(self::CONFIG_PATH_USE_SIGNED_URLS); + } + /** * @method getMediaBaseUrl * @return string diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 58e36b6b..5dda4298 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -77,6 +77,11 @@ Remove "/image/upload/" from URLs Magento\Config\Model\Config\Source\Yesno + + + A signed Cloudinary image delivery URL is a dynamic URL that has its signature validated before making it available for view + Magento\Config\Model\Config\Source\Yesno +
From b872954e563dde6973231216eb0f340da31edff2 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Thu, 13 Dec 2018 16:33:35 +0200 Subject: [PATCH 019/296] CLOUDINARY-4: Adde support for 'Use signed URLs' (dafault: false) --- Model/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/Configuration.php b/Model/Configuration.php index 6e7bd5da..10260002 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -285,7 +285,7 @@ public function getUseRootPath() */ public function getUseSignedUrls() { - return (bool) Mage::getStoreConfig(self::CONFIG_PATH_USE_SIGNED_URLS); + return (bool) $this->configReader->getValue(self::CONFIG_PATH_USE_SIGNED_URLS); } /** From 421b7f18f970cdbcc511d82b35edc8a981706988 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Thu, 13 Dec 2018 17:25:46 +0200 Subject: [PATCH 020/296] CLOUDINARY-4: Adde support for 'Use signed URLs' (dafault: false) + updated module version to v1.6.5 --- Model/Configuration.php | 2 +- composer.json | 2 +- etc/module.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Model/Configuration.php b/Model/Configuration.php index 10260002..ce647b8e 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -159,7 +159,7 @@ public function getCdnSubdomainStatus() */ public function getUserPlatform() { - return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.4', '2.0.0'); + return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.5', '2.0.0'); } /** diff --git a/composer.json b/composer.json index f8ecab81..84c397c2 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "cloudinary/cloudinary-magento2", "description": "Cloudinary Magento 2 Integration.", "type": "magento2-module", - "version": "1.6.4", + "version": "1.6.5", "minimum-stability": "dev", "license": "MIT", "repositories": { diff --git a/etc/module.xml b/etc/module.xml index 6f38d9fe..2b6982b8 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + From 5ca9e91970b573c7ad255a79c9288bdf2d05f08e Mon Sep 17 00:00:00 2001 From: pini-girit Date: Tue, 18 Dec 2018 09:26:15 +0200 Subject: [PATCH 021/296] CLOUDINARY-41: Built a plugin for \Magento\Catalog\Block\Product\ImageFactory::create() in order to use cloudinary url on products, since stopped working on Magento2.3 (It's not going through \Magento\Catalog\Helper\Image::getUrl() so the old plugin has no effect) --- Plugin/Catalog/Block/Product/ImageFactory.php | 135 ++++++++++++++++++ Plugin/FileRemover.php | 2 +- Plugin/FileUploader.php | 4 +- Plugin/ImageHelper.php | 3 + Plugin/MediaConfig.php | 6 +- Plugin/Widget/Model/Template/Filter.php | 11 ++ etc/di.xml | 8 ++ 7 files changed, 163 insertions(+), 6 deletions(-) create mode 100644 Plugin/Catalog/Block/Product/ImageFactory.php diff --git a/Plugin/Catalog/Block/Product/ImageFactory.php b/Plugin/Catalog/Block/Product/ImageFactory.php new file mode 100644 index 00000000..b481b00b --- /dev/null +++ b/Plugin/Catalog/Block/Product/ImageFactory.php @@ -0,0 +1,135 @@ +cloudinaryImageFactory = $cloudinaryImageFactory; + $this->urlGenerator = $urlGenerator; + $this->configuration = $configuration; + $this->transformationModel = $transformationFactory->create(); + $this->dimensions = null; + $this->imageFile = null; + $this->keepFrame = true; + } + + /** + * Create image block from product + * @param CatalogImageFactory $catalogImageFactory + * @param callable $proceed + * @param Product $product + * @param string $imageId + * @param array|null $attributes + * @return ImageBlock + */ + public function aroundCreate(CatalogImageFactory $catalogImageFactory, callable $proceed, Product $product, string $imageId, array $attributes = null): ImageBlock + { + $imageBlock = $proceed($product, $imageId, $attributes); + if (!$this->configuration->isEnabled()) { + return $imageBlock; + } + + if (strpos($imageBlock->getImageUrl(), $this->configuration->getMediaBaseUrl()) === 0) { + $imagePath = preg_replace('/^' . preg_quote($this->configuration->getMediaBaseUrl(), '/') . '/', '', $imageBlock->getImageUrl()); + $imagePath = preg_replace('/\/cache\/[a-f0-9]{32}\//', '/', $imagePath); + $image = $this->cloudinaryImageFactory->build($imagePath, $proceed); + $generatedImageUrl = $this->urlGenerator->generateFor( + $image, + $this->transformationModel->addFreeformTransformationForImage( + $this->createTransformation($imageBlock), + $imagePath + ) + ); + $imageBlock->setOriginalImageUrl($imageBlock->setImageUrl()); + $imageBlock->setImageUrl($generatedImageUrl); + } + + return $imageBlock; + } + + /** + * @param ImageBlock $imageBlock + * @return Transformation + */ + private function createTransformation(ImageBlock $imageBlock) + { + $dimensions = $this->dimensions ?: Dimensions::fromWidthAndHeight($imageBlock->getWidth(), $imageBlock->getHeight()); + + $transform = $this->configuration->getDefaultTransformation()->withDimensions($dimensions); + + if ($this->keepFrame) { + $transform->withCrop(Crop::fromString('lpad')) + ->withDimensions(Dimensions::squareMissingDimension($dimensions)); + } else { + $transform->withCrop(Crop::fromString('fit')); + } + + return $transform; + } +} diff --git a/Plugin/FileRemover.php b/Plugin/FileRemover.php index c5350f90..68321769 100644 --- a/Plugin/FileRemover.php +++ b/Plugin/FileRemover.php @@ -2,8 +2,8 @@ namespace Cloudinary\Cloudinary\Plugin; -use Cloudinary\Cloudinary\Core\Image; use Cloudinary\Cloudinary\Core\CloudinaryImageManager; +use Cloudinary\Cloudinary\Core\Image; use Magento\Cms\Model\Wysiwyg\Images\Storage; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; diff --git a/Plugin/FileUploader.php b/Plugin/FileUploader.php index 4eeec6e7..c0c08b55 100644 --- a/Plugin/FileUploader.php +++ b/Plugin/FileUploader.php @@ -2,8 +2,8 @@ namespace Cloudinary\Cloudinary\Plugin; -use Cloudinary\Cloudinary\Core\Image; use Cloudinary\Cloudinary\Core\CloudinaryImageManager; +use Cloudinary\Cloudinary\Core\Image; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\File\Uploader; @@ -41,11 +41,9 @@ public function afterSave(Uploader $uploader, $result) $filepath = $this->absoluteFilePath($result); if ($this->isMediaFilePath($filepath) && !$this->isMediaTmpFilePath($filepath)) { - $this->cloudinaryImageManager->uploadAndSynchronise( Image::fromPath($filepath, $this->mediaRelativePath($filepath)) ); - } return $result; diff --git a/Plugin/ImageHelper.php b/Plugin/ImageHelper.php index 6cb8141d..45db72cb 100644 --- a/Plugin/ImageHelper.php +++ b/Plugin/ImageHelper.php @@ -134,6 +134,9 @@ public function beforeKeepFrame(CatalogImageHelper $helper, $flag) */ public function aroundGetUrl(CatalogImageHelper $helper, \Closure $originalMethod) { + if (!$this->configuration->isEnabled()) { + return $originalMethod(); + } $imagePath = $this->imageFile ?: $this->product->getData($helper->getType()); $image = $this->imageFactory->build(sprintf('catalog/product%s', $imagePath), $originalMethod); diff --git a/Plugin/MediaConfig.php b/Plugin/MediaConfig.php index a711bdea..b5771afc 100644 --- a/Plugin/MediaConfig.php +++ b/Plugin/MediaConfig.php @@ -39,9 +39,11 @@ public function aroundGetMediaUrl(CatalogMediaConfig $mediaConfig, \Closure $ori { $image = $this->imageFactory->build( $mediaConfig->getBaseMediaPath() . $file, - function() use ($originalMethod, $file) { return $originalMethod($file); } + function () use ($originalMethod, $file) { + return $originalMethod($file); + } ); - return $this->urlGenerator->generateFor($image); + return $this->urlGenerator->generateFor($image); } } diff --git a/Plugin/Widget/Model/Template/Filter.php b/Plugin/Widget/Model/Template/Filter.php index 2e67cba3..0fb6ba30 100644 --- a/Plugin/Widget/Model/Template/Filter.php +++ b/Plugin/Widget/Model/Template/Filter.php @@ -2,6 +2,7 @@ namespace Cloudinary\Cloudinary\Plugin\Widget\Model\Template; +use Cloudinary\Cloudinary\Core\ConfigurationInterface; use Cloudinary\Cloudinary\Core\Image\ImageFactory; use Cloudinary\Cloudinary\Core\UrlGenerator; use Cloudinary\Cloudinary\Model\Template\Filter as CloudinaryWidgetFilter; @@ -26,6 +27,11 @@ class Filter */ protected $_urlGenerator; + /** + * @var ConfigurationInterface + */ + protected $_configuration; + /** * @var CloudinaryWidgetFilter */ @@ -41,11 +47,13 @@ public function __construct( StoreManagerInterface $storeManager, ImageFactory $imageFactory, UrlGenerator $urlGenerator, + ConfigurationInterface $configuration, CloudinaryWidgetFilter $cloudinaryWidgetFilter ) { $this->_storeManager = $storeManager; $this->_imageFactory = $imageFactory; $this->_urlGenerator = $urlGenerator; + $this->_configuration = $configuration; $this->_cloudinaryWidgetFilter = $cloudinaryWidgetFilter; } @@ -59,6 +67,9 @@ public function __construct( */ public function aroundMediaDirective(\Magento\Widget\Model\Template\Filter $widgetFilter, callable $proceed, $construction) { + if (!$this->_configuration->isEnabled()) { + return $proceed($construction); + } $params = $this->_cloudinaryWidgetFilter->getParams($construction[2]); if (!isset($params['url'])) { return $proceed($construction); diff --git a/etc/di.xml b/etc/di.xml index 75f85a11..a78654e4 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -43,6 +43,14 @@ + + + + + + From 42c15026690e7a3d63f6e38de094e2c54ad7d782 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Tue, 18 Dec 2018 09:27:11 +0200 Subject: [PATCH 022/296] Updated README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a62b72f9..3ed1ef7d 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,11 @@ Run the following command under your Magento 2 root dir: ``` composer require cloudinary/cloudinary-magento2 +php bin/magento maintenance:enable php bin/magento setup:upgrade php bin/magento setup:di:compile php bin/magento setup:static-content:deploy +php bin/magento maintenance:disable php bin/magento cache:flush ``` From 82bec49b605841e845b8141a5895a71a182516a5 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Tue, 18 Dec 2018 09:53:03 +0200 Subject: [PATCH 023/296] CLOUDINARY-41: Built a plugin for \Magento\Catalog\Block\Product\ImageFactory::create() in order to use cloudinary url on products, since stopped working on Magento2.3 (It's not going through \Magento\Catalog\Helper\Image::getUrl() so the old plugin has no effect) --- etc/di.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/etc/di.xml b/etc/di.xml index a78654e4..2f8f505a 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -43,10 +43,6 @@ - - From f1dc42630369accc1083560f6cd72fbcbdb701bc Mon Sep 17 00:00:00 2001 From: pini-girit Date: Tue, 18 Dec 2018 14:06:30 +0200 Subject: [PATCH 024/296] CLOUDINARY-40: Added support for cloudless URLs on 'Use Root Path' --- Core/CloudinaryImageProvider.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/CloudinaryImageProvider.php b/Core/CloudinaryImageProvider.php index 64aabeba..73de7b59 100644 --- a/Core/CloudinaryImageProvider.php +++ b/Core/CloudinaryImageProvider.php @@ -99,7 +99,11 @@ public function retrieveTransformed(Image $image, Transformation $transformation ]); if ($this->configuration->getUseRootPath()) { - $imagePath = str_replace(".com/{$this->configuration->getCloud()}/image/upload/", ".com/{$this->configuration->getCloud()}/", $imagePath); + if (strpos($imagePath, ".com/{$this->configuration->getCloud()}/image/upload/") !== false) { + $imagePath = str_replace(".com/{$this->configuration->getCloud()}/image/upload/", ".com/{$this->configuration->getCloud()}/", $imagePath); + } elseif (strpos($imagePath, ".com/image/upload/") !== false) { + $imagePath = str_replace(".com/image/upload/", ".com/", $imagePath); + } } if ($this->configuration->getRemoveVersionNumber()) { From a92d3437bcd92fe205d1673020d6297b5739bc61 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Tue, 18 Dec 2018 15:29:25 +0200 Subject: [PATCH 025/296] CLOUDINARY-40: Added support for cloudless URLs on 'Use Root Path' --- Model/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/Configuration.php b/Model/Configuration.php index ce647b8e..6c58db0d 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -277,7 +277,7 @@ public function getRemoveVersionNumber() */ public function getUseRootPath() { - return (bool) $this->configReader->getValue(self::CONFIG_PATH_REMOVE_VERSION_NUMBER); + return (bool) $this->configReader->getValue(self::CONFIG_PATH_USE_ROOT_PATH); } /** From 182180b584760bd01b192197c6fee3d83e0ac15c Mon Sep 17 00:00:00 2001 From: pini-girit Date: Tue, 18 Dec 2018 17:43:03 +0200 Subject: [PATCH 026/296] CLOUDINARY-40: Added support for cloudless URLs on 'Use Root Path' --- Core/CloudinaryImageProvider.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/CloudinaryImageProvider.php b/Core/CloudinaryImageProvider.php index 73de7b59..22a57dfc 100644 --- a/Core/CloudinaryImageProvider.php +++ b/Core/CloudinaryImageProvider.php @@ -99,10 +99,10 @@ public function retrieveTransformed(Image $image, Transformation $transformation ]); if ($this->configuration->getUseRootPath()) { - if (strpos($imagePath, ".com/{$this->configuration->getCloud()}/image/upload/") !== false) { - $imagePath = str_replace(".com/{$this->configuration->getCloud()}/image/upload/", ".com/{$this->configuration->getCloud()}/", $imagePath); - } elseif (strpos($imagePath, ".com/image/upload/") !== false) { - $imagePath = str_replace(".com/image/upload/", ".com/", $imagePath); + if (strpos($imagePath, "cloudinary.com/{$this->configuration->getCloud()}/image/upload/") !== false) { + $imagePath = str_replace("cloudinary.com/{$this->configuration->getCloud()}/image/upload/", "cloudinary.com/{$this->configuration->getCloud()}/", $imagePath); + } elseif (strpos($imagePath, "cloudinary.com/image/upload/") !== false) { + $imagePath = str_replace("cloudinary.com/image/upload/", "cloudinary.com/", $imagePath); } } From b2205011cb4d4cdc46aaeeb7676c138bca0351ce Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 11:09:22 +0200 Subject: [PATCH 027/296] CLOUDINARY-9: reset autoUpload state on save... --- Model/Observer/Configuration.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Model/Observer/Configuration.php b/Model/Observer/Configuration.php index b060417d..c2a199fc 100644 --- a/Model/Observer/Configuration.php +++ b/Model/Observer/Configuration.php @@ -3,6 +3,8 @@ namespace Cloudinary\Cloudinary\Model\Observer; use Cloudinary\Cloudinary\Core\AutoUploadMapping\RequestProcessor; +use Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration; +use Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfigurationInterface; use Magento\Framework\App\Cache\TypeListInterface; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; @@ -27,6 +29,11 @@ class Configuration implements ObserverInterface */ protected $configuration; + /** + * @var AutoUploadConfiguration + */ + protected $autoUploadConfiguration; + /** * @var TypeListInterface */ @@ -38,17 +45,20 @@ class Configuration implements ObserverInterface * @param RequestProcessor $requestProcessor * @param ManagerInterface $messageManager * @param \Cloudinary\Cloudinary\Model\Configuration $configuration + * @param AutoUploadConfiguration $autoUploadConfiguration * @param TypeListInterface $cacheTypeList */ public function __construct( RequestProcessor $requestProcessor, ManagerInterface $messageManager, \Cloudinary\Cloudinary\Model\Configuration $configuration, + AutoUploadConfiguration $autoUploadConfiguration, TypeListInterface $cacheTypeList ) { $this->requestProcessor = $requestProcessor; $this->messageManager = $messageManager; $this->configuration = $configuration; + $this->autoUploadConfiguration = $autoUploadConfiguration; $this->cacheTypeList = $cacheTypeList; } @@ -64,6 +74,7 @@ public function execute(Observer $observer) \Cloudinary\Cloudinary\Model\Configuration::CONFIG_PATH_ENVIRONMENT_VARIABLE, \Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration::REQUEST_PATH ])) { + $this->autoUploadConfiguration->setState(AutoUploadConfigurationInterface::INACTIVE); $this->cleanConfigCache(); } From 57d35e832d65cc6f15465b8ff2ce2730232f5ace Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 11:25:22 +0200 Subject: [PATCH 028/296] CLOUDINARY-9: reset autoUpload state on save... + another fix for CLOUDINARY-7 --- Model/Observer/Configuration.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Model/Observer/Configuration.php b/Model/Observer/Configuration.php index c2a199fc..c858dc16 100644 --- a/Model/Observer/Configuration.php +++ b/Model/Observer/Configuration.php @@ -2,9 +2,9 @@ namespace Cloudinary\Cloudinary\Model\Observer; +use Cloudinary\Cloudinary\Core\AutoUploadMapping\AutoUploadConfigurationInterface; use Cloudinary\Cloudinary\Core\AutoUploadMapping\RequestProcessor; use Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration; -use Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfigurationInterface; use Magento\Framework\App\Cache\TypeListInterface; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; @@ -69,11 +69,11 @@ public function execute(Observer $observer) { //Clear config cache if needed $this->changedPaths = (array) $observer->getEvent()->getChangedPaths(); - if (in_array($this->changedPaths, [ + if (count(array_intersect($this->changedPaths, [ \Cloudinary\Cloudinary\Model\Configuration::CONFIG_PATH_ENABLED, \Cloudinary\Cloudinary\Model\Configuration::CONFIG_PATH_ENVIRONMENT_VARIABLE, \Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration::REQUEST_PATH - ])) { + ])) > 0) { $this->autoUploadConfiguration->setState(AutoUploadConfigurationInterface::INACTIVE); $this->cleanConfigCache(); } @@ -85,8 +85,8 @@ public function execute(Observer $observer) protected function cleanConfigCache() { - $this->_cacheTypeList->cleanType(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER); - $this->_cacheTypeList->cleanType(\Magento\PageCache\Model\Cache\Type::TYPE_IDENTIFIER); + $this->cacheTypeList->cleanType(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER); + $this->cacheTypeList->cleanType(\Magento\PageCache\Model\Cache\Type::TYPE_IDENTIFIER); return $this; } } From 744cb1315d1bc972656a9f20c1d374c94145e664 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 11:27:41 +0200 Subject: [PATCH 029/296] CLOUDINARY-9: reset autoUpload state on save... + another fix for CLOUDINARY-7 --- Model/Observer/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/Observer/Configuration.php b/Model/Observer/Configuration.php index c858dc16..0443bb6a 100644 --- a/Model/Observer/Configuration.php +++ b/Model/Observer/Configuration.php @@ -86,7 +86,7 @@ public function execute(Observer $observer) protected function cleanConfigCache() { $this->cacheTypeList->cleanType(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER); - $this->cacheTypeList->cleanType(\Magento\PageCache\Model\Cache\Type::TYPE_IDENTIFIER); + //$this->cacheTypeList->cleanType(\Magento\PageCache\Model\Cache\Type::TYPE_IDENTIFIER); return $this; } } From 84d6282537ecb4a418d14985dc585f9b6bd13c0e Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 11:28:15 +0200 Subject: [PATCH 030/296] CLOUDINARY-9: reset autoUpload state on save... + another fix for CLOUDINARY-7 --- Model/Observer/Configuration.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Model/Observer/Configuration.php b/Model/Observer/Configuration.php index 0443bb6a..815d70d0 100644 --- a/Model/Observer/Configuration.php +++ b/Model/Observer/Configuration.php @@ -86,7 +86,6 @@ public function execute(Observer $observer) protected function cleanConfigCache() { $this->cacheTypeList->cleanType(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER); - //$this->cacheTypeList->cleanType(\Magento\PageCache\Model\Cache\Type::TYPE_IDENTIFIER); return $this; } } From 81a328492bb9bc42d667c45002565f637a5dc24d Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 12:08:07 +0200 Subject: [PATCH 031/296] CLOUDINARY-41: one more fallback --- Plugin/Catalog/Block/Product/ImageFactory.php | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/Plugin/Catalog/Block/Product/ImageFactory.php b/Plugin/Catalog/Block/Product/ImageFactory.php index b481b00b..5bd67a5a 100644 --- a/Plugin/Catalog/Block/Product/ImageFactory.php +++ b/Plugin/Catalog/Block/Product/ImageFactory.php @@ -95,19 +95,23 @@ public function aroundCreate(CatalogImageFactory $catalogImageFactory, callable return $imageBlock; } - if (strpos($imageBlock->getImageUrl(), $this->configuration->getMediaBaseUrl()) === 0) { - $imagePath = preg_replace('/^' . preg_quote($this->configuration->getMediaBaseUrl(), '/') . '/', '', $imageBlock->getImageUrl()); - $imagePath = preg_replace('/\/cache\/[a-f0-9]{32}\//', '/', $imagePath); - $image = $this->cloudinaryImageFactory->build($imagePath, $proceed); - $generatedImageUrl = $this->urlGenerator->generateFor( - $image, - $this->transformationModel->addFreeformTransformationForImage( - $this->createTransformation($imageBlock), - $imagePath - ) - ); - $imageBlock->setOriginalImageUrl($imageBlock->setImageUrl()); - $imageBlock->setImageUrl($generatedImageUrl); + try { + if (strpos($imageBlock->getImageUrl(), $this->configuration->getMediaBaseUrl()) === 0) { + $imagePath = preg_replace('/^' . preg_quote($this->configuration->getMediaBaseUrl(), '/') . '/', '', $imageBlock->getImageUrl()); + $imagePath = preg_replace('/\/cache\/[a-f0-9]{32}\//', '/', $imagePath); + $image = $this->cloudinaryImageFactory->build($imagePath, $proceed); + $generatedImageUrl = $this->urlGenerator->generateFor( + $image, + $this->transformationModel->addFreeformTransformationForImage( + $this->createTransformation($imageBlock), + $imagePath + ) + ); + $imageBlock->setOriginalImageUrl($imageBlock->setImageUrl()); + $imageBlock->setImageUrl($generatedImageUrl); + } + } catch (\Exception $e) { + $imageBlock = $proceed($product, $imageId, $attributes); } return $imageBlock; From ac16bb92a2883e8888a2459c93663deb8f2a3cad Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 13:57:24 +0200 Subject: [PATCH 032/296] CLOUDINARY-41: changes build callback on ImageFactory Plugin --- Plugin/Catalog/Block/Product/ImageFactory.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Plugin/Catalog/Block/Product/ImageFactory.php b/Plugin/Catalog/Block/Product/ImageFactory.php index 5bd67a5a..d98c4c91 100644 --- a/Plugin/Catalog/Block/Product/ImageFactory.php +++ b/Plugin/Catalog/Block/Product/ImageFactory.php @@ -99,7 +99,12 @@ public function aroundCreate(CatalogImageFactory $catalogImageFactory, callable if (strpos($imageBlock->getImageUrl(), $this->configuration->getMediaBaseUrl()) === 0) { $imagePath = preg_replace('/^' . preg_quote($this->configuration->getMediaBaseUrl(), '/') . '/', '', $imageBlock->getImageUrl()); $imagePath = preg_replace('/\/cache\/[a-f0-9]{32}\//', '/', $imagePath); - $image = $this->cloudinaryImageFactory->build($imagePath, $proceed); + $image = $this->cloudinaryImageFactory->build( + $imagePath, + function () use ($imageBlock) { + return $imageBlock->getImageUrl(); + } + ); $generatedImageUrl = $this->urlGenerator->generateFor( $image, $this->transformationModel->addFreeformTransformationForImage( From 4525892bba1721ef25443c48216b31bc11f3d170 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 15:31:17 +0200 Subject: [PATCH 033/296] CLOUDINARY-9: reset autoUpload state on save... + another fix for CLOUDINARY-7 --- Core/AutoUploadMapping/RequestProcessor.php | 5 +++-- Model/Observer/Configuration.php | 15 +++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/Core/AutoUploadMapping/RequestProcessor.php b/Core/AutoUploadMapping/RequestProcessor.php index b5508150..207860cb 100644 --- a/Core/AutoUploadMapping/RequestProcessor.php +++ b/Core/AutoUploadMapping/RequestProcessor.php @@ -29,11 +29,12 @@ public function __construct( /** * @param string $folder * @param string $url + * @param bool $force * @return bool */ - public function handle($folder, $url) + public function handle($folder, $url, $force = false) { - if ($this->configuration->isActive() == $this->configuration->getRequestState()) { + if (($this->configuration->isActive() == $this->configuration->getRequestState()) && !$force) { return true; } diff --git a/Model/Observer/Configuration.php b/Model/Observer/Configuration.php index 815d70d0..a9a0a6bb 100644 --- a/Model/Observer/Configuration.php +++ b/Model/Observer/Configuration.php @@ -2,9 +2,7 @@ namespace Cloudinary\Cloudinary\Model\Observer; -use Cloudinary\Cloudinary\Core\AutoUploadMapping\AutoUploadConfigurationInterface; use Cloudinary\Cloudinary\Core\AutoUploadMapping\RequestProcessor; -use Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration; use Magento\Framework\App\Cache\TypeListInterface; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; @@ -29,11 +27,6 @@ class Configuration implements ObserverInterface */ protected $configuration; - /** - * @var AutoUploadConfiguration - */ - protected $autoUploadConfiguration; - /** * @var TypeListInterface */ @@ -45,20 +38,17 @@ class Configuration implements ObserverInterface * @param RequestProcessor $requestProcessor * @param ManagerInterface $messageManager * @param \Cloudinary\Cloudinary\Model\Configuration $configuration - * @param AutoUploadConfiguration $autoUploadConfiguration * @param TypeListInterface $cacheTypeList */ public function __construct( RequestProcessor $requestProcessor, ManagerInterface $messageManager, \Cloudinary\Cloudinary\Model\Configuration $configuration, - AutoUploadConfiguration $autoUploadConfiguration, TypeListInterface $cacheTypeList ) { $this->requestProcessor = $requestProcessor; $this->messageManager = $messageManager; $this->configuration = $configuration; - $this->autoUploadConfiguration = $autoUploadConfiguration; $this->cacheTypeList = $cacheTypeList; } @@ -67,6 +57,7 @@ public function __construct( */ public function execute(Observer $observer) { + $force = false; //Clear config cache if needed $this->changedPaths = (array) $observer->getEvent()->getChangedPaths(); if (count(array_intersect($this->changedPaths, [ @@ -74,11 +65,11 @@ public function execute(Observer $observer) \Cloudinary\Cloudinary\Model\Configuration::CONFIG_PATH_ENVIRONMENT_VARIABLE, \Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration::REQUEST_PATH ])) > 0) { - $this->autoUploadConfiguration->setState(AutoUploadConfigurationInterface::INACTIVE); $this->cleanConfigCache(); + $force = true; } - if (!$this->requestProcessor->handle('media', $this->configuration->getMediaBaseUrl())) { + if (!$this->requestProcessor->handle('media', $this->configuration->getMediaBaseUrl(), $force)) { $this->messageManager->addErrorMessage(self::AUTO_UPLOAD_SETUP_FAIL_MESSAGE); } } From dfe39a91ed65b7d367392c8965638f435c7b5041 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 15:33:48 +0200 Subject: [PATCH 034/296] CLOUDINARY-9: reset autoUpload state on save... + another fix for CLOUDINARY-7 --- Core/AutoUploadMapping/RequestProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/AutoUploadMapping/RequestProcessor.php b/Core/AutoUploadMapping/RequestProcessor.php index 207860cb..f7393fef 100644 --- a/Core/AutoUploadMapping/RequestProcessor.php +++ b/Core/AutoUploadMapping/RequestProcessor.php @@ -34,7 +34,7 @@ public function __construct( */ public function handle($folder, $url, $force = false) { - if (($this->configuration->isActive() == $this->configuration->getRequestState()) && !$force) { + if ($this->configuration->isActive() == $this->configuration->getRequestState() && !$force) { return true; } From 4502900d43c0abefbc0cb9623effe4b1cd676e9e Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 16:21:46 +0200 Subject: [PATCH 035/296] CLOUDINARY-42: remove wrapping quotes from url before processing in Cloudinary\Cloudinary\Plugin\Widget\Model\Template\Filter::aroundMediaDirective() in order to better support wysiwyg images --- Plugin/Widget/Model/Template/Filter.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Plugin/Widget/Model/Template/Filter.php b/Plugin/Widget/Model/Template/Filter.php index 0fb6ba30..bdb5dd4e 100644 --- a/Plugin/Widget/Model/Template/Filter.php +++ b/Plugin/Widget/Model/Template/Filter.php @@ -74,16 +74,17 @@ public function aroundMediaDirective(\Magento\Widget\Model\Template\Filter $widg if (!isset($params['url'])) { return $proceed($construction); } + $url = (preg_match('/^".+"$/', $params['url'])) ? preg_replace('/(^")|("$)/', '', $params['url']) : $params['url']; $storeManager = $this->_storeManager; $image = $this->_imageFactory->build( - $params['url'], + $url, function () use ($storeManager, $params) { return sprintf( '%s%s', $storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA), - $params['url'] + $url ); } ); From 7d49e8354bbb932a05c3e93e0cf3b64f8a6395ac Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 16:25:16 +0200 Subject: [PATCH 036/296] CLOUDINARY-42: remove wrapping quotes from url before processing in Cloudinary\Cloudinary\Plugin\Widget\Model\Template\Filter::aroundMediaDirective() in order to better support wysiwyg images --- Core/Image/ImageFactory.php | 1 - Plugin/Widget/Model/Template/Filter.php | 16 ++-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/Core/Image/ImageFactory.php b/Core/Image/ImageFactory.php index aa409379..0264f99a 100644 --- a/Core/Image/ImageFactory.php +++ b/Core/Image/ImageFactory.php @@ -3,7 +3,6 @@ namespace Cloudinary\Cloudinary\Core\Image; use Cloudinary\Cloudinary\Core\ConfigurationInterface; -use Cloudinary\Cloudinary\Core\Image\SynchronizationCheck; use Cloudinary\Cloudinary\Core\Image; class ImageFactory diff --git a/Plugin/Widget/Model/Template/Filter.php b/Plugin/Widget/Model/Template/Filter.php index bdb5dd4e..98ee4ac8 100644 --- a/Plugin/Widget/Model/Template/Filter.php +++ b/Plugin/Widget/Model/Template/Filter.php @@ -13,10 +13,6 @@ */ class Filter { - /** - * @var StoreManagerInterface - */ - protected $_storeManager; /** * @var ImageFactory */ @@ -38,7 +34,6 @@ class Filter protected $_cloudinaryWidgetFilter; /** - * @param StoreManagerInterface $storeManager * @param ImageFactory $imageFactory * @param UrlGenerator $urlGenerator * @param CloudinaryWidgetFilter $cloudinaryWidgetFilter @@ -50,7 +45,6 @@ public function __construct( ConfigurationInterface $configuration, CloudinaryWidgetFilter $cloudinaryWidgetFilter ) { - $this->_storeManager = $storeManager; $this->_imageFactory = $imageFactory; $this->_urlGenerator = $urlGenerator; $this->_configuration = $configuration; @@ -76,16 +70,10 @@ public function aroundMediaDirective(\Magento\Widget\Model\Template\Filter $widg } $url = (preg_match('/^".+"$/', $params['url'])) ? preg_replace('/(^")|("$)/', '', $params['url']) : $params['url']; - $storeManager = $this->_storeManager; - $image = $this->_imageFactory->build( $url, - function () use ($storeManager, $params) { - return sprintf( - '%s%s', - $storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA), - $url - ); + function () use ($proceed, $construction) { + return $proceed($construction); } ); From 5bac960bc0f3a5ae0d65bf237544119b6aba2cfe Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 17:06:52 +0200 Subject: [PATCH 037/296] CLOUDINARY-43,CLOUDINARY-44: Fixed Product Video Thumbnail --- view/adminhtml/web/js/get-video-information.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/view/adminhtml/web/js/get-video-information.js b/view/adminhtml/web/js/get-video-information.js index 8bbeb8e4..6e5f17df 100644 --- a/view/adminhtml/web/js/get-video-information.js +++ b/view/adminhtml/web/js/get-video-information.js @@ -594,14 +594,7 @@ define([ context = (tmp.context && tmp.context.custom) ? tmp.context.custom : {}; tmp.derived = tmp.derived || []; - thumbnail = this.options.cloudinaryPlaceholder; - thumbnail_bytes = 0; - tmp.derived.forEach(function(derivedItem) { - if (derivedItem.bytes && derivedItem.bytes > thumbnail_bytes) { - thumbnail_bytes = derivedItem.bytes; - thumbnail = (videoInfo.videoSrc.indexOf('https') === 0) ? derivedItem.secure_url : derivedItem.url; - } - }); + thumbnail = videoInfo.videoSrc.replace(/\.[^/.]+$/, "").replace(/\/([^\/]+)$/, '/so_auto/$1.jpg') || this.options.cloudinaryPlaceholder; respData = { duration: 'unknown', From 9846d736f02ed536919784d72731f1b03875a5c1 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 19 Dec 2018 17:22:13 +0200 Subject: [PATCH 038/296] v1.6.8: Changed module version to v1.6.8 (CLOUDINARY-9,CLOUDINARY-40,CLOUDINARY-41,CLOUDINARY-42,CLOUDINARY-43) --- Model/Configuration.php | 2 +- composer.json | 2 +- etc/module.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Model/Configuration.php b/Model/Configuration.php index 6c58db0d..15030908 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -159,7 +159,7 @@ public function getCdnSubdomainStatus() */ public function getUserPlatform() { - return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.5', '2.0.0'); + return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.8', '2.0.0'); } /** diff --git a/composer.json b/composer.json index 84c397c2..5445507f 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "cloudinary/cloudinary-magento2", "description": "Cloudinary Magento 2 Integration.", "type": "magento2-module", - "version": "1.6.5", + "version": "1.6.8", "minimum-stability": "dev", "license": "MIT", "repositories": { diff --git a/etc/module.xml b/etc/module.xml index 2b6982b8..115063e2 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + From 1c968d0caebc2f35259a32afd318ecab49df4041 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 26 Dec 2018 10:57:22 +0200 Subject: [PATCH 039/296] Fixed M2.3 issues: Product page image not loading from Cloudinary + custom transformations not working on category & product --- Plugin/Catalog/Block/Product/ImageFactory.php | 51 ++++- .../Model/Product/Image/UrlBuilder.php | 175 ++++++++++++++++++ etc/di.xml | 4 + 3 files changed, 221 insertions(+), 9 deletions(-) create mode 100644 Plugin/Catalog/Model/Product/Image/UrlBuilder.php diff --git a/Plugin/Catalog/Block/Product/ImageFactory.php b/Plugin/Catalog/Block/Product/ImageFactory.php index d98c4c91..b16a2534 100644 --- a/Plugin/Catalog/Block/Product/ImageFactory.php +++ b/Plugin/Catalog/Block/Product/ImageFactory.php @@ -15,10 +15,23 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Block\Product\Image as ImageBlock; use Magento\Catalog\Block\Product\ImageFactory as CatalogImageFactory; +use Magento\Catalog\Helper\Image as CatalogImageHelper; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Image\ParamsBuilder; +use Magento\Framework\View\ConfigInterface; class ImageFactory { + /** + * @var ConfigInterface + */ + private $presentationConfig; + + /** + * @var ParamsBuilder + */ + private $imageParamsBuilder; + /** * @var CloudinaryImageFactory */ @@ -60,16 +73,23 @@ class ImageFactory private $transformationModel; /** + * @param ConfigInterface $presentationConfig + * @param ParamsBuilder $imageParamsBuilder * @param CloudinaryImageFactory $cloudinaryImageFactory * @param UrlGenerator $urlGenerator * @param ConfigurationInterface $configuration + * @param TransformationFactory $transformationFactory */ public function __construct( + ConfigInterface $presentationConfig, + ParamsBuilder $imageParamsBuilder, CloudinaryImageFactory $cloudinaryImageFactory, UrlGenerator $urlGenerator, ConfigurationInterface $configuration, TransformationFactory $transformationFactory ) { + $this->presentationConfig = $presentationConfig; + $this->imageParamsBuilder = $imageParamsBuilder; $this->cloudinaryImageFactory = $cloudinaryImageFactory; $this->urlGenerator = $urlGenerator; $this->configuration = $configuration; @@ -96,19 +116,28 @@ public function aroundCreate(CatalogImageFactory $catalogImageFactory, callable } try { - if (strpos($imageBlock->getImageUrl(), $this->configuration->getMediaBaseUrl()) === 0) { - $imagePath = preg_replace('/^' . preg_quote($this->configuration->getMediaBaseUrl(), '/') . '/', '', $imageBlock->getImageUrl()); - $imagePath = preg_replace('/\/cache\/[a-f0-9]{32}\//', '/', $imagePath); + if (strpos($imageBlock->getImageUrl(), $this->configuration->getMediaBaseUrl() . 'catalog/product') === 0) { + $viewImageConfig = $this->presentationConfig->getViewConfig()->getMediaAttributes( + 'Magento_Catalog', + CatalogImageHelper::MEDIA_TYPE_CONFIG_NODE, + $imageId + ); + $imageMiscParams = $this->imageParamsBuilder->build($viewImageConfig); + + $imagePath = preg_replace('/^' . preg_quote($this->configuration->getMediaBaseUrl(), '/') . '/', '/', $imageBlock->getImageUrl()); + $imagePath = preg_replace('/\/catalog\/product\/cache\/[a-f0-9]{32}\//', '/', $imagePath); + $image = $this->cloudinaryImageFactory->build( - $imagePath, + sprintf('catalog/product%s', $imagePath), function () use ($imageBlock) { return $imageBlock->getImageUrl(); } ); + $generatedImageUrl = $this->urlGenerator->generateFor( $image, $this->transformationModel->addFreeformTransformationForImage( - $this->createTransformation($imageBlock), + $this->createTransformation($imageMiscParams), $imagePath ) ); @@ -123,15 +152,19 @@ function () use ($imageBlock) { } /** - * @param ImageBlock $imageBlock + * @param array $imageMiscParams * @return Transformation */ - private function createTransformation(ImageBlock $imageBlock) + private function createTransformation(array $imageMiscParams) { - $dimensions = $this->dimensions ?: Dimensions::fromWidthAndHeight($imageBlock->getWidth(), $imageBlock->getHeight()); - + $imageMiscParams['image_height'] = (isset($imageMiscParams['image_height'])) ? $imageMiscParams['image_height'] : null; + $imageMiscParams['image_width'] = (isset($imageMiscParams['image_width'])) ? $imageMiscParams['image_width'] : null; + $dimensions = $this->dimensions ?: Dimensions::fromWidthAndHeight($imageMiscParams['image_width'], $imageMiscParams['image_height']); $transform = $this->configuration->getDefaultTransformation()->withDimensions($dimensions); + if (isset($imageMiscParams['keep_frame'])) { + $this->keepFrame = ($imageMiscParams['keep_frame'] === 'frame') ? true : false; + } if ($this->keepFrame) { $transform->withCrop(Crop::fromString('lpad')) ->withDimensions(Dimensions::squareMissingDimension($dimensions)); diff --git a/Plugin/Catalog/Model/Product/Image/UrlBuilder.php b/Plugin/Catalog/Model/Product/Image/UrlBuilder.php new file mode 100644 index 00000000..a630b614 --- /dev/null +++ b/Plugin/Catalog/Model/Product/Image/UrlBuilder.php @@ -0,0 +1,175 @@ +presentationConfig = $presentationConfig; + $this->imageParamsBuilder = $imageParamsBuilder; + $this->cloudinaryImageFactory = $cloudinaryImageFactory; + $this->urlGenerator = $urlGenerator; + $this->configuration = $configuration; + $this->transformationModel = $transformationFactory->create(); + $this->dimensions = null; + $this->imageFile = null; + $this->keepFrame = true; + } + + /** + * Build image url using base path and params + * + * @param CatalogUrlBuilder $catalogUrlBuilder + * @param callable $proceed + * @param string $baseFilePath + * @param string $imageDisplayArea + * @return string + */ + public function aroundGetUrl(CatalogUrlBuilder $catalogUrlBuilder, callable $proceed, string $baseFilePath, string $imageDisplayArea): string + { + $url = $proceed($baseFilePath, $imageDisplayArea); + if (!$this->configuration->isEnabled()) { + return $url; + } + + try { + if (strpos($url, $this->configuration->getMediaBaseUrl() . 'catalog/product') === 0) { + $imageArguments = $this->presentationConfig->getViewConfig()->getMediaAttributes( + 'Magento_Catalog', + CatalogImageHelper::MEDIA_TYPE_CONFIG_NODE, + $imageDisplayArea + ); + $imageMiscParams = $this->imageParamsBuilder->build($imageArguments); + + $imagePath = preg_replace('/^' . preg_quote($this->configuration->getMediaBaseUrl(), '/') . '/', '/', $url); + $imagePath = preg_replace('/\/catalog\/product\/cache\/[a-f0-9]{32}\//', '/', $imagePath); + + $image = $this->cloudinaryImageFactory->build( + sprintf('catalog/product%s', $imagePath), + function () use ($url) { + return $url; + } + ); + + $generatedImageUrl = $this->urlGenerator->generateFor( + $image, + $this->transformationModel->addFreeformTransformationForImage( + $this->createTransformation($imageMiscParams), + $imagePath + ) + ); + + $url = $generatedImageUrl; + } + } catch (\Exception $e) { + $url = $proceed($baseFilePath, $imageDisplayArea); + } + + return $url; + } + + /** + * @param array $imageMiscParams + * @return Transformation + */ + private function createTransformation(array $imageMiscParams) + { + $imageMiscParams['image_height'] = (isset($imageMiscParams['image_height'])) ? $imageMiscParams['image_height'] : null; + $imageMiscParams['image_width'] = (isset($imageMiscParams['image_width'])) ? $imageMiscParams['image_width'] : null; + $dimensions = $this->dimensions ?: Dimensions::fromWidthAndHeight($imageMiscParams['image_width'], $imageMiscParams['image_height']); + $transform = $this->configuration->getDefaultTransformation()->withDimensions($dimensions); + + if (isset($imageMiscParams['keep_frame'])) { + $this->keepFrame = ($imageMiscParams['keep_frame'] === 'frame') ? true : false; + } + if ($this->keepFrame) { + $transform->withCrop(Crop::fromString('lpad')) + ->withDimensions(Dimensions::squareMissingDimension($dimensions)); + } else { + $transform->withCrop(Crop::fromString('fit')); + } + + return $transform; + } +} diff --git a/etc/di.xml b/etc/di.xml index 2f8f505a..f5a2aa3d 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -47,6 +47,10 @@ + + + + From 8fc85bf33737bf59af808a9006ad5917d212bd6e Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 26 Dec 2018 10:59:00 +0200 Subject: [PATCH 040/296] System configuration label change --- etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 5dda4298..3ef00697 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -60,7 +60,7 @@ Cloudinary\Cloudinary\Model\Config\Source\Dropdown\Dpr - + Cloudinary\Cloudinary\Model\Config\Backend\Free Cloudinary\Cloudinary\Block\Adminhtml\Form\Field\Free From 13b0257fe582b4c22f0c04842eecd4a7adcd9cc6 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Wed, 26 Dec 2018 10:59:49 +0200 Subject: [PATCH 041/296] Changed module version to v1.7.0 --- Model/Configuration.php | 2 +- composer.json | 2 +- etc/module.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Model/Configuration.php b/Model/Configuration.php index 15030908..81fa3cdf 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -159,7 +159,7 @@ public function getCdnSubdomainStatus() */ public function getUserPlatform() { - return sprintf(self::USER_PLATFORM_TEMPLATE, '1.6.8', '2.0.0'); + return sprintf(self::USER_PLATFORM_TEMPLATE, '1.7.0', '2.0.0'); } /** diff --git a/composer.json b/composer.json index 5445507f..43d68cb6 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "cloudinary/cloudinary-magento2", "description": "Cloudinary Magento 2 Integration.", "type": "magento2-module", - "version": "1.6.8", + "version": "1.7.0", "minimum-stability": "dev", "license": "MIT", "repositories": { diff --git a/etc/module.xml b/etc/module.xml index 115063e2..e9c960e5 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + From 2b7bcfade441dcf53d3cccc5c04514adaadb61b4 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 09:58:12 +0200 Subject: [PATCH 042/296] Updated Setup/UpgradeData.php --- Setup/UpgradeData.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Setup/UpgradeData.php b/Setup/UpgradeData.php index 3b0ffcda..b368f3eb 100644 --- a/Setup/UpgradeData.php +++ b/Setup/UpgradeData.php @@ -40,7 +40,9 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $setup->startSetup(); if (version_compare($context->getVersion(), '1.6.3') < 0) { - echo "- Reseting configurations for 'website' & 'store' scopes (only supports 'default' at the moment).\n"; + if ($context->getVersion()) { + echo "- Reseting configurations for 'website' & 'store' scopes (only supports 'default' at the moment).\n"; + } $this->_resourceConnection->getConnection()->delete( $this->_resourceConnection->getTableName('core_config_data'), "path LIKE 'cloudinary/%' AND scope != 'default'" From 7a1daf00d643a417f2d3ec6c5b4ee23f0d6c34e8 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 12:19:01 +0200 Subject: [PATCH 043/296] CLOUDINARY-67 --- Model/Config/Source/Dropdown/Gravity.php | 60 +++++++++--------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/Model/Config/Source/Dropdown/Gravity.php b/Model/Config/Source/Dropdown/Gravity.php index 3bc9eb69..4e315014 100644 --- a/Model/Config/Source/Dropdown/Gravity.php +++ b/Model/Config/Source/Dropdown/Gravity.php @@ -8,63 +8,47 @@ class Gravity implements OptionSourceInterface { public function toOptionArray() { - return array( - array( + return [ + [ 'value' => '', 'label' => 'Magento\'s Default', - ), - array( - 'value' => 'face', - 'label' => 'Face', - ), - array( - 'value' => 'faces', - 'label' => 'Faces', - ), - array( + ], + [ 'value' => 'north_west', 'label' => 'North West', - ), - array( + ], + [ 'value' => 'north', 'label' => 'North', - ), - array( + ], + [ 'value' => 'north_east', 'label' => 'North East', - ), - array( + ], + [ 'value' => 'east', 'label' => 'East', - ), - array( + ], + [ 'value' => 'center', 'label' => 'Center', - ), - array( + ], + [ 'value' => 'west', 'label' => 'West', - ), - array( + ], + [ 'value' => 'south_west', 'label' => 'South West', - ), - array( + ], + [ 'value' => 'south', 'label' => 'South', - ), - array( + ], + [ 'value' => 'south_east', 'label' => 'South East', - ), - array( - 'value' => 'face:center', - 'label' => 'Face (Center)', - ), - array( - 'value' => 'faces:center', - 'label' => 'Faces (Center)', - ), - ); + ], + ]; } } From bf198ed15c7033e368838fada0f000dfd261d0ce Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 12:23:03 +0200 Subject: [PATCH 044/296] CLOUDINARY-41,CLOUDINARY-55,CLOUDINARY-65: Added a workaround to the M2.3 fixes in order to also support Magento versions prior to 2.3 + Changed config validation for environment variable in order to use Cloudinary\Api::ping() + changed version to 1.7.2 --- Core/CloudinaryImageProvider.php | 24 +++++------ Core/CredentialValidator.php | 1 - Model/Config/Backend/Credentials.php | 42 +++++++++++++------ Model/Configuration.php | 2 +- Model/Observer/Configuration.php | 4 +- Plugin/Catalog/Block/Product/ImageFactory.php | 23 +++++++--- .../Model/Product/Image/UrlBuilder.php | 23 +++++++--- composer.json | 4 +- etc/module.xml | 2 +- 9 files changed, 82 insertions(+), 43 deletions(-) diff --git a/Core/CloudinaryImageProvider.php b/Core/CloudinaryImageProvider.php index 22a57dfc..3cb415b9 100644 --- a/Core/CloudinaryImageProvider.php +++ b/Core/CloudinaryImageProvider.php @@ -24,27 +24,19 @@ class CloudinaryImageProvider implements ImageProvider */ private $configurationBuilder; - /** - * @var CredentialValidator - */ - private $credentialValidator; - /** * @param ConfigurationInterface $configuration * @param ConfigurationBuilder $configurationBuilder * @param UploadResponseValidator $uploadResponseValidator - * @param CredentialValidator $credentialValidator */ public function __construct( ConfigurationInterface $configuration, ConfigurationBuilder $configurationBuilder, - UploadResponseValidator $uploadResponseValidator, - CredentialValidator $credentialValidator + UploadResponseValidator $uploadResponseValidator ) { $this->configuration = $configuration; $this->uploadResponseValidator = $uploadResponseValidator; $this->configurationBuilder = $configurationBuilder; - $this->credentialValidator = $credentialValidator; if ($configuration->isEnabled()) { $this->authorise(); } @@ -59,8 +51,7 @@ public static function fromConfiguration(ConfigurationInterface $configuration) return new CloudinaryImageProvider( $configuration, new ConfigurationBuilder($configuration), - new UploadResponseValidator(), - new CredentialValidator() + new UploadResponseValidator() ); } @@ -139,7 +130,16 @@ public function delete(Image $image) */ public function validateCredentials() { - return $this->credentialValidator->validate($this->configuration->getCredentials()); + try { + $pingValidation = $this->api->ping(); + if (!(isset($pingValidation["status"]) && $pingValidation["status"] === "ok")) { + return false; + //throw new ValidatorException(__(self::CREDENTIALS_CHECK_UNSURE)); + } + } catch (\Exception $e) { + return false; + } + return true; } private function authorise() diff --git a/Core/CredentialValidator.php b/Core/CredentialValidator.php index f6b0f152..99290b98 100644 --- a/Core/CredentialValidator.php +++ b/Core/CredentialValidator.php @@ -13,6 +13,5 @@ public function validate(Credentials $credentials) $request = new ValidateRemoteUrlRequest($signedValidationUrl); return $request->validate(); - } } diff --git a/Model/Config/Backend/Credentials.php b/Model/Config/Backend/Credentials.php index 15e8ed62..f483f682 100644 --- a/Model/Config/Backend/Credentials.php +++ b/Model/Config/Backend/Credentials.php @@ -2,17 +2,19 @@ namespace Cloudinary\Cloudinary\Model\Config\Backend; +use Cloudinary; +use Cloudinary\Api; +use Cloudinary\Cloudinary\Core\ConfigurationBuilder; +use Cloudinary\Cloudinary\Core\ConfigurationInterface; +use Cloudinary\Cloudinary\Core\Credentials as CredentialsValue; +use Cloudinary\Cloudinary\Core\Exception\InvalidCredentials; +use Cloudinary\Cloudinary\Core\Security\CloudinaryEnvironmentVariable; use Magento\Config\Model\Config\Backend\Encrypted; use Magento\Framework\App\Cache\TypeListInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Data\Collection\AbstractDb; use Magento\Framework\Encryption\EncryptorInterface; use Magento\Framework\Exception\ValidatorException; -use Cloudinary\Cloudinary\Core\CredentialValidator; -use Cloudinary\Cloudinary\Core\ConfigurationInterface; -use Cloudinary\Cloudinary\Core\Security\CloudinaryEnvironmentVariable; -use Cloudinary\Cloudinary\Core\Credentials as CredentialsValue; -use Cloudinary\Cloudinary\Core\Exception\InvalidCredentials; use Magento\Framework\Model\Context; use Magento\Framework\Model\ResourceModel\AbstractResource; use Magento\Framework\Registry; @@ -25,14 +27,19 @@ class Credentials extends Encrypted const CLOUDINARY_ENABLED_PATH = 'groups/cloud/fields/cloudinary_enabled/value'; /** - * @var CredentialValidator + * @var ConfigurationInterface + */ + private $configuration; + + /** + * @var ConfigurationBuilder */ - private $credentialValidator; + private $configurationBuilder; /** - * @var ConfigurationInterface + * @var Cloudinary\Api */ - private $configuration; + private $api; /** * @param Context $context @@ -40,7 +47,6 @@ class Credentials extends Encrypted * @param ScopeConfigInterface $config * @param TypeListInterface $cacheTypeList * @param EncryptorInterface $encryptor - * @param CredentialValidator $credentialValidator * @param ConfigurationInterface $configuration * @param AbstractResource $resource * @param AbstractDb $resourceCollection @@ -52,14 +58,16 @@ public function __construct( ScopeConfigInterface $config, TypeListInterface $cacheTypeList, EncryptorInterface $encryptor, - CredentialValidator $credentialValidator, ConfigurationInterface $configuration, AbstractResource $resource = null, AbstractDb $resourceCollection = null, + ConfigurationBuilder $configurationBuilder, + Api $api, array $data = [] ) { - $this->credentialValidator = $credentialValidator; $this->configuration = $configuration; + $this->configurationBuilder = $configurationBuilder; + $this->api = $api; parent::__construct( $context, @@ -96,7 +104,9 @@ public function beforeSave() */ private function validate(CredentialsValue $credentials) { - if (!$this->credentialValidator->validate($credentials)) { + $this->_authorise(); + $pingValidation = $this->api->ping(); + if (!(isset($pingValidation["status"]) && $pingValidation["status"] === "ok")) { throw new ValidatorException(__(self::CREDENTIALS_CHECK_UNSURE)); } } @@ -135,4 +145,10 @@ private function isModuleActiveInFormData() { return $this->getDataByPath(self::CLOUDINARY_ENABLED_PATH) === '1'; } + + private function _authorise() + { + Cloudinary::config($this->configurationBuilder->build()); + Cloudinary::$USER_PLATFORM = $this->configuration->getUserPlatform(); + } } diff --git a/Model/Configuration.php b/Model/Configuration.php index 81fa3cdf..38238c35 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -159,7 +159,7 @@ public function getCdnSubdomainStatus() */ public function getUserPlatform() { - return sprintf(self::USER_PLATFORM_TEMPLATE, '1.7.0', '2.0.0'); + return sprintf(self::USER_PLATFORM_TEMPLATE, '1.7.2', '2.0.0'); } /** diff --git a/Model/Observer/Configuration.php b/Model/Observer/Configuration.php index a9a0a6bb..16bbfe5b 100644 --- a/Model/Observer/Configuration.php +++ b/Model/Observer/Configuration.php @@ -57,7 +57,6 @@ public function __construct( */ public function execute(Observer $observer) { - $force = false; //Clear config cache if needed $this->changedPaths = (array) $observer->getEvent()->getChangedPaths(); if (count(array_intersect($this->changedPaths, [ @@ -66,10 +65,9 @@ public function execute(Observer $observer) \Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration::REQUEST_PATH ])) > 0) { $this->cleanConfigCache(); - $force = true; } - if (!$this->requestProcessor->handle('media', $this->configuration->getMediaBaseUrl(), $force)) { + if (!$this->requestProcessor->handle('media', $this->configuration->getMediaBaseUrl(), true)) { $this->messageManager->addErrorMessage(self::AUTO_UPLOAD_SETUP_FAIL_MESSAGE); } } diff --git a/Plugin/Catalog/Block/Product/ImageFactory.php b/Plugin/Catalog/Block/Product/ImageFactory.php index b16a2534..56a1aa82 100644 --- a/Plugin/Catalog/Block/Product/ImageFactory.php +++ b/Plugin/Catalog/Block/Product/ImageFactory.php @@ -17,18 +17,23 @@ use Magento\Catalog\Block\Product\ImageFactory as CatalogImageFactory; use Magento\Catalog\Helper\Image as CatalogImageHelper; use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\Product\Image\ParamsBuilder; +use Magento\Framework\ObjectManagerInterface; use Magento\Framework\View\ConfigInterface; class ImageFactory { + /** + * @var ObjectManagerInterface + */ + protected $objectManager; + /** * @var ConfigInterface */ private $presentationConfig; /** - * @var ParamsBuilder + * @var \Magento\Catalog\Model\Product\Image\ParamsBuilder */ private $imageParamsBuilder; @@ -73,23 +78,23 @@ class ImageFactory private $transformationModel; /** + * @param ObjectManagerInterface $objectManager * @param ConfigInterface $presentationConfig - * @param ParamsBuilder $imageParamsBuilder * @param CloudinaryImageFactory $cloudinaryImageFactory * @param UrlGenerator $urlGenerator * @param ConfigurationInterface $configuration * @param TransformationFactory $transformationFactory */ public function __construct( + ObjectManagerInterface $objectManager, ConfigInterface $presentationConfig, - ParamsBuilder $imageParamsBuilder, CloudinaryImageFactory $cloudinaryImageFactory, UrlGenerator $urlGenerator, ConfigurationInterface $configuration, TransformationFactory $transformationFactory ) { + $this->objectManager = $objectManager; $this->presentationConfig = $presentationConfig; - $this->imageParamsBuilder = $imageParamsBuilder; $this->cloudinaryImageFactory = $cloudinaryImageFactory; $this->urlGenerator = $urlGenerator; $this->configuration = $configuration; @@ -111,10 +116,18 @@ public function __construct( public function aroundCreate(CatalogImageFactory $catalogImageFactory, callable $proceed, Product $product, string $imageId, array $attributes = null): ImageBlock { $imageBlock = $proceed($product, $imageId, $attributes); + if (!$this->configuration->isEnabled()) { return $imageBlock; } + if (class_exists('\Magento\Catalog\Model\Product\Image\ParamsBuilder')) { + $this->imageParamsBuilder = $this->objectManager->get('\Magento\Catalog\Model\Product\Image\ParamsBuilder'); + } else { + //Skip on Magento versions prior to 2.3 + return $imageBlock; + } + try { if (strpos($imageBlock->getImageUrl(), $this->configuration->getMediaBaseUrl() . 'catalog/product') === 0) { $viewImageConfig = $this->presentationConfig->getViewConfig()->getMediaAttributes( diff --git a/Plugin/Catalog/Model/Product/Image/UrlBuilder.php b/Plugin/Catalog/Model/Product/Image/UrlBuilder.php index a630b614..25e1515c 100644 --- a/Plugin/Catalog/Model/Product/Image/UrlBuilder.php +++ b/Plugin/Catalog/Model/Product/Image/UrlBuilder.php @@ -14,19 +14,24 @@ use Cloudinary\Cloudinary\Model\TransformationFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Helper\Image as CatalogImageHelper; -use Magento\Catalog\Model\Product\Image\ParamsBuilder; use Magento\Catalog\Model\Product\Image\UrlBuilder as CatalogUrlBuilder; +use Magento\Framework\ObjectManagerInterface; use Magento\Framework\View\ConfigInterface; class UrlBuilder { + /** + * @var ObjectManagerInterface + */ + protected $objectManager; + /** * @var ConfigInterface */ private $presentationConfig; /** - * @var ParamsBuilder + * @var \Magento\Catalog\Model\Product\Image\ParamsBuilder */ private $imageParamsBuilder; @@ -71,23 +76,23 @@ class UrlBuilder private $transformationModel; /** + * @param ObjectManagerInterface $objectManager * @param ConfigInterface $presentationConfig - * @param ParamsBuilder $imageParamsBuilder * @param CloudinaryImageFactory $cloudinaryImageFactory * @param UrlGenerator $urlGenerator * @param ConfigurationInterface $configuration * @param TransformationFactory $transformationFactory */ public function __construct( + ObjectManagerInterface $objectManager, ConfigInterface $presentationConfig, - ParamsBuilder $imageParamsBuilder, CloudinaryImageFactory $cloudinaryImageFactory, UrlGenerator $urlGenerator, ConfigurationInterface $configuration, TransformationFactory $transformationFactory ) { + $this->objectManager = $objectManager; $this->presentationConfig = $presentationConfig; - $this->imageParamsBuilder = $imageParamsBuilder; $this->cloudinaryImageFactory = $cloudinaryImageFactory; $this->urlGenerator = $urlGenerator; $this->configuration = $configuration; @@ -109,10 +114,18 @@ public function __construct( public function aroundGetUrl(CatalogUrlBuilder $catalogUrlBuilder, callable $proceed, string $baseFilePath, string $imageDisplayArea): string { $url = $proceed($baseFilePath, $imageDisplayArea); + if (!$this->configuration->isEnabled()) { return $url; } + if (class_exists('\Magento\Catalog\Model\Product\Image\ParamsBuilder')) { + $this->imageParamsBuilder = $this->objectManager->get('\Magento\Catalog\Model\Product\Image\ParamsBuilder'); + } else { + //Skip on Magento versions prior to 2.3 + return $url; + } + try { if (strpos($url, $this->configuration->getMediaBaseUrl() . 'catalog/product') === 0) { $imageArguments = $this->presentationConfig->getViewConfig()->getMediaAttributes( diff --git a/composer.json b/composer.json index 43d68cb6..1149be4b 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "cloudinary/cloudinary-magento2", "description": "Cloudinary Magento 2 Integration.", "type": "magento2-module", - "version": "1.7.0", + "version": "1.7.2", "minimum-stability": "dev", "license": "MIT", "repositories": { @@ -13,7 +13,7 @@ }, "require": { "php": ">=5.5", - "cloudinary/cloudinary_php": "1.8.0" + "cloudinary/cloudinary_php": "*" }, "require-dev": { "phpspec/phpspec": "^3.0", diff --git a/etc/module.xml b/etc/module.xml index e9c960e5..804bdeb4 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + From 17ecbd5256889fb71d7c68ab5a2a0a97a40b94ac Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 12:38:44 +0200 Subject: [PATCH 045/296] CLOUDINARY-66: changed environment variable comment on configuration --- etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 3ef00697..8c68ab4b 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -20,7 +20,7 @@ - Set the credentials of your Cloudinary account. Copy the "Environment variable" string from the dashboard of Cloudinary's Management Console. + Format: CLOUDINARY_URL=cloudinary://API_Key:API_Secret@Cloud_Name]]> Cloudinary\Cloudinary\Model\Config\Backend\Credentials From 3a8682101388bec54ddca83fc57ae7b303dbce36 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 12:51:33 +0200 Subject: [PATCH 046/296] CLOUDINARY-66: changed environment variable comment on configuration --- etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 8c68ab4b..3feee5ff 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -20,7 +20,7 @@ - Format: CLOUDINARY_URL=cloudinary://API_Key:API_Secret@Cloud_Name]]> + Format should be: CLOUDINARY_URL=cloudinary://API_Key:API_Secret@Cloud_Name]]> Cloudinary\Cloudinary\Model\Config\Backend\Credentials From c619f12ae6d9f685901986acb9976ab29f46e0ba Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 13:26:49 +0200 Subject: [PATCH 047/296] CLOUDINARY-66: changed environment variable comment on configuration --- etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 3feee5ff..dd3091ec 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -20,7 +20,7 @@ - Format should be: CLOUDINARY_URL=cloudinary://API_Key:API_Secret@Cloud_Name]]> + Format should be: cloudinary://API_Key:API_Secret@Cloud_Name]]> Cloudinary\Cloudinary\Model\Config\Backend\Credentials From 19feebd6957072ad45b9986cb0156a2017e6364b Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 15:31:33 +0200 Subject: [PATCH 048/296] CLOUDINARY-65: small validationfix --- Model/Config/Backend/Credentials.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Model/Config/Backend/Credentials.php b/Model/Config/Backend/Credentials.php index f483f682..0ffe6429 100644 --- a/Model/Config/Backend/Credentials.php +++ b/Model/Config/Backend/Credentials.php @@ -50,6 +50,8 @@ class Credentials extends Encrypted * @param ConfigurationInterface $configuration * @param AbstractResource $resource * @param AbstractDb $resourceCollection + * @param ConfigurationBuilder $configurationBuilder + * @param Api $api * @param array $data */ public function __construct( @@ -87,14 +89,12 @@ public function beforeSave() parent::beforeSave(); - if (!$rawValue) { - throw new ValidatorException(__(self::CREDENTIALS_CHECK_MISSING)); - } - - if ($this->isSaveAllowed()) { - $this->validate($this->getCredentialsFromEnvironmentVariable($rawValue)); - } else { - $this->validate($this->getCredentialsFromConfig()); + if ($rawValue) { + if ($this->isSaveAllowed()) { + $this->validate($this->getCredentialsFromEnvironmentVariable($rawValue)); + } else { + $this->validate($this->getCredentialsFromConfig()); + } } } From c9377362e523522d9b0bea67ca0c774cfa3a5d84 Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 16:24:01 +0200 Subject: [PATCH 049/296] Small improvements in configuration validations --- Model/Configuration.php | 2 +- Model/Observer/Configuration.php | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Model/Configuration.php b/Model/Configuration.php index 38238c35..ab795b54 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -102,7 +102,7 @@ public function __construct( } /** - * @return Cloud + * @return StoreManagerInterface */ public function getStoreManager() { diff --git a/Model/Observer/Configuration.php b/Model/Observer/Configuration.php index 16bbfe5b..b3390f66 100644 --- a/Model/Observer/Configuration.php +++ b/Model/Observer/Configuration.php @@ -4,6 +4,8 @@ use Cloudinary\Cloudinary\Core\AutoUploadMapping\RequestProcessor; use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Message\ManagerInterface; @@ -27,6 +29,13 @@ class Configuration implements ObserverInterface */ protected $configuration; + /** + * Application config + * + * @var ScopeConfigInterface + */ + protected $appConfig; + /** * @var TypeListInterface */ @@ -39,17 +48,20 @@ class Configuration implements ObserverInterface * @param ManagerInterface $messageManager * @param \Cloudinary\Cloudinary\Model\Configuration $configuration * @param TypeListInterface $cacheTypeList + * @param ReinitableConfigInterface $config */ public function __construct( RequestProcessor $requestProcessor, ManagerInterface $messageManager, \Cloudinary\Cloudinary\Model\Configuration $configuration, - TypeListInterface $cacheTypeList + TypeListInterface $cacheTypeList, + ReinitableConfigInterface $config ) { $this->requestProcessor = $requestProcessor; $this->messageManager = $messageManager; $this->configuration = $configuration; $this->cacheTypeList = $cacheTypeList; + $this->appConfig = $config; } /** @@ -65,6 +77,11 @@ public function execute(Observer $observer) \Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration::REQUEST_PATH ])) > 0) { $this->cleanConfigCache(); + $this->appConfig->reinit(); + } + + if (!$this->configuration->isEnabled()) { + return $this; } if (!$this->requestProcessor->handle('media', $this->configuration->getMediaBaseUrl(), true)) { From 691cf0aad36ffcc4f33a0f16bc9f1a5a6153753c Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 17:11:08 +0200 Subject: [PATCH 050/296] Fixes & improvements in environment variable validation --- .../CloudinaryEnvironmentVariable.php | 4 +-- Model/Config/Backend/Credentials.php | 35 ++++++++++++------- Model/Configuration.php | 2 +- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/Core/Security/CloudinaryEnvironmentVariable.php b/Core/Security/CloudinaryEnvironmentVariable.php index 3a8a3fe3..405177d3 100644 --- a/Core/Security/CloudinaryEnvironmentVariable.php +++ b/Core/Security/CloudinaryEnvironmentVariable.php @@ -8,7 +8,6 @@ class CloudinaryEnvironmentVariable implements EnvironmentVariable { - private $environmentVariable; private function __construct($environmentVariable) @@ -16,7 +15,7 @@ private function __construct($environmentVariable) $this->environmentVariable = (string)$environmentVariable; try { Cloudinary::config_from_url(str_replace('CLOUDINARY_URL=', '', $environmentVariable)); - } catch (\Exception $e){ + } catch (\Exception $e) { throw new \Cloudinary\Cloudinary\Core\Exception\InvalidCredentials('Cloudinary config creation from environment variable failed'); } } @@ -43,5 +42,4 @@ public function __toString() { return $this->environmentVariable; } - } diff --git a/Model/Config/Backend/Credentials.php b/Model/Config/Backend/Credentials.php index 0ffe6429..0e1bf65b 100644 --- a/Model/Config/Backend/Credentials.php +++ b/Model/Config/Backend/Credentials.php @@ -6,9 +6,7 @@ use Cloudinary\Api; use Cloudinary\Cloudinary\Core\ConfigurationBuilder; use Cloudinary\Cloudinary\Core\ConfigurationInterface; -use Cloudinary\Cloudinary\Core\Credentials as CredentialsValue; use Cloudinary\Cloudinary\Core\Exception\InvalidCredentials; -use Cloudinary\Cloudinary\Core\Security\CloudinaryEnvironmentVariable; use Magento\Config\Model\Config\Backend\Encrypted; use Magento\Framework\App\Cache\TypeListInterface; use Magento\Framework\App\Config\ScopeConfigInterface; @@ -99,12 +97,12 @@ public function beforeSave() } /** - * @param CredentialsValue $credentials + * @param array $credentials * @throws ValidatorException */ - private function validate(CredentialsValue $credentials) + private function validate(array $credentials) { - $this->_authorise(); + $this->_authorise($credentials); $pingValidation = $this->api->ping(); if (!(isset($pingValidation["status"]) && $pingValidation["status"] === "ok")) { throw new ValidatorException(__(self::CREDENTIALS_CHECK_UNSURE)); @@ -114,25 +112,35 @@ private function validate(CredentialsValue $credentials) /** * @param string $environmentVariable * @throws ValidatorException - * @return CredentialsValue + * @return array */ private function getCredentialsFromEnvironmentVariable($environmentVariable) { try { - return CloudinaryEnvironmentVariable::fromString($environmentVariable)->getCredentials(); - } catch (InvalidCredentials $e) { + Cloudinary::config_from_url(str_replace('CLOUDINARY_URL=', '', $environmentVariable)); + $credentials = [ + "cloud_name" => Cloudinary::config_get('cloud_name'), + "api_key" => Cloudinary::config_get('api_key'), + "api_secret" => Cloudinary::config_get('api_secret'), + "private_cdn" => Cloudinary::config_get('private_cdn') + ]; + if (Cloudinary::config_get('secure_distribution')) { + $credentials["secure_distribution"] = Cloudinary::config_get('secure_distribution'); + } + return $credentials; + } catch (\Exception $e) { throw new ValidatorException(__(self::CREDENTIALS_CHECK_FAILED)); } } /** * @throws ValidatorException - * @return CredentialsValue + * @return array */ private function getCredentialsFromConfig() { try { - return $this->configuration->getCredentials(); + return $this->getCredentialsFromEnvironmentVariable($this->configuration->getEnvironmentVariable()->__toString()); } catch (InvalidCredentials $e) { throw new ValidatorException(__(self::CREDENTIALS_CHECK_FAILED)); } @@ -146,9 +154,12 @@ private function isModuleActiveInFormData() return $this->getDataByPath(self::CLOUDINARY_ENABLED_PATH) === '1'; } - private function _authorise() + /** + * @param array $credentials + */ + private function _authorise(array $credentials) { - Cloudinary::config($this->configurationBuilder->build()); + Cloudinary::config($credentials); Cloudinary::$USER_PLATFORM = $this->configuration->getUserPlatform(); } } diff --git a/Model/Configuration.php b/Model/Configuration.php index ab795b54..5f587dd8 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -248,7 +248,7 @@ public function hasEnvironmentVariable() /** * @return CloudinaryEnvironmentVariable */ - private function getEnvironmentVariable() + public function getEnvironmentVariable() { if (is_null($this->environmentVariable)) { try { From 33a12f1ac60b2f4f103d973616bcde0a25bfdcac Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 17:15:53 +0200 Subject: [PATCH 051/296] Fixes & improvements in environment variable validation --- Model/Config/Backend/Credentials.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Model/Config/Backend/Credentials.php b/Model/Config/Backend/Credentials.php index 0e1bf65b..c2124620 100644 --- a/Model/Config/Backend/Credentials.php +++ b/Model/Config/Backend/Credentials.php @@ -121,11 +121,10 @@ private function getCredentialsFromEnvironmentVariable($environmentVariable) $credentials = [ "cloud_name" => Cloudinary::config_get('cloud_name'), "api_key" => Cloudinary::config_get('api_key'), - "api_secret" => Cloudinary::config_get('api_secret'), - "private_cdn" => Cloudinary::config_get('private_cdn') + "api_secret" => Cloudinary::config_get('api_secret') ]; - if (Cloudinary::config_get('secure_distribution')) { - $credentials["secure_distribution"] = Cloudinary::config_get('secure_distribution'); + if (Cloudinary::config_get('private_cdn')) { + $credentials["private_cdn"] = Cloudinary::config_get('private_cdn'); } return $credentials; } catch (\Exception $e) { From 680bb33411dabb497c766639d44ef6f719acb64b Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 17:16:22 +0200 Subject: [PATCH 052/296] Fixes & improvements in environment variable validation --- Model/Configuration.php | 2 +- composer.json | 2 +- etc/module.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Model/Configuration.php b/Model/Configuration.php index 5f587dd8..aef7aa70 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -159,7 +159,7 @@ public function getCdnSubdomainStatus() */ public function getUserPlatform() { - return sprintf(self::USER_PLATFORM_TEMPLATE, '1.7.2', '2.0.0'); + return sprintf(self::USER_PLATFORM_TEMPLATE, '1.7.3', '2.0.0'); } /** diff --git a/composer.json b/composer.json index 1149be4b..30225bb1 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "cloudinary/cloudinary-magento2", "description": "Cloudinary Magento 2 Integration.", "type": "magento2-module", - "version": "1.7.2", + "version": "1.7.3", "minimum-stability": "dev", "license": "MIT", "repositories": { diff --git a/etc/module.xml b/etc/module.xml index 804bdeb4..9ccb5dc0 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + From 79af181294178e49c856efd47c0dba972ef4e23a Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 30 Dec 2018 17:57:21 +0200 Subject: [PATCH 053/296] Fixes & improvements in environment variable validationFixes --- Model/Config/Backend/Credentials.php | 19 ++++++++++++++++++- Model/Configuration.php | 4 ++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Model/Config/Backend/Credentials.php b/Model/Config/Backend/Credentials.php index c2124620..75fcc66a 100644 --- a/Model/Config/Backend/Credentials.php +++ b/Model/Config/Backend/Credentials.php @@ -9,6 +9,7 @@ use Cloudinary\Cloudinary\Core\Exception\InvalidCredentials; use Magento\Config\Model\Config\Backend\Encrypted; use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Data\Collection\AbstractDb; use Magento\Framework\Encryption\EncryptorInterface; @@ -39,6 +40,13 @@ class Credentials extends Encrypted */ private $api; + /** + * Application config + * + * @var ScopeConfigInterface + */ + protected $appConfig; + /** * @param Context $context * @param Registry $registry @@ -50,6 +58,7 @@ class Credentials extends Encrypted * @param AbstractDb $resourceCollection * @param ConfigurationBuilder $configurationBuilder * @param Api $api + * @param ReinitableConfigInterface $appConfig * @param array $data */ public function __construct( @@ -63,11 +72,13 @@ public function __construct( AbstractDb $resourceCollection = null, ConfigurationBuilder $configurationBuilder, Api $api, + ReinitableConfigInterface $appConfig, array $data = [] ) { $this->configuration = $configuration; $this->configurationBuilder = $configurationBuilder; $this->api = $api; + $this->appConfig = $appConfig; parent::__construct( $context, @@ -87,7 +98,13 @@ public function beforeSave() parent::beforeSave(); - if ($rawValue) { + $this->cacheTypeList->cleanType(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER); + $this->appConfig->reinit(); + + if ($rawValue || $this->configuration->isEnabled(false)) { + if (!$rawValue) { + throw new ValidatorException(__(self::CREDENTIALS_CHECK_MISSING)); + } if ($this->isSaveAllowed()) { $this->validate($this->getCredentialsFromEnvironmentVariable($rawValue)); } else { diff --git a/Model/Configuration.php b/Model/Configuration.php index aef7aa70..24d5eb1c 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -173,9 +173,9 @@ public function getUploadConfig() /** * @return boolean */ - public function isEnabled() + public function isEnabled($checkEnvVar = true) { - return $this->hasEnvironmentVariable() && $this->configReader->isSetFlag(self::CONFIG_PATH_ENABLED); + return ($this->hasEnvironmentVariable() || !$checkEnvVar) && $this->configReader->isSetFlag(self::CONFIG_PATH_ENABLED); } public function enable() From 21d94b4af3cdec75dd8b8369a81662cddee4a5fc Mon Sep 17 00:00:00 2001 From: pini-girit Date: Sun, 13 Jan 2019 16:41:27 +0200 Subject: [PATCH 054/296] v1.7.4: Fixed PHP_CodeSniffer errors & installation error on php5.6 + updated composer.json --- Api/ResourcesManagementInterface.php | 2 + Api/SynchronisationRepositoryInterface.php | 4 +- Block/Adminhtml/Form/Field/Free.php | 8 +- Block/Adminhtml/Product/Edit/NewVideo.php | 12 +- Command/ResetAll.php | 30 +- Command/StopMigration.php | 4 +- Command/UploadImages.php | 4 +- Controller/Adminhtml/Ajax/Free/Image.php | 8 +- Controller/Adminhtml/Ajax/Free/Sample.php | 8 +- Core/AutoUploadMapping/ApiClient.php | 31 +- Core/AutoUploadMapping/RequestProcessor.php | 12 +- Core/Cloud.php | 2 - Core/CloudinaryImageManager.php | 9 +- Core/CloudinaryImageProvider.php | 22 +- Core/ConfigurationInterface.php | 4 +- Core/Credentials.php | 6 +- Core/Exception/InvalidCredentials.php | 1 - Core/Exception/MigrationError.php | 5 +- Core/Image/ImageFactory.php | 5 +- Core/Image/Transformation/Dimensions.php | 2 +- Core/Image/Transformation/Freeform.php | 3 +- Core/Image/Transformation/Gravity.php | 2 - Core/Migration/BatchUploader.php | 22 +- Core/Migration/Logger.php | 6 +- Core/Security/ApiSignature.php | 3 +- Core/Security/ConsoleUrl.php | 1 - Core/Security/Key.php | 2 - Core/Security/Secret.php | 1 - Core/Security/SignedConsoleUrl.php | 3 +- Core/SynchroniseAssetsRepositoryInterface.php | 4 +- Core/UploadConfig.php | 3 +- Core/UrlGenerator.php | 5 +- Core/ValidateRemoteUrlRequest.php | 1 - Helper/Product/Free.php | 14 +- Helper/Reset.php | 4 +- Model/Api/ResourcesManagement.php | 22 +- .../AutoUploadConfiguration.php | 2 +- Model/BatchUploader.php | 16 +- Model/Config/Backend/Credentials.php | 26 +- Model/Config/Backend/Free.php | 30 +- Model/Configuration.php | 12 +- Model/ImageRepository.php | 7 +- Model/Logger/OutputLogger.php | 1 + Model/Observer/Configuration.php | 23 +- Model/Observer/DeleteProductImage.php | 4 +- Model/Observer/SaveProductTransform.php | 4 +- Model/Observer/UploadProductImage.php | 4 +- Model/ProductImageFinder.php | 13 +- Model/ProductImageFinder/ImageCreator.php | 3 +- Model/ProductImageFinder/ImageFilter.php | 3 +- Model/ProductImageFinder/NewImageFilter.php | 3 +- Model/SynchronisationChecker.php | 4 +- Model/SynchronisationRepository.php | 12 +- Model/Template/Filter.php | 2 +- Model/Transformation.php | 20 +- Plugin/Catalog/Block/Product/ImageFactory.php | 23 +- .../Model/Product/Image/UrlBuilder.php | 20 +- Plugin/FileRemover.php | 4 +- Plugin/FileUploader.php | 14 +- Plugin/ImageHelper.php | 30 +- Plugin/MediaConfig.php | 6 +- Plugin/ValidateProductTransform.php | 4 +- Plugin/Widget/Model/Template/Filter.php | 10 +- Setup/InstallSchema.php | 1 + Setup/UpgradeData.php | 26 +- Setup/UpgradeSchema.php | 2 +- .../Product/Form/Modifier/Product.php | 16 +- composer.json | 52 +- etc/module.xml | 2 +- view/adminhtml/web/js/cloudinary-free.js | 177 +- .../adminhtml/web/js/get-video-information.js | 1464 +++++----- view/adminhtml/web/js/new-video-dialog.js | 2376 +++++++++-------- .../web/js/product_free_transform.js | 266 +- .../web/js/fotorama-add-video-events.js | 1578 +++++------ view/frontend/web/js/load-player.js | 778 +++--- 75 files changed, 3811 insertions(+), 3502 deletions(-) diff --git a/Api/ResourcesManagementInterface.php b/Api/ResourcesManagementInterface.php index b80c890a..00d01578 100644 --- a/Api/ResourcesManagementInterface.php +++ b/Api/ResourcesManagementInterface.php @@ -7,12 +7,14 @@ interface ResourcesManagementInterface /** * GET for getImage api + * * @return string */ public function getImage(); /** * GET for getVideo api + * * @return string */ public function getVideo(); diff --git a/Api/SynchronisationRepositoryInterface.php b/Api/SynchronisationRepositoryInterface.php index 8446a1ba..cd64bde2 100644 --- a/Api/SynchronisationRepositoryInterface.php +++ b/Api/SynchronisationRepositoryInterface.php @@ -8,13 +8,13 @@ interface SynchronisationRepositoryInterface { /** - * @param SearchCriteriaInterface $searchCriteria + * @param SearchCriteriaInterface $searchCriteria * @return SearchResultsInterface */ public function getList(SearchCriteriaInterface $searchCriteria); /** - * @param string $imagePath + * @param string $imagePath * * @return SearchResultsInterface */ diff --git a/Block/Adminhtml/Form/Field/Free.php b/Block/Adminhtml/Form/Field/Free.php index 46428ba9..240f52b9 100644 --- a/Block/Adminhtml/Form/Field/Free.php +++ b/Block/Adminhtml/Form/Field/Free.php @@ -21,10 +21,10 @@ class Free extends Field private $model; /** - * @param Context $context + * @param Context $context * @param ConfigurationInterface $configuration - * @param FreeBackendModel $model - * @param array $data + * @param FreeBackendModel $model + * @param array $data */ public function __construct( Context $context, @@ -48,7 +48,7 @@ protected function _beforeToHtml() } /** - * @param AbstractElement $element + * @param AbstractElement $element * @return string */ protected function _getElementHtml(AbstractElement $element) diff --git a/Block/Adminhtml/Product/Edit/NewVideo.php b/Block/Adminhtml/Product/Edit/NewVideo.php index 9dafd369..8c775a97 100644 --- a/Block/Adminhtml/Product/Edit/NewVideo.php +++ b/Block/Adminhtml/Product/Edit/NewVideo.php @@ -18,13 +18,13 @@ class NewVideo extends \Magento\ProductVideo\Block\Adminhtml\Product\Edit\NewVid protected $_cloudinaryConfigurationBuilder; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Data\FormFactory $formFactory - * @param \Magento\ProductVideo\Helper\Media $mediaHelper - * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder + * @param \Magento\Backend\Block\Template\Context $context + * @param \Magento\Framework\Registry $registry + * @param \Magento\Framework\Data\FormFactory $formFactory + * @param \Magento\ProductVideo\Helper\Media $mediaHelper + * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder * @param \Cloudinary\Cloudinary\Core\ConfigurationBuilder $cloudinaryConfigurationBuilder - * @param array $data + * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, diff --git a/Command/ResetAll.php b/Command/ResetAll.php index 4ab9816e..4faa8202 100644 --- a/Command/ResetAll.php +++ b/Command/ResetAll.php @@ -60,8 +60,8 @@ protected function configure() } /** - * @param InputInterface $input - * @param OutputInterface $output + * @param InputInterface $input + * @param OutputInterface $output * * @return void */ @@ -101,7 +101,7 @@ private function displayPreActionMessage(OutputInterface $output) $output->writeln(sprintf(self::WARNING_FORMAT, self::PRE_ACTION_WARNING2)); array_map( - function($line) use ($output) { + function ($line) use ($output) { $output->writeln(sprintf('%s', $line)); }, self::PRE_ACTION_MESSAGES @@ -109,9 +109,9 @@ function($line) use ($output) { } /** - * @param InputInterface $input - * @param OutputInterface $output - * @param QuestionHelper $helper + * @param InputInterface $input + * @param OutputInterface $output + * @param QuestionHelper $helper * @return bool */ private function confirmActionStart(InputInterface $input, OutputInterface $output, QuestionHelper $helper) @@ -122,9 +122,9 @@ private function confirmActionStart(InputInterface $input, OutputInterface $outp } /** - * @param InputInterface $input - * @param OutputInterface $output - * @param QuestionHelper $helper + * @param InputInterface $input + * @param OutputInterface $output + * @param QuestionHelper $helper * @return string */ private function readAdminName(InputInterface $input, OutputInterface $output, QuestionHelper $helper) @@ -135,7 +135,7 @@ private function readAdminName(InputInterface $input, OutputInterface $output, Q } /** - * @param string $username + * @param string $username * @return User */ private function getAdminUser($username) @@ -144,9 +144,9 @@ private function getAdminUser($username) } /** - * @param InputInterface $input - * @param OutputInterface $output - * @param QuestionHelper $helper + * @param InputInterface $input + * @param OutputInterface $output + * @param QuestionHelper $helper * @return string */ private function readAdminPassword(InputInterface $input, OutputInterface $output, QuestionHelper $helper) @@ -158,8 +158,8 @@ private function readAdminPassword(InputInterface $input, OutputInterface $outpu } /** - * @param User $user - * @param string $password + * @param User $user + * @param string $password * @return bool */ private function authenticate(User $user, $password) diff --git a/Command/StopMigration.php b/Command/StopMigration.php index ff85b4b1..6a09c11e 100644 --- a/Command/StopMigration.php +++ b/Command/StopMigration.php @@ -38,8 +38,8 @@ protected function configure() } /** - * @param InputInterface $input - * @param OutputInterface $output + * @param InputInterface $input + * @param OutputInterface $output * * @return void */ diff --git a/Command/UploadImages.php b/Command/UploadImages.php index 9f7f7bb4..7a2cca80 100644 --- a/Command/UploadImages.php +++ b/Command/UploadImages.php @@ -43,8 +43,8 @@ protected function configure() } /** - * @param InputInterface $input - * @param OutputInterface $output + * @param InputInterface $input + * @param OutputInterface $output * * @return void */ diff --git a/Controller/Adminhtml/Ajax/Free/Image.php b/Controller/Adminhtml/Ajax/Free/Image.php index 4740dd13..fcc74936 100644 --- a/Controller/Adminhtml/Ajax/Free/Image.php +++ b/Controller/Adminhtml/Ajax/Free/Image.php @@ -31,9 +31,9 @@ class Image extends Action private $configuration; /** - * @param Context $context - * @param JsonFactory $resultJsonFactory - * @param FreeBackendModel $model + * @param Context $context + * @param JsonFactory $resultJsonFactory + * @param FreeBackendModel $model * @param ConfigurationInterface $configuration */ public function __construct( @@ -71,7 +71,7 @@ public function execute() } /** - * @param string $freeTransform + * @param string $freeTransform * @return Transformation */ private function defaultTransformWithFreeTransform($freeTransform) diff --git a/Controller/Adminhtml/Ajax/Free/Sample.php b/Controller/Adminhtml/Ajax/Free/Sample.php index 48b82655..39c3dd83 100644 --- a/Controller/Adminhtml/Ajax/Free/Sample.php +++ b/Controller/Adminhtml/Ajax/Free/Sample.php @@ -31,9 +31,9 @@ class Sample extends Action private $configuration; /** - * @param Context $context - * @param JsonFactory $resultJsonFactory - * @param FreeBackendModel $model + * @param Context $context + * @param JsonFactory $resultJsonFactory + * @param FreeBackendModel $model * @param ConfigurationInterface $configuration */ public function __construct( @@ -70,7 +70,7 @@ public function execute() } /** - * @param string $freeTransform + * @param string $freeTransform * @return Transformation */ private function defaultTransformWithFreeTransform($freeTransform) diff --git a/Core/AutoUploadMapping/ApiClient.php b/Core/AutoUploadMapping/ApiClient.php index 082ec6e6..f2610161 100644 --- a/Core/AutoUploadMapping/ApiClient.php +++ b/Core/AutoUploadMapping/ApiClient.php @@ -31,8 +31,9 @@ class ApiClient /** * ApiClient constructor. + * * @param ConfigurationInterface $configuration - * @param ConfigurationBuilder $configurationBuilder + * @param ConfigurationBuilder $configurationBuilder */ public function __construct( ConfigurationInterface $configuration, @@ -48,7 +49,7 @@ public function __construct( } /** - * @param ConfigurationInterface $configuration + * @param ConfigurationInterface $configuration * @return ApiClient */ public static function fromConfiguration(ConfigurationInterface $configuration) @@ -61,14 +62,13 @@ public static function fromConfiguration(ConfigurationInterface $configuration) } /** - * @param string $folder - * @param string $url + * @param string $folder + * @param string $url * @return bool */ public function prepareMapping($folder, $url) { try { - $existingMappings = $this->parseFetchMappingsResponse($this->api->upload_mappings()); if ($this->hasMapping($existingMappings, $folder)) { @@ -80,7 +80,6 @@ public function prepareMapping($folder, $url) } return true; - } catch (\Exception $e) { $this->errors[] = $e; return false; @@ -88,7 +87,7 @@ public function prepareMapping($folder, $url) } /** - * @param Response $response + * @param Response $response * @return array * @throws \Exception */ @@ -102,23 +101,23 @@ private function parseFetchMappingsResponse(Response $response) } /** - * @param array $mappings - * @param string $folder + * @param array $mappings + * @param string $folder * @return array */ private function filterMappings(array $mappings, $folder) { return array_filter( $mappings, - function(array $mapping) use ($folder) { + function (array $mapping) use ($folder) { return $mapping[self::FOLDER_KEY] == $folder; } ); } /** - * @param array $mappings - * @param string $folder + * @param array $mappings + * @param string $folder * @return bool */ private function hasMapping(array $mappings, $folder) @@ -127,9 +126,9 @@ private function hasMapping(array $mappings, $folder) } /** - * @param array $existingMappings - * @param string $folder - * @param string $url + * @param array $existingMappings + * @param string $folder + * @param string $url * @return bool */ private function mappingMatches(array $existingMappings, $folder, $url) @@ -137,7 +136,7 @@ private function mappingMatches(array $existingMappings, $folder, $url) return count( array_filter( $this->filterMappings($existingMappings, $folder), - function(array $mapping) use ($url) { + function (array $mapping) use ($url) { return $mapping[self::URL_KEY] == $url; } ) diff --git a/Core/AutoUploadMapping/RequestProcessor.php b/Core/AutoUploadMapping/RequestProcessor.php index f7393fef..e6897ea7 100644 --- a/Core/AutoUploadMapping/RequestProcessor.php +++ b/Core/AutoUploadMapping/RequestProcessor.php @@ -16,7 +16,7 @@ class RequestProcessor /** * @param AutoUploadConfigurationInterface $configuration - * @param ApiClient $apiClient + * @param ApiClient $apiClient */ public function __construct( AutoUploadConfigurationInterface $configuration, @@ -27,9 +27,9 @@ public function __construct( } /** - * @param string $folder - * @param string $url - * @param bool $force + * @param string $folder + * @param string $url + * @param bool $force * @return bool */ public function handle($folder, $url, $force = false) @@ -48,8 +48,8 @@ public function handle($folder, $url, $force = false) } /** - * @param string $folder - * @param string $url + * @param string $folder + * @param string $url * @return bool */ private function handleActiveRequest($folder, $url) diff --git a/Core/Cloud.php b/Core/Cloud.php index a5b3ee27..3338a6d4 100644 --- a/Core/Cloud.php +++ b/Core/Cloud.php @@ -4,10 +4,8 @@ class Cloud { - private $cloudName; - private function __construct($cloudName) { $this->cloudName = (string)$cloudName; diff --git a/Core/CloudinaryImageManager.php b/Core/CloudinaryImageManager.php index 2a11c7c0..30b14b38 100644 --- a/Core/CloudinaryImageManager.php +++ b/Core/CloudinaryImageManager.php @@ -7,6 +7,7 @@ /** * Class CloudinaryImageManager + * * @package Cloudinary\Cloudinary\Core */ class CloudinaryImageManager @@ -29,7 +30,7 @@ class CloudinaryImageManager /** * CloudinaryImageManager constructor. * - * @param ImageProvider $cloudinaryImageProvider + * @param ImageProvider $cloudinaryImageProvider * @param SynchroniseAssetsRepositoryInterface $synchronisationRepository */ public function __construct( @@ -41,8 +42,8 @@ public function __construct( } /** - * @param Image $image - * @param OutputInterface|null $output + * @param Image $image + * @param OutputInterface|null $output * @throws \Exception */ public function uploadAndSynchronise(Image $image, OutputInterface $output = null, $retryAttempt = 0) @@ -78,7 +79,7 @@ public function removeAndUnSynchronise(Image $image) /** * @param OutputInterface|null $output - * @param string $message + * @param string $message */ private function report(OutputInterface $output = null, $message = '') { diff --git a/Core/CloudinaryImageProvider.php b/Core/CloudinaryImageProvider.php index 3cb415b9..be1c033d 100644 --- a/Core/CloudinaryImageProvider.php +++ b/Core/CloudinaryImageProvider.php @@ -25,8 +25,8 @@ class CloudinaryImageProvider implements ImageProvider private $configurationBuilder; /** - * @param ConfigurationInterface $configuration - * @param ConfigurationBuilder $configurationBuilder + * @param ConfigurationInterface $configuration + * @param ConfigurationBuilder $configurationBuilder * @param UploadResponseValidator $uploadResponseValidator */ public function __construct( @@ -43,7 +43,7 @@ public function __construct( } /** - * @param ConfigurationInterface $configuration + * @param ConfigurationInterface $configuration * @return CloudinaryImageProvider */ public static function fromConfiguration(ConfigurationInterface $configuration) @@ -56,7 +56,7 @@ public static function fromConfiguration(ConfigurationInterface $configuration) } /** - * @param Image $image + * @param Image $image * @return mixed */ public function upload(Image $image) @@ -77,17 +77,19 @@ public function upload(Image $image) } /** - * @param Image $image - * @param Transformation $transformation + * @param Image $image + * @param Transformation $transformation * @return Image */ public function retrieveTransformed(Image $image, Transformation $transformation) { - $imagePath = \cloudinary_url($image->getId(), [ + $imagePath = \cloudinary_url( + $image->getId(), [ 'transformation' => $transformation->build(), 'secure' => true, 'sign_url' => $this->configuration->getUseSignedUrls() - ]); + ] + ); if ($this->configuration->getUseRootPath()) { if (strpos($imagePath, "cloudinary.com/{$this->configuration->getCloud()}/image/upload/") !== false) { @@ -106,7 +108,7 @@ public function retrieveTransformed(Image $image, Transformation $transformation } /** - * @param Image $image + * @param Image $image * @return Image */ public function retrieve(Image $image) @@ -115,7 +117,7 @@ public function retrieve(Image $image) } /** - * @param Image $image + * @param Image $image * @return bool */ public function delete(Image $image) diff --git a/Core/ConfigurationInterface.php b/Core/ConfigurationInterface.php index a362195e..e2c6a1db 100644 --- a/Core/ConfigurationInterface.php +++ b/Core/ConfigurationInterface.php @@ -2,8 +2,6 @@ namespace Cloudinary\Cloudinary\Core; -use Cloudinary\Cloudinary\Core\Cloud; -use Cloudinary\Cloudinary\Core\Credentials; use Cloudinary\Cloudinary\Core\Image\Transformation; interface ConfigurationInterface @@ -49,7 +47,7 @@ public function isEnabled(); public function getFormatsToPreserve(); /** - * @param string $file + * @param string $file * * @return string */ diff --git a/Core/Credentials.php b/Core/Credentials.php index d30567d6..545eb97d 100644 --- a/Core/Credentials.php +++ b/Core/Credentials.php @@ -2,23 +2,21 @@ namespace Cloudinary\Cloudinary\Core; - use Cloudinary\Cloudinary\Core\Security\Key; use Cloudinary\Cloudinary\Core\Security\Secret; class Credentials { - private $key; private $secret; - private function __construct(Key $key,Secret $secret) + private function __construct(Key $key, Secret $secret) { $this->key = $key; $this->secret = $secret; } - public static function fromKeyAndSecret(Key $key,Secret $secret) + public static function fromKeyAndSecret(Key $key, Secret $secret) { return new Credentials($key, $secret); } diff --git a/Core/Exception/InvalidCredentials.php b/Core/Exception/InvalidCredentials.php index 6f36aaba..129c95fe 100644 --- a/Core/Exception/InvalidCredentials.php +++ b/Core/Exception/InvalidCredentials.php @@ -6,5 +6,4 @@ class InvalidCredentials extends Exception { - } diff --git a/Core/Exception/MigrationError.php b/Core/Exception/MigrationError.php index 6ede19f7..f6bb259e 100644 --- a/Core/Exception/MigrationError.php +++ b/Core/Exception/MigrationError.php @@ -7,6 +7,7 @@ /** * Class MigrationError + * * @package Cloudinary\Cloudinary\Core\Exception */ class MigrationError extends Exception @@ -35,8 +36,8 @@ public function getImage() } /** - * @param Image $image - * @param string $message + * @param Image $image + * @param string $message * @throws MigrationError */ public static function throwWith(Image $image, $message = '') diff --git a/Core/Image/ImageFactory.php b/Core/Image/ImageFactory.php index 0264f99a..cd76f4ad 100644 --- a/Core/Image/ImageFactory.php +++ b/Core/Image/ImageFactory.php @@ -19,8 +19,9 @@ class ImageFactory /** * ImageFactory constructor. + * * @param ConfigurationInterface $configuration - * @param SynchronizationCheck $synchronizationChecker + * @param SynchronizationCheck $synchronizationChecker */ public function __construct(ConfigurationInterface $configuration, SynchronizationCheck $synchronizationChecker) { @@ -29,7 +30,7 @@ public function __construct(ConfigurationInterface $configuration, Synchronizati } /** - * @param $imagePath + * @param $imagePath * @return Image */ public function build($imagePath, callable $localPathGenerator) diff --git a/Core/Image/Transformation/Dimensions.php b/Core/Image/Transformation/Dimensions.php index 0bc23b5b..757c23d3 100644 --- a/Core/Image/Transformation/Dimensions.php +++ b/Core/Image/Transformation/Dimensions.php @@ -33,7 +33,7 @@ public static function squareMissingDimension(Dimensions $dimensions) { if (!$dimensions->getWidth()) { return Dimensions::square($dimensions->getHeight()); - } else if (!$dimensions->getHeight()) { + } elseif (!$dimensions->getHeight()) { return Dimensions::square($dimensions->getWidth()); } return $dimensions; diff --git a/Core/Image/Transformation/Freeform.php b/Core/Image/Transformation/Freeform.php index 5d234737..b64388cf 100644 --- a/Core/Image/Transformation/Freeform.php +++ b/Core/Image/Transformation/Freeform.php @@ -11,6 +11,7 @@ class Freeform /** * Freeform constructor. + * * @param string $urlParameters */ public function __construct($urlParameters) @@ -19,7 +20,7 @@ public function __construct($urlParameters) } /** - * @param string $value + * @param string $value * @return Freeform */ public static function fromString($value) diff --git a/Core/Image/Transformation/Gravity.php b/Core/Image/Transformation/Gravity.php index 9ee45377..46a53295 100644 --- a/Core/Image/Transformation/Gravity.php +++ b/Core/Image/Transformation/Gravity.php @@ -26,5 +26,3 @@ public static function null() return new Gravity(null); } } - - diff --git a/Core/Migration/BatchUploader.php b/Core/Migration/BatchUploader.php index 443a1e45..3515c1e1 100644 --- a/Core/Migration/BatchUploader.php +++ b/Core/Migration/BatchUploader.php @@ -57,11 +57,12 @@ class BatchUploader /** * BatchUploader constructor. + * * @param ImageProvider $imageProvider - * @param Task $migrationTask - * @param Logger $logger - * @param string $baseMediaPath - * @param callable $exceptionCallback + * @param Task $migrationTask + * @param Logger $logger + * @param string $baseMediaPath + * @param callable $exceptionCallback */ public function __construct( ImageProvider $imageProvider, @@ -84,7 +85,6 @@ public function uploadImages(array $images) { $this->countMigrated = 0; foreach ($images as $image) { - if ($this->migrationTask->hasBeenStopped()) { break; } @@ -94,7 +94,7 @@ public function uploadImages(array $images) } /** - * @param Synchronizable $image + * @param Synchronizable $image * @return string */ private function getAbsolutePath(Synchronizable $image) @@ -104,7 +104,7 @@ private function getAbsolutePath(Synchronizable $image) /** * @param Synchronizable $image - * @param int $retryAttempt + * @param int $retryAttempt */ private function uploadImage(Synchronizable $image, $retryAttempt = 0) { @@ -149,8 +149,8 @@ private function notify(\Exception $e) } /** - * @param $retryMessage - * @param \Exception $e + * @param $retryMessage + * @param \Exception $e * @return \Exception */ private function addRetryMessage($retryMessage, \Exception $e) @@ -164,8 +164,8 @@ private function addRetryMessage($retryMessage, \Exception $e) } /** - * @param \Exception $e - * @param $message + * @param \Exception $e + * @param $message * @return string */ private function buildUploadErrorMessage(\Exception $e, $message) diff --git a/Core/Migration/Logger.php b/Core/Migration/Logger.php index 5303c770..24a60084 100644 --- a/Core/Migration/Logger.php +++ b/Core/Migration/Logger.php @@ -4,11 +4,11 @@ interface Logger { - public function warning($message, array $context = array()); + public function warning($message, array $context = []); - public function notice($message, array $context = array()); + public function notice($message, array $context = []); - public function error($message, array $context = array()); + public function error($message, array $context = []); public function debugLog($message); } diff --git a/Core/Security/ApiSignature.php b/Core/Security/ApiSignature.php index 531c9422..8a85685a 100644 --- a/Core/Security/ApiSignature.php +++ b/Core/Security/ApiSignature.php @@ -6,7 +6,6 @@ class ApiSignature { - private $apiSignature; private function __construct(Secret $secret, array $params) @@ -14,7 +13,7 @@ private function __construct(Secret $secret, array $params) $this->apiSignature = Cloudinary::api_sign_request($params, (string) $secret); } - public static function fromSecretAndParams(Secret $secret, array $params = array()) + public static function fromSecretAndParams(Secret $secret, array $params = []) { return new ApiSignature($secret, $params); } diff --git a/Core/Security/ConsoleUrl.php b/Core/Security/ConsoleUrl.php index 1dcdf528..601cf471 100644 --- a/Core/Security/ConsoleUrl.php +++ b/Core/Security/ConsoleUrl.php @@ -4,7 +4,6 @@ class ConsoleUrl { - private $consoleUrl; const CLOUDINARY_CONSOLE_BASE_URL = 'https://cloudinary.com/console/'; diff --git a/Core/Security/Key.php b/Core/Security/Key.php index cd138703..1ab658bf 100644 --- a/Core/Security/Key.php +++ b/Core/Security/Key.php @@ -4,7 +4,6 @@ class Key { - private $key; private function __construct($key) @@ -21,5 +20,4 @@ public function __toString() { return $this->key; } - } diff --git a/Core/Security/Secret.php b/Core/Security/Secret.php index e07e4902..67296ab2 100644 --- a/Core/Security/Secret.php +++ b/Core/Security/Secret.php @@ -4,7 +4,6 @@ class Secret { - private $secret; private function __construct($secret) diff --git a/Core/Security/SignedConsoleUrl.php b/Core/Security/SignedConsoleUrl.php index 57483dbb..8fcf1bac 100644 --- a/Core/Security/SignedConsoleUrl.php +++ b/Core/Security/SignedConsoleUrl.php @@ -6,12 +6,11 @@ class SignedConsoleUrl { - private $signedConsoleUrl; private function __construct(ConsoleUrl $url, Credentials $credentials) { - $params = array("timestamp" => time(), "mode" => "check"); + $params = ["timestamp" => time(), "mode" => "check"]; $params["signature"] = (string)ApiSignature::fromSecretAndParams($credentials->getSecret(), $params); $params["api_key"] = (string)$credentials->getKey(); $query = http_build_query($params); diff --git a/Core/SynchroniseAssetsRepositoryInterface.php b/Core/SynchroniseAssetsRepositoryInterface.php index f5d2f278..df903f58 100644 --- a/Core/SynchroniseAssetsRepositoryInterface.php +++ b/Core/SynchroniseAssetsRepositoryInterface.php @@ -5,13 +5,13 @@ interface SynchroniseAssetsRepositoryInterface { /** - * @param string $imagePath + * @param string $imagePath * @return mixed */ public function saveAsSynchronized($imagePath); /** - * @param string g$imagePath + * @param string g $imagePath * @return mixed */ public function removeSynchronised($imagePath); diff --git a/Core/UploadConfig.php b/Core/UploadConfig.php index 92e8c8dd..db43cab4 100644 --- a/Core/UploadConfig.php +++ b/Core/UploadConfig.php @@ -8,7 +8,6 @@ namespace Cloudinary\Cloudinary\Core; - class UploadConfig { /** @@ -68,6 +67,6 @@ public function toArray() "use_filename" => $this->useFilename, "unique_filename" => $this->uniqueFilename, "overwrite" => $this->overwrite, - ] ; + ]; } } diff --git a/Core/UrlGenerator.php b/Core/UrlGenerator.php index 1884baab..f8eceb59 100644 --- a/Core/UrlGenerator.php +++ b/Core/UrlGenerator.php @@ -2,7 +2,6 @@ namespace Cloudinary\Cloudinary\Core; -use Cloudinary\Cloudinary\Core\ImageInterface; use Cloudinary\Cloudinary\Core\Image\LocalImage; use Cloudinary\Cloudinary\Core\Image\Transformation; use Cloudinary\Cloudinary\Core\Image\Transformation\Dimensions; @@ -21,7 +20,7 @@ class UrlGenerator /** * @param ConfigurationInterface $configuration - * @param ImageProvider $imageProvider + * @param ImageProvider $imageProvider */ public function __construct(ConfigurationInterface $configuration, ImageProvider $imageProvider) { @@ -48,7 +47,7 @@ public function generateFor(ImageInterface $image, Transformation $transformatio } /** - * @param Image $image + * @param Image $image * @param Dimensions $dimensions * * @return string diff --git a/Core/ValidateRemoteUrlRequest.php b/Core/ValidateRemoteUrlRequest.php index 747f8210..cbb32db5 100644 --- a/Core/ValidateRemoteUrlRequest.php +++ b/Core/ValidateRemoteUrlRequest.php @@ -4,7 +4,6 @@ class ValidateRemoteUrlRequest { - private $curlHandler; public function __construct($url) diff --git a/Helper/Product/Free.php b/Helper/Product/Free.php index 57750788..10e8c6e0 100644 --- a/Helper/Product/Free.php +++ b/Helper/Product/Free.php @@ -20,7 +20,7 @@ class Free private $configuration; /** - * @param FreeModel $freeModel + * @param FreeModel $freeModel * @param ConfigurationInterface $configuration */ public function __construct(FreeModel $freeModel, ConfigurationInterface $configuration) @@ -43,8 +43,8 @@ public function validate($imageName, $transform) } /** - * @param string $id - * @param array $images + * @param string $id + * @param array $images * @return string */ public function getImageNameForId($id, array $images) @@ -53,7 +53,7 @@ public function getImageNameForId($id, array $images) } /** - * @param Product $product + * @param Product $product * @return array */ public function getMediaGalleryImages(Product $product) @@ -68,8 +68,8 @@ public function getMediaGalleryImages(Product $product) } /** - * @param array|null $data - * @param array|null $isUpdated + * @param array|null $data + * @param array|null $isUpdated * @return array */ public function filterUpdated($data, $isUpdated) @@ -80,7 +80,7 @@ public function filterUpdated($data, $isUpdated) return array_filter( $data, - function($id) use ($isUpdated) { + function ($id) use ($isUpdated) { return $isUpdated[$id] === '1'; }, ARRAY_FILTER_USE_KEY diff --git a/Helper/Reset.php b/Helper/Reset.php index 2ec8a002..267ef1a4 100644 --- a/Helper/Reset.php +++ b/Helper/Reset.php @@ -25,8 +25,8 @@ class Reset /** * @param ResourceConnection $connection - * @param Synchronisation $synchronisation - * @param Transformation $transformation + * @param Synchronisation $synchronisation + * @param Transformation $transformation */ public function __construct( ResourceConnection $connection, diff --git a/Model/Api/ResourcesManagement.php b/Model/Api/ResourcesManagement.php index c16dad41..1e374f72 100644 --- a/Model/Api/ResourcesManagement.php +++ b/Model/Api/ResourcesManagement.php @@ -34,8 +34,9 @@ class ResourcesManagement implements \Cloudinary\Cloudinary\Api\ResourcesManagem /** * ApiClient constructor. + * * @param ConfigurationInterface $configuration - * @param ConfigurationBuilder $configurationBuilder + * @param ConfigurationBuilder $configurationBuilder */ public function __construct( ConfigurationInterface $configuration, @@ -67,24 +68,31 @@ public function _sendJsonResponse($response) /** * Get details of a single resource + * * @method _getResourceData * @return string (json encoded data) */ protected function _getResourceData() { try { - $this->_resourceData = $this->_api->resource($this->_request->getParam("id"), [ + $this->_resourceData = $this->_api->resource( + $this->_request->getParam("id"), [ "resource_type" => $this->_resourceType - ]); - $this->_sendJsonResponse([ + ] + ); + $this->_sendJsonResponse( + [ "error" => 0, "data" => $this->_resourceData - ]); + ] + ); } catch (\Exception $e) { - $this->_sendJsonResponse([ + $this->_sendJsonResponse( + [ "error" => 1, "message" => $e->getMessage() - ]); + ] + ); } } diff --git a/Model/AutoUploadMapping/AutoUploadConfiguration.php b/Model/AutoUploadMapping/AutoUploadConfiguration.php index 55d3cdbb..3d3f7ffd 100644 --- a/Model/AutoUploadMapping/AutoUploadConfiguration.php +++ b/Model/AutoUploadMapping/AutoUploadConfiguration.php @@ -69,7 +69,7 @@ public function setRequestState($state) /** * @param string $key - * @param bool $state + * @param bool $state */ private function setFlag($key, $state) { diff --git a/Model/BatchUploader.php b/Model/BatchUploader.php index dea838f1..4aa8e031 100644 --- a/Model/BatchUploader.php +++ b/Model/BatchUploader.php @@ -44,9 +44,9 @@ class BatchUploader private $autoUploadConfiguration; /** - * @param ImageRepository $imageRepository - * @param Configuration $configuration - * @param MigrationTask $migrationTask + * @param ImageRepository $imageRepository + * @param Configuration $configuration + * @param MigrationTask $migrationTask * @param CloudinaryImageManager $cloudinaryImageManager */ public function __construct( @@ -66,7 +66,7 @@ public function __construct( /** * Find unsynchronised images and upload them to cloudinary * - * @param OutputInterface|null $output + * @param OutputInterface|null $output * @return bool * @throws \Exception */ @@ -101,7 +101,7 @@ public function uploadUnsynchronisedImages(OutputInterface $output = null) /** * @param OutputInterface $output - * @param string $message + * @param string $message */ private function displayMessage(OutputInterface $output, $message) { @@ -111,7 +111,7 @@ private function displayMessage(OutputInterface $output, $message) } /** - * @param Image $image + * @param Image $image * @param OutputInterface $output */ private function uploadAndSynchronise(Image $image, OutputInterface $output) @@ -124,7 +124,7 @@ private function uploadAndSynchronise(Image $image, OutputInterface $output) } /** - * @param OutputInterface $output + * @param OutputInterface $output * @return bool */ private function validateAutoUploadMapping(OutputInterface $output) @@ -139,7 +139,7 @@ private function validateAutoUploadMapping(OutputInterface $output) } /** - * @param OutputInterface $output + * @param OutputInterface $output * @return bool */ private function validateMigrationLock(OutputInterface $output) diff --git a/Model/Config/Backend/Credentials.php b/Model/Config/Backend/Credentials.php index 75fcc66a..87a92677 100644 --- a/Model/Config/Backend/Credentials.php +++ b/Model/Config/Backend/Credentials.php @@ -48,18 +48,18 @@ class Credentials extends Encrypted protected $appConfig; /** - * @param Context $context - * @param Registry $registry - * @param ScopeConfigInterface $config - * @param TypeListInterface $cacheTypeList - * @param EncryptorInterface $encryptor - * @param ConfigurationInterface $configuration - * @param AbstractResource $resource - * @param AbstractDb $resourceCollection - * @param ConfigurationBuilder $configurationBuilder - * @param Api $api + * @param Context $context + * @param Registry $registry + * @param ScopeConfigInterface $config + * @param TypeListInterface $cacheTypeList + * @param EncryptorInterface $encryptor + * @param ConfigurationInterface $configuration + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection + * @param ConfigurationBuilder $configurationBuilder + * @param Api $api * @param ReinitableConfigInterface $appConfig - * @param array $data + * @param array $data */ public function __construct( Context $context, @@ -114,7 +114,7 @@ public function beforeSave() } /** - * @param array $credentials + * @param array $credentials * @throws ValidatorException */ private function validate(array $credentials) @@ -127,7 +127,7 @@ private function validate(array $credentials) } /** - * @param string $environmentVariable + * @param string $environmentVariable * @throws ValidatorException * @return array */ diff --git a/Model/Config/Backend/Free.php b/Model/Config/Backend/Free.php index 1c00b162..e61e0d45 100644 --- a/Model/Config/Backend/Free.php +++ b/Model/Config/Backend/Free.php @@ -40,16 +40,16 @@ class Free extends \Magento\Framework\App\Config\Value private $zendClient; /** - * @param Context $context - * @param Registry $registry - * @param ScopeConfigInterface $config - * @param TypeListInterface $cacheTypeList - * @param ConfigurationInterface $configuration, + * @param Context $context + * @param Registry $registry + * @param ScopeConfigInterface $config + * @param TypeListInterface $cacheTypeList + * @param ConfigurationInterface $configuration, * @param CloudinaryImageProvider $cloudinaryImageProvider - * @param ZendClient $zendClient - * @param AbstractResource $resource - * @param AbstractDb $resourceCollection - * @param array $data + * @param ZendClient $zendClient + * @param AbstractResource $resource + * @param AbstractDb $resourceCollection + * @param array $data */ public function __construct( Context $context, @@ -86,7 +86,7 @@ public function beforeSave() } /** - * @param string $url + * @param string $url * @throws ValidatorException */ public function validate($url) @@ -105,7 +105,7 @@ public function validate($url) } /** - * @param Zend_Http_Response $response + * @param Zend_Http_Response $response * @return Phrase */ public function formatError(Zend_Http_Response $response) @@ -117,7 +117,7 @@ public function formatError(Zend_Http_Response $response) } /** - * @param string $url + * @param string $url * @return Zend_Http_Response */ public function httpRequest($url) @@ -134,7 +134,7 @@ public function hasAccountConfigured() } /** - * @param Transformation $transformation + * @param Transformation $transformation * @return string */ public function sampleImageUrl(Transformation $transformation) @@ -146,8 +146,8 @@ public function sampleImageUrl(Transformation $transformation) } /** - * @param String $filename - * @param Transformation $transformation + * @param String $filename + * @param Transformation $transformation * @return string */ public function namedImageUrl($filename, Transformation $transformation) diff --git a/Model/Configuration.php b/Model/Configuration.php index 24d5eb1c..0323f2c0 100644 --- a/Model/Configuration.php +++ b/Model/Configuration.php @@ -79,11 +79,11 @@ class Configuration implements ConfigurationInterface private $storeManager; /** - * @param ScopeConfigInterface $configReader - * @param WriterInterface $configWriter - * @param EncryptorInterface $decryptor + * @param ScopeConfigInterface $configReader + * @param WriterInterface $configWriter + * @param EncryptorInterface $decryptor * @param AutoUploadConfigurationInterface $autoUploadConfiguration - * @param StoreManagerInterface $storeManager + * @param StoreManagerInterface $storeManager */ public function __construct( ScopeConfigInterface $configReader, @@ -159,7 +159,7 @@ public function getCdnSubdomainStatus() */ public function getUserPlatform() { - return sprintf(self::USER_PLATFORM_TEMPLATE, '1.7.3', '2.0.0'); + return sprintf(self::USER_PLATFORM_TEMPLATE, '1.7.4', '2.0.0'); } /** @@ -197,7 +197,7 @@ public function getFormatsToPreserve() } /** - * @param string $file + * @param string $file * @return string */ public function getMigratedPath($file) diff --git a/Model/ImageRepository.php b/Model/ImageRepository.php index 999ca17f..69ad711f 100644 --- a/Model/ImageRepository.php +++ b/Model/ImageRepository.php @@ -10,6 +10,7 @@ /** * Class ImageRepository + * * @package Cloudinary\Cloudinary\Model */ class ImageRepository @@ -27,7 +28,7 @@ class ImageRepository private $synchronizationChecker; /** - * @param Filesystem $filesystem + * @param Filesystem $filesystem */ public function __construct(Filesystem $filesystem, SynchronizationCheck $synchronizationChecker) { @@ -54,7 +55,7 @@ public function findUnsynchronisedImages() } /** - * @param $directory + * @param $directory * @return \RecursiveIteratorIterator */ private function getRecursiveIterator($directory) @@ -66,7 +67,7 @@ private function getRecursiveIterator($directory) } /** - * @param $item + * @param $item * @return bool */ private function isValidImageFile($item) diff --git a/Model/Logger/OutputLogger.php b/Model/Logger/OutputLogger.php index 3232b5f4..32989480 100644 --- a/Model/Logger/OutputLogger.php +++ b/Model/Logger/OutputLogger.php @@ -20,6 +20,7 @@ class OutputLogger implements OutputInterface /** * OutputLogger constructor. + * * @param LoggerInterface $logger */ public function __construct(LoggerInterface $logger) diff --git a/Model/Observer/Configuration.php b/Model/Observer/Configuration.php index b3390f66..b294a9e2 100644 --- a/Model/Observer/Configuration.php +++ b/Model/Observer/Configuration.php @@ -44,11 +44,11 @@ class Configuration implements ObserverInterface protected $changedPaths = []; /** - * @param RequestProcessor $requestProcessor - * @param ManagerInterface $messageManager + * @param RequestProcessor $requestProcessor + * @param ManagerInterface $messageManager * @param \Cloudinary\Cloudinary\Model\Configuration $configuration - * @param TypeListInterface $cacheTypeList - * @param ReinitableConfigInterface $config + * @param TypeListInterface $cacheTypeList + * @param ReinitableConfigInterface $config */ public function __construct( RequestProcessor $requestProcessor, @@ -71,11 +71,16 @@ public function execute(Observer $observer) { //Clear config cache if needed $this->changedPaths = (array) $observer->getEvent()->getChangedPaths(); - if (count(array_intersect($this->changedPaths, [ - \Cloudinary\Cloudinary\Model\Configuration::CONFIG_PATH_ENABLED, - \Cloudinary\Cloudinary\Model\Configuration::CONFIG_PATH_ENVIRONMENT_VARIABLE, - \Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration::REQUEST_PATH - ])) > 0) { + if (count( + array_intersect( + $this->changedPaths, [ + \Cloudinary\Cloudinary\Model\Configuration::CONFIG_PATH_ENABLED, + \Cloudinary\Cloudinary\Model\Configuration::CONFIG_PATH_ENVIRONMENT_VARIABLE, + \Cloudinary\Cloudinary\Model\AutoUploadMapping\AutoUploadConfiguration::REQUEST_PATH + ] + ) + ) > 0 + ) { $this->cleanConfigCache(); $this->appConfig->reinit(); } diff --git a/Model/Observer/DeleteProductImage.php b/Model/Observer/DeleteProductImage.php index 83afd0c6..25808968 100644 --- a/Model/Observer/DeleteProductImage.php +++ b/Model/Observer/DeleteProductImage.php @@ -20,7 +20,7 @@ class DeleteProductImage implements ObserverInterface private $cloudinaryImageManager; /** - * @param ProductImageFinder $productImageFinder + * @param ProductImageFinder $productImageFinder * @param CloudinaryImageManager $cloudinaryImageManager */ public function __construct( @@ -32,7 +32,7 @@ public function __construct( } /** - * @param Observer $observer + * @param Observer $observer */ public function execute(Observer $observer) { diff --git a/Model/Observer/SaveProductTransform.php b/Model/Observer/SaveProductTransform.php index 496ee79e..49236967 100644 --- a/Model/Observer/SaveProductTransform.php +++ b/Model/Observer/SaveProductTransform.php @@ -20,7 +20,7 @@ class SaveProductTransform implements ObserverInterface private $transformationFactory; /** - * @param Helper $helper + * @param Helper $helper * @param TransformationFactory $transformationFactory */ public function __construct(Helper $helper, TransformationFactory $transformationFactory) @@ -30,7 +30,7 @@ public function __construct(Helper $helper, TransformationFactory $transformatio } /** - * @param Observer $observer + * @param Observer $observer */ public function execute(Observer $observer) { diff --git a/Model/Observer/UploadProductImage.php b/Model/Observer/UploadProductImage.php index fb92612b..d0e9faf8 100644 --- a/Model/Observer/UploadProductImage.php +++ b/Model/Observer/UploadProductImage.php @@ -20,7 +20,7 @@ class UploadProductImage implements ObserverInterface private $cloudinaryImageManager; /** - * @param ProductImageFinder $productImageFinder + * @param ProductImageFinder $productImageFinder * @param CloudinaryImageManager $cloudinaryImageManager */ public function __construct( @@ -32,7 +32,7 @@ public function __construct( } /** - * @param Observer $observer + * @param Observer $observer */ public function execute(Observer $observer) { diff --git a/Model/ProductImageFinder.php b/Model/ProductImageFinder.php index fda8cba0..1a050e24 100644 --- a/Model/ProductImageFinder.php +++ b/Model/ProductImageFinder.php @@ -10,6 +10,7 @@ /** * Class ProductImageFinder + * * @package Cloudinary\Cloudinary\Model */ class ProductImageFinder @@ -48,16 +49,18 @@ public function findDeletedImages(Product $product) } /** - * @param Product $product + * @param Product $product * @param ImageFilter $filter * * @return \Cloudinary\Cloudinary\Core\Image[] */ private function find(Product $product, ImageFilter $filter) { - return array_map($this->imageCreator, array_filter( - $product->getMediaGallery('images') ?: [], - $filter - )); + return array_map( + $this->imageCreator, array_filter( + $product->getMediaGallery('images') ?: [], + $filter + ) + ); } } diff --git a/Model/ProductImageFinder/ImageCreator.php b/Model/ProductImageFinder/ImageCreator.php index b3eef664..8f987c74 100644 --- a/Model/ProductImageFinder/ImageCreator.php +++ b/Model/ProductImageFinder/ImageCreator.php @@ -10,6 +10,7 @@ /** * Class ImageCreator + * * @package Cloudinary\Cloudinary\Model\ProductImageFinder */ class ImageCreator @@ -27,7 +28,7 @@ class ImageCreator /** * ImageCreator constructor. * - * @param Filesystem $filesystem + * @param Filesystem $filesystem * @param MediaConfig $mediaConfig */ public function __construct(Filesystem $filesystem, MediaConfig $mediaConfig) diff --git a/Model/ProductImageFinder/ImageFilter.php b/Model/ProductImageFinder/ImageFilter.php index 48670a6a..a027b9b1 100644 --- a/Model/ProductImageFinder/ImageFilter.php +++ b/Model/ProductImageFinder/ImageFilter.php @@ -4,12 +4,13 @@ /** * Interface ImageFilter + * * @package Cloudinary\Cloudinary\Model\ProductImageFinder */ interface ImageFilter { /** - * @param $imageData + * @param $imageData * @return boolean */ public function __invoke($imageData); diff --git a/Model/ProductImageFinder/NewImageFilter.php b/Model/ProductImageFinder/NewImageFilter.php index 14be07dd..03dfce62 100644 --- a/Model/ProductImageFinder/NewImageFilter.php +++ b/Model/ProductImageFinder/NewImageFilter.php @@ -3,12 +3,13 @@ /** * Class NewImageFinder + * * @package Cloudinary\Cloudinary\Model\ProductImageFinder */ class NewImageFilter implements ImageFilter { /** - * @param $imageData + * @param $imageData * @return bool */ public function __invoke($imageData) diff --git a/Model/SynchronisationChecker.php b/Model/SynchronisationChecker.php index ee43f83f..93eb6902 100644 --- a/Model/SynchronisationChecker.php +++ b/Model/SynchronisationChecker.php @@ -20,7 +20,7 @@ class SynchronisationChecker implements SynchronizationCheck /** * @param SynchronisationRepositoryInterface $synchronisationRepository - * @param AutoUploadConfigurationInterface $autoUploadConfiguration + * @param AutoUploadConfigurationInterface $autoUploadConfiguration */ public function __construct( SynchronisationRepositoryInterface $synchronisationRepository, @@ -31,7 +31,7 @@ public function __construct( } /** - * @param $imageName + * @param $imageName * @return bool */ public function isSynchronized($imageName) diff --git a/Model/SynchronisationRepository.php b/Model/SynchronisationRepository.php index 9ddc1320..1b335e90 100644 --- a/Model/SynchronisationRepository.php +++ b/Model/SynchronisationRepository.php @@ -78,7 +78,7 @@ public function __construct( * * @api * - * @param SearchCriteriaInterface $searchCriteria + * @param SearchCriteriaInterface $searchCriteria * @return SearchResultsInterface */ public function getList(SearchCriteriaInterface $searchCriteria) @@ -99,7 +99,7 @@ public function getList(SearchCriteriaInterface $searchCriteria) } /** - * @param string $imagePath + * @param string $imagePath * * @return SearchResultsInterface */ @@ -111,7 +111,7 @@ public function getListByImagePath($imagePath) } /** - * @param string $imagePath + * @param string $imagePath */ public function saveAsSynchronized($imagePath) { @@ -135,7 +135,7 @@ public function removeSynchronised($imagePath) /** * Create image name filter * - * @param string $imagePath + * @param string $imagePath * @return \Magento\Framework\Api\Filter */ private function createImagePathFilter($imagePath) @@ -148,8 +148,8 @@ private function createImagePathFilter($imagePath) } /** - * @param SearchCriteriaInterface $searchCriteria - * @param SynchronisationCollection $collection + * @param SearchCriteriaInterface $searchCriteria + * @param SynchronisationCollection $collection */ private function setFilters(SearchCriteriaInterface $searchCriteria, $collection) { diff --git a/Model/Template/Filter.php b/Model/Template/Filter.php index cca3bdc2..8897e606 100644 --- a/Model/Template/Filter.php +++ b/Model/Template/Filter.php @@ -9,7 +9,7 @@ class Filter extends WidgetFilter /** * Return associative array of parameters *exposing $this->getParameters(). * - * @param string $value raw parameters + * @param string $value raw parameters * @return array */ public function getParams($value) diff --git a/Model/Transformation.php b/Model/Transformation.php index b23aecad..d2979ebb 100644 --- a/Model/Transformation.php +++ b/Model/Transformation.php @@ -17,12 +17,12 @@ class Transformation extends AbstractModel private $configuration; /** - * @param Context $context - * @param Registry $registry - * @param Configuration $configuration + * @param Context $context + * @param Registry $registry + * @param Configuration $configuration * @param AbstractResource $resource - * @param AbstractDb $resourceCollection - * @param array $data + * @param AbstractDb $resourceCollection + * @param array $data */ public function __construct( Context $context, @@ -42,7 +42,7 @@ protected function _construct() } /** - * @param string $imageName + * @param string $imageName * @return $this */ public function setImageName($imageName) @@ -59,7 +59,7 @@ public function getImageName() } /** - * @param string $transformation + * @param string $transformation * @return $this */ public function setFreeTransformation($transformation) @@ -76,7 +76,7 @@ public function getFreeTransformation() } /** - * @param string $imageFile + * @param string $imageFile * @return ImageTransformation */ public function transformationForImage($imageFile) @@ -88,8 +88,8 @@ public function transformationForImage($imageFile) } /** - * @param ImageTransformation $transformation - * @param string $imageFile + * @param ImageTransformation $transformation + * @param string $imageFile * @return ImageTransformation */ public function addFreeformTransformationForImage(ImageTransformation $transformation, $imageFile) diff --git a/Plugin/Catalog/Block/Product/ImageFactory.php b/Plugin/Catalog/Block/Product/ImageFactory.php index 56a1aa82..aa22d983 100644 --- a/Plugin/Catalog/Block/Product/ImageFactory.php +++ b/Plugin/Catalog/Block/Product/ImageFactory.php @@ -1,7 +1,5 @@ _resourceConnection = $resourceConnection; + $this->resourceConnection = $resourceConnection; + $this->output = $output; } /** @@ -41,10 +49,10 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface if (version_compare($context->getVersion(), '1.6.3') < 0) { if ($context->getVersion()) { - echo "- Reseting configurations for 'website' & 'store' scopes (only supports 'default' at the moment).\n"; + $this->output->writeln("Reseting configurations for 'website' & 'store' scopes (only supports 'default' at the moment)"); } - $this->_resourceConnection->getConnection()->delete( - $this->_resourceConnection->getTableName('core_config_data'), + $this->resourceConnection->getConnection()->delete( + $this->resourceConnection->getTableName('core_config_data'), "path LIKE 'cloudinary/%' AND scope != 'default'" ); } diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index d3175e9a..eea58a79 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -10,7 +10,7 @@ class UpgradeSchema implements UpgradeSchemaInterface { /** - * @param SchemaSetupInterface $setup + * @param SchemaSetupInterface $setup * @param ModuleContextInterface $context */ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context) diff --git a/Ui/DataProvider/Product/Form/Modifier/Product.php b/Ui/DataProvider/Product/Form/Modifier/Product.php index 7bfc78c1..ec2d268e 100644 --- a/Ui/DataProvider/Product/Form/Modifier/Product.php +++ b/Ui/DataProvider/Product/Form/Modifier/Product.php @@ -48,7 +48,7 @@ class Product extends AbstractModifier protected $cloudinaryImageProvider; /** - * @param LocatorInterface $locator + * @param LocatorInterface $locator * @param TransformationFactory $transformationFactory */ public function __construct( @@ -154,13 +154,13 @@ private function isCloudinaryModuleActive() } /** - * @param [Entry] + * @param [Entry] * @return [Entry] */ private function extractData(array $images) { return array_map( - function($media) { + function ($media) { return $media->getData(); }, $images @@ -168,21 +168,21 @@ function($media) { } /** - * @param [Entry] + * @param [Entry] * @return [Entry] */ private function filterNonImageTypes(array $images) { return array_filter( $images, - function($image) { + function ($image) { return $image->getMediaType() === 'image'; } ); } /** - * @param [Entry] + * @param [Entry] * @return [Entry] */ private function injectFreeTransformations(array $images) @@ -197,7 +197,7 @@ private function injectFreeTransformations(array $images) } /** - * @param [Entry] + * @param [Entry] * @return [Entry] */ private function injectImageUrls(array $images) @@ -218,7 +218,7 @@ private function injectImageUrls(array $images) } /** - * @param string $freeTransform + * @param string $freeTransform * @return Transformation */ private function defaultTransformWithFreeTransform($freeTransform) diff --git a/composer.json b/composer.json index 30225bb1..41d9be32 100644 --- a/composer.json +++ b/composer.json @@ -2,55 +2,11 @@ "name": "cloudinary/cloudinary-magento2", "description": "Cloudinary Magento 2 Integration.", "type": "magento2-module", - "version": "1.7.3", - "minimum-stability": "dev", + "version": "1.7.4", "license": "MIT", - "repositories": { - "0": { - "type": "git", - "url": "https://github.com/cloudinary/cloudinary_magento2" - } - }, "require": { - "php": ">=5.5", "cloudinary/cloudinary_php": "*" }, - "require-dev": { - "phpspec/phpspec": "^3.0", - "behat/behat": "~3.0.15", - "sensiolabs/behat-page-object-extension": "2.0.*@dev", - "behat/mink-selenium2-driver": "*", - "behat/mink-goutte-driver": "^1.0", - "squizlabs/php_codesniffer": "1.*", - "phpmd/phpmd": "1.*", - "sebastian/phpcpd": "2.*", - "pdepend/pdepend": "1.*", - "phploc/phploc": "2.*", - "theseer/phpdox": "0.6.*", - "theseer/fxsl": "1.0.*@dev", - "covex-nn/phpcb": "1.0.*@dev", - "bossa/phpspec2-expect": "^2.0", - "bex/behat-magento2-init": "dev-master", - "bex/behat-browser-initialiser": "^1.0", - "bex/behat-screenshot": "^1.0" - }, - "config": { - "bin-dir": "bin", - "use-include-path": true - }, - "autoload-dev": { - "psr-0": { - "": [ - "features/bootstrap", - "features/fixtures", - "/app/app/code/" - ] - }, - "psr-4": { - "Magento\\Framework\\": "/app/vendor/magento/framework/", - "Magento\\Catalog\\": "/app/vendor/magento/module-catalog" - } - }, "autoload": { "files": [ "registration.php" @@ -58,5 +14,9 @@ "psr-4": { "Cloudinary\\Cloudinary\\": "" } - } + }, + "repositories": [{ + "type": "git", + "url": "https://github.com/cloudinary/cloudinary_magento2" + }] } \ No newline at end of file diff --git a/etc/module.xml b/etc/module.xml index 9ccb5dc0..1454e474 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + diff --git a/view/adminhtml/web/js/cloudinary-free.js b/view/adminhtml/web/js/cloudinary-free.js index 8bdf1c2e..96d257ec 100644 --- a/view/adminhtml/web/js/cloudinary-free.js +++ b/view/adminhtml/web/js/cloudinary-free.js @@ -1,80 +1,103 @@ -define(['jquery'], function ($) { - 'use strict'; +define( + [ + 'jquery' + ], function ($) { + 'use strict'; + + $.widget( + 'cloudinary.cloudinaryFreeTransform', { + + currentTransform: '', + + getTransformText: function () { + return $(this.options.transformInputFieldId).val(); + }, + + getImageHtml: function (src) { + var id = 'cloudinary_custom_transform_preview_image', + style = 'width: auto; height: auto; max-width: 500px; max-height: 500px; min-height: 50px;', + footer = '

Image size restricted for viewing purposes

'; + return '' + footer; + }, + + getErrorHtml: function (message) { + return '
  • ' + message + '
'; + }, + + updatePreviewImage: function (url) { + var $image = $('#cloudinary_custom_transform_preview_image'); + + if (!$image.length) { + $('#cloudinary_custom_transform_preview').html(this.getImageHtml(url)); + } else { + $image.attr('src', url); + } + }, + + updatePreview: function () { + var self = this; + + if (!self.isPreviewActive()) { + return; + } + + self.currentTransform = self.getTransformText(); + self.setPreviewActiveState(false); + + $.ajax( + { + url: this.options.ajaxUrl, + data: { + free: self.getTransformText(), + form_key: self.options.ajaxKey + }, + type: 'post', + dataType: 'json', + showLoader: true + } + ).done( + function (response) { + self.updatePreviewImage(response.url); + } + ).fail( + function (result) { + $('#cloudinary_custom_transform_preview').html(self.getErrorHtml(result.responseJSON.error)); + } + ); + }, + + setPreviewActiveState: function (state) { + if (state && (this.currentTransform !== this.getTransformText())) { + $(this.options.previewButtonId).removeClass('disabled'); + } else { + $(this.options.previewButtonId).addClass('disabled'); + } + }, + + isPreviewActive: function () { + return !$(this.options.previewButtonId).hasClass('disabled'); + }, + + _create: function () { + var self = this; + + $(this.options.previewButtonId).on( + 'click', + function () { + self.updatePreview(); + } + ); + $(this.options.transformInputFieldId).on( + 'change keydown paste input', + function () { + self.setPreviewActiveState(true); + } + ); + } - $.widget('cloudinary.cloudinaryFreeTransform', { - - currentTransform: '', - - getTransformText: function() { - return $(this.options.transformInputFieldId).val(); - }, - - getImageHtml: function(src) { - var id = 'cloudinary_custom_transform_preview_image', - style = 'width: auto; height: auto; max-width: 500px; max-height: 500px; min-height: 50px;', - footer = '

Image size restricted for viewing purposes

'; - return '' + footer; - }, - - getErrorHtml: function(message) { - return '
  • ' + message + '
'; - }, - - updatePreviewImage: function(url) { - var $image = $('#cloudinary_custom_transform_preview_image'); - - if (!$image.length) { - $('#cloudinary_custom_transform_preview').html(this.getImageHtml(url)); - } else { - $image.attr('src', url); } - }, - - updatePreview: function() { - var self = this; - - if (!self.isPreviewActive()) { - return; - } - - self.currentTransform = self.getTransformText(); - self.setPreviewActiveState(false); - - $.ajax({ - url: this.options.ajaxUrl, - data: {free: self.getTransformText(), form_key: self.options.ajaxKey}, - type: 'post', - dataType: 'json', - showLoader: true - }).done(function(response) { - self.updatePreviewImage(response.url); - }).fail(function(result) { - $('#cloudinary_custom_transform_preview').html(self.getErrorHtml(result.responseJSON.error)); - }); - }, - - setPreviewActiveState: function(state) { - if (state && (this.currentTransform !== this.getTransformText())) { - $(this.options.previewButtonId).removeClass('disabled'); - } else { - $(this.options.previewButtonId).addClass('disabled'); - } - }, - - isPreviewActive: function() { - return !$(this.options.previewButtonId).hasClass('disabled'); - }, - - _create: function () { - var self = this; - - $(this.options.previewButtonId).on('click', function() { self.updatePreview(); }); - $(this.options.transformInputFieldId).on('change keydown paste input', function() { - self.setPreviewActiveState(true); - }); - } - - }); + ); - return $.cloudinary.cloudinaryFreeTransform; -}); + return $.cloudinary.cloudinaryFreeTransform; + } +); \ No newline at end of file diff --git a/view/adminhtml/web/js/get-video-information.js b/view/adminhtml/web/js/get-video-information.js index 6e5f17df..82a68f51 100644 --- a/view/adminhtml/web/js/get-video-information.js +++ b/view/adminhtml/web/js/get-video-information.js @@ -2,800 +2,850 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -define([ - 'jquery', - 'Magento_Ui/js/modal/alert', - 'jquery/ui', - 'mage/translate' -], function($, alert) { - 'use strict'; - - var videoRegister = { - _register: {}, - - /** - * Checks, if api is already registered - * - * @param {String} api - * @returns {bool} - */ - isRegistered: function(api) { - return this._register[api] !== undefined; - }, - - /** - * Checks, if api is loaded - * - * @param {String} api - * @returns {bool} - */ - isLoaded: function(api) { - return this._register[api] !== undefined && this._register[api] === true; - }, - - /** - * Register new video api - * @param {String} api - * @param {bool} loaded - */ - register: function(api, loaded) { - loaded = loaded || false; - this._register[api] = loaded; - } - }; - - $.widget('mage.productVideoLoader', { - - /** - * @private - */ - _create: function() { - switch (this.element.data('type')) { - case 'youtube': - this.element.videoYoutube(); - this._player = this.element.data('mageVideoYoutube'); - break; - - case 'vimeo': - this.element.videoVimeo(); - this._player = this.element.data('mageVideoVimeo'); - break; - case 'cloudinary': - this.element.videoCloudinary(); - this._player = this.element.data('mageVideoCloudinary'); - break; - default: - throw { - name: $.mage.__('Video Error'), - message: $.mage.__('Unknown video type'), +define( + [ + 'jquery', + 'Magento_Ui/js/modal/alert', + 'jquery/ui', + 'mage/translate' + ], + function ($, alert) { + 'use strict'; + + var videoRegister = { + _register: {}, - /** - * Return string - */ - toString: function() { - return this.name + ': ' + this.message; - } - }; - } - }, - - /** - * Initializes variables - * @private - */ - _initialize: function() { - this._params = this.element.data('params') || {}; - this._code = this.element.data('code'); - this._width = this.element.data('width'); - this._height = this.element.data('height'); - this._autoplay = !!this.element.data('autoplay'); - this._videoSrc = this.element.data('video-src'); - this._playing = this._autoplay || false; - this.useYoutubeNocookie = this.element.data('youtubenocookie') || false; - - this._responsive = this.element.data('responsive') !== false; - - if (this._responsive === true) { - this.element.addClass('responsive'); - } + /** + * Checks, if api is already registered + * + * @param {String} api + * @returns {bool} + */ + isRegistered: function (api) { + return this._register[api] !== undefined; + }, - this._calculateRatio(); - }, - - /** - * Abstract play command - */ - play: function() { - this._player.play(); - }, - - /** - * Abstract pause command - */ - pause: function() { - this._player.pause(); - }, - - /** - * Abstract stop command - */ - stop: function() { - this._player.stop(); - }, - - /** - * Abstract playing command - */ - playing: function() { - return this._player.playing(); - }, - - /** - * Abstract destroying command - */ - destroy: function() { - this._player.destroy(); - }, - - /** - * Calculates ratio for responsive videos - * @private - */ - _calculateRatio: function() { - if (!this._responsive) { - return; + /** + * Checks, if api is loaded + * + * @param {String} api + * @returns {bool} + */ + isLoaded: function (api) { + return this._register[api] !== undefined && this._register[api] === true; + }, + + /** + * Register new video api + * + * @param {String} api + * @param {bool} loaded + */ + register: function (api, loaded) { + loaded = loaded || false; + this._register[api] = loaded; } - this.element.css('paddingBottom', this._height / this._width * 100 + '%'); - } - }); + }; - $.widget('mage.videoYoutube', $.mage.productVideoLoader, { + $.widget( + 'mage.productVideoLoader', { - /** - * Initialization of the Youtube widget - * @private - */ - _create: function() { - var self = this; + /** + * @private + */ + _create: function () { + switch (this.element.data('type')) { + case 'youtube': + this.element.videoYoutube(); + this._player = this.element.data('mageVideoYoutube'); + break; - this._initialize(); + case 'vimeo': + this.element.videoVimeo(); + this._player = this.element.data('mageVideoVimeo'); + break; + case 'cloudinary': + this.element.videoCloudinary(); + this._player = this.element.data('mageVideoCloudinary'); + break; + default: + throw { + name: $.mage.__('Video Error'), + message: $.mage.__('Unknown video type'), + + /** + * Return string + */ + toString: function () { + return this.name + ': ' + this.message; + } + }; + } + }, - this.element.append('
'); + /** + * Initializes variables + * + * @private + */ + _initialize: function () { + this._params = this.element.data('params') || {}; + this._code = this.element.data('code'); + this._width = this.element.data('width'); + this._height = this.element.data('height'); + this._autoplay = !!this.element.data('autoplay'); + this._videoSrc = this.element.data('video-src'); + this._playing = this._autoplay || false; + this.useYoutubeNocookie = this.element.data('youtubenocookie') || false; + + this._responsive = this.element.data('responsive') !== false; + + if (this._responsive === true) { + this.element.addClass('responsive'); + } - this._on(window, { + this._calculateRatio(); + }, /** - * Youtube state check - * @private + * Abstract play command */ - 'youtubeapiready': function() { - var host = 'https://www.youtube.com'; + play: function () { + this._player.play(); + }, - if (self.useYoutubeNocookie) { - host = 'https://www.youtube-nocookie.com'; - } + /** + * Abstract pause command + */ + pause: function () { + this._player.pause(); + }, + + /** + * Abstract stop command + */ + stop: function () { + this._player.stop(); + }, - if (self._player !== undefined) { + /** + * Abstract playing command + */ + playing: function () { + return this._player.playing(); + }, + + /** + * Abstract destroying command + */ + destroy: function () { + this._player.destroy(); + }, + + /** + * Calculates ratio for responsive videos + * + * @private + */ + _calculateRatio: function () { + if (!this._responsive) { return; } + this.element.css('paddingBottom', this._height / this._width * 100 + '%'); + } + } + ); - if (self._autoplay) { - self._params.autoplay = 1; - } - self._params.rel = 0; + $.widget( + 'mage.videoYoutube', $.mage.productVideoLoader, { + + /** + * Initialization of the Youtube widget + * + * @private + */ + _create: function () { + var self = this; + + this._initialize(); + + this.element.append('
'); - self._player = new window.YT.Player(self.element.children(':first')[0], { - height: self._height, - width: self._width, - videoId: self._code, - playerVars: self._params, - host: host, - events: { + this._on( + window, { /** + * Youtube state check + * * @private */ - 'onReady': function onPlayerReady() { - self._player.getDuration(); - }, + 'youtubeapiready': function () { + var host = 'https://www.youtube.com'; - /** - * State change flag init - */ - onStateChange: function(data) { - switch (window.parseInt(data.data, 10)) { - case 1: - self._playing = true; - break; - default: - self._playing = false; - break; + if (self.useYoutubeNocookie) { + host = 'https://www.youtube-nocookie.com'; } - self._trigger('statechange', {}, data); + if (self._player !== undefined) { + return; + } + + if (self._autoplay) { + self._params.autoplay = 1; + } + self._params.rel = 0; + + self._player = new window.YT.Player( + self.element.children(':first')[0], { + height: self._height, + width: self._width, + videoId: self._code, + playerVars: self._params, + host: host, + events: { + + /** + * @private + */ + 'onReady': function onPlayerReady() + { + self._player.getDuration(); + }, + + /** + * State change flag init + */ + onStateChange: function (data) { + switch (window.parseInt(data.data, 10)) { + case 1: + self._playing = true; + break; + default: + self._playing = false; + break; + } + + self._trigger('statechange', {}, data); + } + } + + } + ); } } + ); - }); - } - }); - - this._loadApi(); - }, - - /** - * Loads Youtube API and triggers event, when loaded - * @private - */ - _loadApi: function() { - var element, - scriptTag; - - if (videoRegister.isRegistered('youtube')) { - if (videoRegister.isLoaded('youtube')) { - $(window).trigger('youtubeapiready'); - } + this._loadApi(); + }, - return; - } - videoRegister.register('youtube'); + /** + * Loads Youtube API and triggers event, when loaded + * + * @private + */ + _loadApi: function () { + var element, + scriptTag; - element = document.createElement('script'); - scriptTag = document.getElementsByTagName('script')[0]; + if (videoRegister.isRegistered('youtube')) { + if (videoRegister.isLoaded('youtube')) { + $(window).trigger('youtubeapiready'); + } - element.async = true; - element.src = 'https://www.youtube.com/iframe_api'; - scriptTag.parentNode.insertBefore(element, scriptTag); + return; + } + videoRegister.register('youtube'); - /** - * Trigger youtube api ready event - */ - window.onYouTubeIframeAPIReady = function() { - $(window).trigger('youtubeapiready'); - videoRegister.register('youtube', true); - }; - }, - - /** - * Play command for Youtube - */ - play: function() { - this._player.playVideo(); - this._playing = true; - }, - - /** - * Pause command for Youtube - */ - pause: function() { - this._player.pauseVideo(); - this._playing = false; - }, - - /** - * Stop command for Youtube - */ - stop: function() { - this._player.stopVideo(); - this._playing = false; - }, - - /** - * Playing command for Youtube - */ - playing: function() { - return this._playing; - }, - - /** - * stops and unloads player - * @private - */ - destroy: function() { - this.stop(); - this._player.destroy(); - } - }); - - $.widget('mage.videoVimeo', $.mage.productVideoLoader, { - - /** - * Initialize the Vimeo widget - * @private - */ - _create: function() { - var timestamp, - src, - additionalParams; - - this._initialize(); - timestamp = new Date().getTime(); - - if (this._autoplay) { - additionalParams += '&autoplay=1'; - } + element = document.createElement('script'); + scriptTag = document.getElementsByTagName('script')[0]; - src = 'https://player.vimeo.com/video/' + - this._code + '?api=1&player_id=vimeo' + - this._code + - timestamp + - additionalParams; - this.element.append( - $('