Cloudinary_Cloudinary - Version 2.0.0

Version Notes

v1.2.0

Release Highlights:

* Foldered migration

Download this release

Release Info

Developer Cloudinary
Extension Cloudinary_Cloudinary
Version 2.0.0
Comparing to
See all releases


Code changes from version 1.2.2 to 2.0.0

Files changed (58) hide show
  1. app/code/community/Cloudinary/Cloudinary/Block/Adminhtml/Manage.php +1 -1
  2. app/code/community/Cloudinary/Cloudinary/Block/Adminhtml/System/Config/Signup.php +5 -3
  3. app/code/community/Cloudinary/Cloudinary/Helper/Configuration.php +0 -173
  4. app/code/community/Cloudinary/Cloudinary/Helper/Configuration/Validation.php +0 -28
  5. app/code/community/Cloudinary/Cloudinary/Helper/Console.php +1 -4
  6. app/code/community/Cloudinary/Cloudinary/Helper/Image.php +63 -35
  7. app/code/community/Cloudinary/Cloudinary/Helper/Util/ArrayUtils.php +0 -22
  8. app/code/community/Cloudinary/Cloudinary/Model/Catalog/Product/Image.php +18 -11
  9. app/code/community/Cloudinary/Cloudinary/Model/Catalog/Product/Media/Config.php +19 -15
  10. app/code/community/Cloudinary/Cloudinary/Model/Cms/Adminhtml/Template/Filter.php +52 -16
  11. app/code/community/Cloudinary/Cloudinary/Model/Cms/Synchronisation.php +5 -11
  12. app/code/community/Cloudinary/Cloudinary/Model/Cms/Template/Filter.php +31 -12
  13. app/code/community/Cloudinary/Cloudinary/Model/Cms/Uploader.php +13 -43
  14. app/code/community/Cloudinary/Cloudinary/Model/Cms/Wysiwyg/Images/Storage.php +55 -26
  15. app/code/community/Cloudinary/Cloudinary/Model/Configuration.php +209 -0
  16. app/code/community/Cloudinary/Cloudinary/Model/Cron.php +3 -1
  17. app/code/community/Cloudinary/Cloudinary/Model/Image.php +8 -27
  18. app/code/community/Cloudinary/Cloudinary/Model/Logger.php +0 -9
  19. app/code/community/Cloudinary/Cloudinary/Model/MagentoFolderTranslator.php +1 -2
  20. app/code/community/Cloudinary/Cloudinary/Model/MediaCollectionCounter.php +0 -24
  21. app/code/community/Cloudinary/Cloudinary/Model/Observer.php +68 -59
  22. app/code/community/Cloudinary/Cloudinary/Model/PreConditionsValidator.php +0 -29
  23. app/code/community/Cloudinary/Cloudinary/Model/Resource/Cms/Synchronisation/Collection.php +7 -9
  24. app/code/community/Cloudinary/Cloudinary/Model/Resource/Media/Collection/Interface.php +0 -8
  25. app/code/community/Cloudinary/Cloudinary/Model/Resource/Synchronisation/Collection.php +0 -1
  26. app/code/community/Cloudinary/Cloudinary/Model/SyncedImages.php +0 -31
  27. app/code/community/Cloudinary/Cloudinary/Model/Synchronisation.php +3 -3
  28. app/code/community/Cloudinary/Cloudinary/Model/SynchronizationChecker.php +18 -0
  29. app/code/community/Cloudinary/Cloudinary/controllers/Adminhtml/CloudinaryController.php +22 -5
  30. app/code/community/Cloudinary/Cloudinary/etc/config.xml +1 -1
  31. lib/Cloudinary/Api.php +144 -7
  32. lib/Cloudinary/AuthToken.php +72 -0
  33. lib/Cloudinary/Cloudinary.php +342 -67
  34. lib/Cloudinary/Helpers.php +2 -1
  35. lib/Cloudinary/Uploader.php +66 -28
  36. lib/CloudinaryExtension/CloudinaryImageManager.php +52 -0
  37. lib/CloudinaryExtension/CloudinaryImageProvider.php +51 -44
  38. lib/CloudinaryExtension/Configuration.php +0 -97
  39. lib/CloudinaryExtension/ConfigurationBuilder.php +30 -0
  40. lib/CloudinaryExtension/ConfigurationInterface.php +67 -0
  41. lib/CloudinaryExtension/CredentialValidator.php +18 -0
  42. lib/CloudinaryExtension/Credentials.php +6 -1
  43. lib/CloudinaryExtension/Image.php +4 -2
  44. lib/CloudinaryExtension/Image/ImageFactory.php +46 -0
  45. lib/CloudinaryExtension/Image/LocalImage.php +25 -0
  46. lib/CloudinaryExtension/Image/Synchronizable.php +11 -0
  47. lib/CloudinaryExtension/Image/SynchronizationChecker.php +11 -0
  48. lib/CloudinaryExtension/Image/Transformation.php +29 -25
  49. lib/CloudinaryExtension/Image/Transformation/Crop.php +13 -0
  50. lib/CloudinaryExtension/Image/Transformation/Dimensions.php +0 -1
  51. lib/CloudinaryExtension/ImageInterface.php +11 -0
  52. lib/CloudinaryExtension/ImageProvider.php +3 -2
  53. lib/CloudinaryExtension/Security/CloudinaryEnvironmentVariable.php +7 -10
  54. lib/CloudinaryExtension/SynchroniseAssetsRepositoryInterface.php +18 -0
  55. lib/CloudinaryExtension/UploadConfig.php +73 -0
  56. lib/CloudinaryExtension/UploadResponseValidator.php +17 -0
  57. lib/CloudinaryExtension/UrlGenerator.php +65 -0
  58. package.xml +4 -4
app/code/community/Cloudinary/Cloudinary/Block/Adminhtml/Manage.php CHANGED
@@ -17,7 +17,7 @@ class Cloudinary_Cloudinary_Block_Adminhtml_Manage extends Mage_Adminhtml_Block_
17
  $this->_migrationTask = Mage::getModel('cloudinary_cloudinary/migration')
18
  ->load(Cloudinary_Cloudinary_Model_Migration::CLOUDINARY_MIGRATION_ID);
19
 
20
- $this->_cloudinaryConfig = Mage::helper('cloudinary_cloudinary/configuration');
21
 
22
  parent::__construct();
23
  }
17
  $this->_migrationTask = Mage::getModel('cloudinary_cloudinary/migration')
18
  ->load(Cloudinary_Cloudinary_Model_Migration::CLOUDINARY_MIGRATION_ID);
19
 
20
+ $this->_cloudinaryConfig = Mage::getModel('cloudinary_cloudinary/configuration');
21
 
22
  parent::__construct();
23
  }
app/code/community/Cloudinary/Cloudinary/Block/Adminhtml/System/Config/Signup.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  class Cloudinary_Cloudinary_Block_Adminhtml_System_Config_Signup extends Mage_Adminhtml_Block_Abstract
4
  implements Varien_Data_Form_Element_Renderer_Interface
5
  {
@@ -19,8 +21,8 @@ class Cloudinary_Cloudinary_Block_Adminhtml_System_Config_Signup extends Mage_Ad
19
 
20
  private function _environmentVariableIsPresentInConfig()
21
  {
22
- $configuration = Mage::helper('cloudinary_cloudinary/configuration');
23
- return $configuration->getEnvironmentVariable();
 
24
  }
25
-
26
  }
1
  <?php
2
 
3
+ use Cloudinary_Cloudinary_Model_Configuration as Configuration;
4
+
5
  class Cloudinary_Cloudinary_Block_Adminhtml_System_Config_Signup extends Mage_Adminhtml_Block_Abstract
6
  implements Varien_Data_Form_Element_Renderer_Interface
7
  {
21
 
22
  private function _environmentVariableIsPresentInConfig()
23
  {
24
+ return Mage::helper('core')->decrypt(
25
+ Mage::getStoreConfig(Configuration::CONFIG_PATH_ENVIRONMENT_VARIABLE)
26
+ );
27
  }
 
28
  }
app/code/community/Cloudinary/Cloudinary/Helper/Configuration.php DELETED
@@ -1,173 +0,0 @@
1
- <?php
2
-
3
- use CloudinaryExtension\Configuration;
4
- use CloudinaryExtension\Image\Transformation;
5
- use CloudinaryExtension\Image\Transformation\Dpr;
6
- use CloudinaryExtension\Image\Transformation\FetchFormat;
7
- use CloudinaryExtension\Image\Transformation\Gravity;
8
- use CloudinaryExtension\Image\Transformation\Quality;
9
- use CloudinaryExtension\Security\CloudinaryEnvironmentVariable;
10
-
11
- class Cloudinary_Cloudinary_Helper_Configuration extends Mage_Core_Helper_Abstract
12
- {
13
- const CONFIG_PATH_ENABLED = 'cloudinary/cloud/cloudinary_enabled';
14
-
15
- const CONFIG_PATH_ENVIRONMENT_VARIABLE = 'cloudinary/setup/cloudinary_environment_variable';
16
-
17
- const CONFIG_DEFAULT_GRAVITY = 'cloudinary/transformations/cloudinary_gravity';
18
-
19
- const CONFIG_DEFAULT_QUALITY = 'cloudinary/transformations/cloudinary_image_quality';
20
-
21
- const CONFIG_DEFAULT_DPR = 'cloudinary/transformations/cloudinary_image_dpr';
22
-
23
- const CONFIG_DEFAULT_FETCH_FORMAT = 'cloudinary/transformations/cloudinary_fetch_format';
24
-
25
- const CONFIG_CDN_SUBDOMAIN = 'cloudinary/configuration/cloudinary_cdn_subdomain';
26
-
27
- const CONFIG_FOLDERED_MIGRATION = 'cloudinary/configuration/cloudinary_foldered_migration';
28
-
29
- const STATUS_ENABLED = 1;
30
-
31
- const STATUS_DISABLED = 0;
32
-
33
- const USER_PLATFORM_TEMPLATE = 'CloudinaryMagento/%s (Magento %s)';
34
-
35
- private $folderTranslator;
36
-
37
- private $isActive;
38
-
39
- public function __construct()
40
- {
41
- $this->isActive = true;
42
- $this->folderTranslator = Mage::getModel('cloudinary_cloudinary/magentoFolderTranslator');
43
- }
44
-
45
- public function activate()
46
- {
47
- $this->isActive = true;
48
- }
49
-
50
- public function deactivate()
51
- {
52
- $this->isActive = false;
53
- }
54
-
55
- public function buildCredentials()
56
- {
57
- $environmentVariable = CloudinaryEnvironmentVariable::fromString($this->getEnvironmentVariable());
58
- return $environmentVariable->getCredentials();
59
- }
60
-
61
- public function getEnvironmentVariable()
62
- {
63
- return Mage::helper('core')->decrypt(Mage::getStoreConfig(self::CONFIG_PATH_ENVIRONMENT_VARIABLE));
64
- }
65
-
66
- public function getDefaultGravity()
67
- {
68
- return (string)Mage::getStoreConfig(self::CONFIG_DEFAULT_GRAVITY);
69
- }
70
-
71
- public function getFetchFormat()
72
- {
73
- return Mage::getStoreConfig(self::CONFIG_DEFAULT_FETCH_FORMAT) === "1" ? FetchFormat::FETCH_FORMAT_AUTO : null;
74
- }
75
-
76
- public function getImageQuality()
77
- {
78
- return (string)Mage::getStoreConfig(self::CONFIG_DEFAULT_QUALITY);
79
- }
80
-
81
- public function getImageDpr()
82
- {
83
- return (string)Mage::getStoreConfig(self::CONFIG_DEFAULT_DPR);
84
- }
85
-
86
- public function getCdnSubdomainFlag()
87
- {
88
- return (boolean)Mage::getStoreConfig(self::CONFIG_CDN_SUBDOMAIN);
89
- }
90
-
91
- public function isFolderedMigration()
92
- {
93
- return Mage::getStoreConfigFlag(self::CONFIG_FOLDERED_MIGRATION);
94
- }
95
-
96
- public function getMigratedPath($file)
97
- {
98
- if ($this->isFolderedMigration()) {
99
- $result = $this->folderTranslator->translate($file);
100
- } else {
101
- $result = basename($file);
102
- }
103
- return $result;
104
- }
105
-
106
- public function reverseMigratedPathIfNeeded($migratedPath)
107
- {
108
- if ($this->isFolderedMigration()) {
109
- return $this->folderTranslator->reverse($migratedPath);
110
- }
111
- return $migratedPath;
112
- }
113
-
114
- public function isEnabled()
115
- {
116
- return $this->isActive && (boolean)Mage::getStoreConfig(self::CONFIG_PATH_ENABLED);
117
- }
118
-
119
- public function enable()
120
- {
121
- $this->_setStoreConfig(self::CONFIG_PATH_ENABLED, self::STATUS_ENABLED);
122
- }
123
-
124
- public function disable()
125
- {
126
- $this->_setStoreConfig(self::CONFIG_PATH_ENABLED, self::STATUS_DISABLED);
127
- }
128
-
129
- public function getUserPlatform()
130
- {
131
- return sprintf(
132
- self::USER_PLATFORM_TEMPLATE,
133
- Mage::getConfig()->getModuleConfig('Cloudinary_Cloudinary')->version,
134
- Mage::getVersion()
135
- );
136
- }
137
-
138
- public function buildConfiguration()
139
- {
140
- $config = Configuration::fromEnvironmentVariable(
141
- CloudinaryEnvironmentVariable::fromString($this->getEnvironmentVariable())
142
- );
143
-
144
- $config->setUserPlatform($this->getUserPlatform());
145
-
146
- if ($this->getCdnSubdomainFlag()) {
147
- $config->enableCdnSubdomain();
148
- }
149
-
150
- $config->getDefaultTransformation()
151
- ->withGravity(Gravity::fromString($this->getDefaultGravity()))
152
- ->withFetchFormat(FetchFormat::fromString($this->getFetchFormat()))
153
- ->withQuality(Quality::fromString($this->getImageQuality()))
154
- ->withDpr(Dpr::fromString($this->getImageDpr()));
155
-
156
- return $config;
157
- }
158
-
159
- private function _setStoreConfig($configPath, $value)
160
- {
161
- $config = new Mage_Core_Model_Config();
162
- $config->saveConfig($configPath, $value)->reinit();
163
- }
164
-
165
- /**
166
- * @return Cloudinary_Cloudinary_Helper_Configuration
167
- */
168
- public static function getInstance()
169
- {
170
- return Mage::helper('cloudinary_cloudinary/configuration');
171
- }
172
-
173
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/community/Cloudinary/Cloudinary/Helper/Configuration/Validation.php DELETED
@@ -1,28 +0,0 @@
1
- <?php
2
-
3
- use CloudinaryExtension\CloudinaryImageProvider;
4
- use CloudinaryExtension\Configuration;
5
- use CloudinaryExtension\Exception\InvalidCredentials;
6
- use CloudinaryExtension\Security\CloudinaryEnvironmentVariable;
7
-
8
- class Cloudinary_Cloudinary_Helper_Configuration_Validation extends Mage_Core_Helper_Abstract
9
- {
10
-
11
- public function validateEnvironmentVariable($environmentVariable)
12
- {
13
- $configuration = $this->_getConfiguration($environmentVariable);
14
- $imageProvider = CloudinaryImageProvider::fromConfiguration($configuration);
15
-
16
- if (!$imageProvider->validateCredentials()) {
17
- throw new InvalidCredentials("There was a problem validating your Cloudinary credentials.");
18
- }
19
- }
20
-
21
- private function _getConfiguration($environmentVariable)
22
- {
23
- return Configuration::fromEnvironmentVariable(
24
- CloudinaryEnvironmentVariable::fromString($environmentVariable)
25
- );
26
- }
27
-
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/community/Cloudinary/Cloudinary/Helper/Console.php CHANGED
@@ -5,15 +5,12 @@ use CloudinaryExtension\Security\SignedConsoleUrl;
5
 
6
  class Cloudinary_Cloudinary_Helper_Console extends Mage_Core_Helper_Abstract
7
  {
8
-
9
  public function getMediaLibraryUrl()
10
  {
11
  $consoleUrl = ConsoleUrl::fromPath("media_library/cms");
12
  return (string)SignedConsoleUrl::fromConsoleUrlAndCredentials(
13
  $consoleUrl,
14
- Mage::helper('cloudinary_cloudinary/configuration')->buildCredentials()
15
  );
16
-
17
  }
18
-
19
  }
5
 
6
  class Cloudinary_Cloudinary_Helper_Console extends Mage_Core_Helper_Abstract
7
  {
 
8
  public function getMediaLibraryUrl()
9
  {
10
  $consoleUrl = ConsoleUrl::fromPath("media_library/cms");
11
  return (string)SignedConsoleUrl::fromConsoleUrlAndCredentials(
12
  $consoleUrl,
13
+ Mage::getModel('cloudinary_cloudinary/configuration')->getCredentials()
14
  );
 
15
  }
 
16
  }
app/code/community/Cloudinary/Cloudinary/Helper/Image.php CHANGED
@@ -1,32 +1,63 @@
1
  <?php
2
 
3
- use CloudinaryExtension\Cloud;
4
  use CloudinaryExtension\CloudinaryImageProvider;
 
5
  use CloudinaryExtension\Image;
 
6
  use CloudinaryExtension\Image\Transformation\Dimensions;
7
  use CloudinaryExtension\Image\Transformation\Crop;
 
 
8
 
9
  class Cloudinary_Cloudinary_Helper_Image extends Mage_Catalog_Helper_Image
10
  {
11
- use Cloudinary_Cloudinary_Model_PreConditionsValidator;
12
-
 
13
  private $_imageProvider;
 
 
 
 
14
  private $_dimensions;
 
 
 
 
15
  private $_attributeName;
 
 
 
 
16
  private $_configuration;
17
 
18
- public function init(Mage_Catalog_Model_Product $product, $attributeName, $imageFile = null)
19
- {
20
- if ($this->_isEnabled()) {
 
21
 
22
- $this->_configuration = $this->_getConfigHelper()->buildConfiguration();
 
 
 
23
 
24
- $this->_dimensions = Dimensions::null();
25
- $this->_attributeName = $attributeName;
 
 
 
 
 
 
 
 
 
26
 
27
- $this->_imageProvider = CloudinaryImageProvider::fromConfiguration(
28
- $this->_configuration
29
- );
 
 
30
  }
31
 
32
  return parent::init($product, $attributeName, $imageFile);
@@ -34,39 +65,36 @@ class Cloudinary_Cloudinary_Helper_Image extends Mage_Catalog_Helper_Image
34
 
35
  public function resize($width, $height = null)
36
  {
37
- if ($this->_imageShouldComeFromCloudinary($this->_getRequestedImageFile())) {
38
- $this->_dimensions = Dimensions::fromWidthAndHeight($width, $height);
39
- return $this;
40
- }
41
 
42
  return parent::resize($width, $height);
43
  }
44
 
45
- private function _getRequestedImageFile()
46
  {
47
- return $this->getImageFile() ?: $this->getProduct()->getData($this->_attributeName);
 
 
 
 
48
  }
49
 
50
  public function __toString()
51
  {
52
- $result = null;
53
- $imageFile = $this->_getRequestedImageFile();
54
-
55
- if ($this->_imageShouldComeFromCloudinary($imageFile)) {
56
- $image = Cloudinary_Cloudinary_Helper_Image::newApiImage($imageFile);
57
 
58
- $transformation = $this->createTransformation();
59
-
60
- $result = (string)$this->_imageProvider->transformImage($image, $transformation);
61
- } else {
62
- $result = parent::__toString();
63
- }
64
- return $result;
65
  }
66
 
67
- public static function newApiImage($path){
68
- $migratedPath = Cloudinary_Cloudinary_Helper_Configuration::getInstance()->getMigratedPath($path);
69
- return Image::fromPath($path, $migratedPath);
 
 
 
70
  }
71
 
72
  private function createTransformation()
@@ -74,11 +102,11 @@ class Cloudinary_Cloudinary_Helper_Image extends Mage_Catalog_Helper_Image
74
  if ($this->_getModel()->getKeepFrameState()) {
75
  return $this->_configuration->getDefaultTransformation()
76
  ->withDimensions(Dimensions::squareMissingDimension($this->_dimensions))
77
- ->withCrop(Crop::fromString('pad'));
78
  } else {
79
  return $this->_configuration->getDefaultTransformation()
80
  ->withDimensions($this->_dimensions)
81
- ->withCrop(Crop::fromString('fit'));
82
  }
83
  }
84
  }
1
  <?php
2
 
 
3
  use CloudinaryExtension\CloudinaryImageProvider;
4
+ use CloudinaryExtension\Configuration;
5
  use CloudinaryExtension\Image;
6
+ use CloudinaryExtension\Image\Transformation;
7
  use CloudinaryExtension\Image\Transformation\Dimensions;
8
  use CloudinaryExtension\Image\Transformation\Crop;
9
+ use CloudinaryExtension\UrlGenerator;
10
+ use CloudinaryExtension\Image\ImageFactory;
11
 
12
  class Cloudinary_Cloudinary_Helper_Image extends Mage_Catalog_Helper_Image
13
  {
14
+ /**
15
+ * @var CloudinaryImageProvider
16
+ */
17
  private $_imageProvider;
18
+
19
+ /**
20
+ * @var Dimensions
21
+ */
22
  private $_dimensions;
23
+
24
+ /**
25
+ * @var string
26
+ */
27
  private $_attributeName;
28
+
29
+ /**
30
+ * @var Configuration
31
+ */
32
  private $_configuration;
33
 
34
+ /**
35
+ * @var Cloudinary_Cloudinary_Helper_ImageFactory
36
+ */
37
+ private $_imageFactory;
38
 
39
+ /**
40
+ * @var UrlGenerator
41
+ */
42
+ private $_urlGenerator;
43
 
44
+ public function __construct()
45
+ {
46
+ $this->_configuration = Mage::getModel('cloudinary_cloudinary/configuration');
47
+ $this->_imageFactory = new ImageFactory(
48
+ $this->_configuration,
49
+ Mage::getModel('cloudinary_cloudinary/synchronizationChecker')
50
+ );
51
+ $this->_imageProvider = CloudinaryImageProvider::fromConfiguration($this->_configuration);
52
+ $this->_dimensions = Dimensions::null();
53
+ $this->_urlGenerator = new UrlGenerator($this->_configuration, $this->_imageProvider);
54
+ }
55
 
56
+ public function init(Mage_Catalog_Model_Product $product, $attributeName, $imageFile = null)
57
+ {
58
+ if ($this->_configuration->isEnabled()) {
59
+ $this->_attributeName = $attributeName;
60
+ $this->_dimensions = Dimensions::null();
61
  }
62
 
63
  return parent::init($product, $attributeName, $imageFile);
65
 
66
  public function resize($width, $height = null)
67
  {
68
+ $this->_dimensions = Dimensions::fromWidthAndHeight($width, $height);
 
 
 
69
 
70
  return parent::resize($width, $height);
71
  }
72
 
73
+ public function getImageUrlForCategory(Mage_Catalog_Model_Category $category)
74
  {
75
+ $imagePath = Mage::getBaseDir('media') . DS . 'catalog' . DS . 'category' . DS . $category->getImage();
76
+
77
+ $image = $this->_imageFactory->build($imagePath, array($category, 'getImageUrl'));
78
+
79
+ return $this->_urlGenerator->generateFor($image);
80
  }
81
 
82
  public function __toString()
83
  {
84
+ $image = $this->_imageFactory->build(
85
+ $this->_getRequestedImageFile(),
86
+ function() { return parent::__toString();}
87
+ );
 
88
 
89
+ return $this->_urlGenerator->generateFor($image, $this->createTransformation());
 
 
 
 
 
 
90
  }
91
 
92
+ /**
93
+ * @return string
94
+ */
95
+ private function _getRequestedImageFile()
96
+ {
97
+ return $this->getImageFile() ?: $this->getProduct()->getData($this->_attributeName);
98
  }
99
 
100
  private function createTransformation()
102
  if ($this->_getModel()->getKeepFrameState()) {
103
  return $this->_configuration->getDefaultTransformation()
104
  ->withDimensions(Dimensions::squareMissingDimension($this->_dimensions))
105
+ ->withCrop(Crop::pad());
106
  } else {
107
  return $this->_configuration->getDefaultTransformation()
108
  ->withDimensions($this->_dimensions)
109
+ ->withCrop(Crop::fit());
110
  }
111
  }
112
  }
app/code/community/Cloudinary/Cloudinary/Helper/Util/ArrayUtils.php DELETED
@@ -1,22 +0,0 @@
1
- <?php
2
-
3
- class Cloudinary_Cloudinary_Helper_Util_ArrayUtils
4
- {
5
-
6
- /**
7
- * Results with a subset of an associative array, preserving only the values that have a key that is present in $keys
8
- *
9
- * @param $array the original array we want to select from
10
- * @param $keys the keys to preserve in the input array
11
- * @return array
12
- */
13
- public static function arraySelect($array, $keys)
14
- {
15
- $result = [];
16
- foreach ($keys as $key) {
17
- $result[$key] = $array[$key];
18
- }
19
- return $result;
20
- }
21
- }
22
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/community/Cloudinary/Cloudinary/Model/Catalog/Product/Image.php CHANGED
@@ -2,23 +2,30 @@
2
 
3
  use CloudinaryExtension\CloudinaryImageProvider;
4
  use CloudinaryExtension\Image;
 
5
 
6
  class Cloudinary_Cloudinary_Model_Catalog_Product_Image extends Mage_Catalog_Model_Product_Image
7
  {
8
- use Cloudinary_Cloudinary_Model_PreConditionsValidator;
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  public function getUrl()
11
  {
12
- $config = $this->_getConfigHelper();
13
- $file = $this->_newFile;
14
- if ($this->_imageShouldComeFromCloudinary($file)) {
15
- $imageProvider = CloudinaryImageProvider::fromConfiguration($config->buildConfiguration());
16
- $result = (string)$imageProvider->transformImage(Cloudinary_Cloudinary_Helper_Image::newApiImage($file));
17
- } else {
18
- $result = parent::getUrl();
19
- }
20
- Cloudinary_Cloudinary_Model_Logger::getInstance()->debugLog($result);
21
- return $result;
22
  }
23
 
24
  public function getKeepFrameState()
2
 
3
  use CloudinaryExtension\CloudinaryImageProvider;
4
  use CloudinaryExtension\Image;
5
+ use CloudinaryExtension\Image\ImageFactory;
6
 
7
  class Cloudinary_Cloudinary_Model_Catalog_Product_Image extends Mage_Catalog_Model_Product_Image
8
  {
9
+ /**
10
+ * @var ImageFactory
11
+ */
12
+ private $_imageFactory;
13
+
14
+ public function __construct()
15
+ {
16
+ $this->_imageFactory = new ImageFactory(
17
+ Mage::getModel('cloudinary_cloudinary/configuration'),
18
+ Mage::getModel('cloudinary_cloudinary/synchronizationChecker')
19
+ );
20
+
21
+ return parent::__construct();
22
+ }
23
 
24
  public function getUrl()
25
  {
26
+ return (string) $this->_imageFactory->build(
27
+ $this->_newFile, function() { return parent::getUrl();}
28
+ );
 
 
 
 
 
 
 
29
  }
30
 
31
  public function getKeepFrameState()
app/code/community/Cloudinary/Cloudinary/Model/Catalog/Product/Media/Config.php CHANGED
@@ -3,34 +3,38 @@
3
  use CloudinaryExtension\Cloud;
4
  use CloudinaryExtension\CloudinaryImageProvider;
5
  use CloudinaryExtension\Image;
 
 
6
 
7
  class Cloudinary_Cloudinary_Model_Catalog_Product_Media_Config extends Mage_Catalog_Model_Product_Media_Config
8
  {
9
- use Cloudinary_Cloudinary_Model_PreConditionsValidator;
 
 
10
 
11
- public function getMediaUrl($file)
12
  {
13
- if ($this->_imageShouldComeFromCloudinary($file)) {
14
- return $this->_getUrlForImage($file);
15
- }
 
 
 
 
16
 
17
- return parent::getMediaUrl($file);
18
  }
19
 
20
- public function getTmpMediaUrl($file)
21
  {
22
- if ($this->_imageShouldComeFromCloudinary($file)) {
23
- return $this->_getUrlForImage($file);
24
- }
25
 
26
- return parent::getTmpMediaUrl($file);
27
  }
28
 
29
- private function _getUrlForImage($file)
30
  {
31
- $config = Cloudinary_Cloudinary_Helper_Configuration::getInstance();
32
- $imageProvider = CloudinaryImageProvider::fromConfiguration($config->buildConfiguration());
33
 
34
- return (string)$imageProvider->transformImage(Cloudinary_Cloudinary_Helper_Image::newApiImage($file));
35
  }
36
  }
3
  use CloudinaryExtension\Cloud;
4
  use CloudinaryExtension\CloudinaryImageProvider;
5
  use CloudinaryExtension\Image;
6
+ use CloudinaryExtension\Image\ImageFactory;
7
+ use CloudinaryExtension\UrlGenerator;
8
 
9
  class Cloudinary_Cloudinary_Model_Catalog_Product_Media_Config extends Mage_Catalog_Model_Product_Media_Config
10
  {
11
+ private $_configuration;
12
+ private $_imageProvider;
13
+ private $_urlGenerator;
14
 
15
+ public function __construct()
16
  {
17
+ $this->_configuration = Mage::getModel('cloudinary_cloudinary/configuration');
18
+ $this->_imageFactory = new ImageFactory(
19
+ $this->_configuration,
20
+ Mage::getModel('cloudinary_cloudinary/synchronizationChecker')
21
+ );
22
+ $this->_imageProvider = CloudinaryImageProvider::fromConfiguration($this->_configuration);
23
+ $this->_urlGenerator = new UrlGenerator($this->_configuration, $this->_imageProvider);
24
 
 
25
  }
26
 
27
+ public function getMediaUrl($file)
28
  {
29
+ $image = $this->_imageFactory->build($file, function() use($file) { return parent::getMediaUrl($file);});
 
 
30
 
31
+ return $this->_urlGenerator->generateFor($image);
32
  }
33
 
34
+ public function getTmpMediaUrl($file)
35
  {
36
+ $image = $this->_imageFactory->build($file, function() use($file) { return parent::getTmpMediaUrl($file);});
 
37
 
38
+ return $this->_urlGenerator->generateFor($image);
39
  }
40
  }
app/code/community/Cloudinary/Cloudinary/Model/Cms/Adminhtml/Template/Filter.php CHANGED
@@ -1,32 +1,68 @@
1
  <?php
2
 
 
3
  use CloudinaryExtension\Image;
 
 
4
 
5
- class Cloudinary_Cloudinary_Model_Cms_Adminhtml_Template_Filter
6
- extends Mage_Cms_Model_Adminhtml_Template_Filter
7
  {
8
- use Cloudinary_Cloudinary_Model_PreConditionsValidator;
 
 
 
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  public function mediaDirective($construction)
11
  {
12
- $directiveParams = $construction[2];
13
- $params = $this->_getIncludeParameters($directiveParams);
 
 
 
14
 
15
- if (!isset($params['url'])) {
16
- Mage::throwException('Undefined url parameter for media directive.');
17
  }
18
 
19
- $allowRemoteFileOpen = ini_get('allow_url_fopen');
20
-
21
- if ($this->_isEnabled() && $allowRemoteFileOpen) {
22
 
23
- $imagePath = $params['url'];
 
 
 
 
 
 
24
 
25
- if ($this->_imageShouldComeFromCloudinary($imagePath)) {
26
- return Mage::getModel('cloudinary_cloudinary/image')->getUrl($imagePath);
27
- }
28
  }
29
 
30
- return parent::mediaDirective($construction);
31
  }
32
- }
1
  <?php
2
 
3
+ use CloudinaryExtension\CloudinaryImageProvider;
4
  use CloudinaryExtension\Image;
5
+ use CloudinaryExtension\Image\ImageFactory;
6
+ use CloudinaryExtension\UrlGenerator;
7
 
8
+ class Cloudinary_Cloudinary_Model_Cms_Adminhtml_Template_Filter extends Mage_Cms_Model_Adminhtml_Template_Filter
 
9
  {
10
+ /**
11
+ * @var ImageFactory
12
+ */
13
+ private $imageFactory;
14
 
15
+ /**
16
+ * @var UrlGenerator
17
+ */
18
+ private $urlGenerator;
19
+
20
+ public function __construct()
21
+ {
22
+ $configuration = Mage::getModel('cloudinary_cloudinary/configuration');
23
+ $this->imageFactory = new ImageFactory(
24
+ $configuration,
25
+ Mage::getModel('cloudinary_cloudinary/synchronizationChecker')
26
+ );
27
+
28
+ $this->urlGenerator = new UrlGenerator(
29
+ $configuration,
30
+ CloudinaryImageProvider::fromConfiguration($configuration)
31
+ );
32
+
33
+ parent::__construct();
34
+ }
35
+
36
+ /**
37
+ * @param array $construction
38
+ * @return string
39
+ */
40
  public function mediaDirective($construction)
41
  {
42
+ if (ini_get('allow_url_fopen')) {
43
+ $image = $this->imageFactory->build(
44
+ $this->imagePath($construction),
45
+ function() use($construction) { return parent::mediaDirective($construction); }
46
+ );
47
 
48
+ return $this->urlGenerator->generateFor($image);
 
49
  }
50
 
51
+ return parent::mediaDirective($construction);
52
+ }
 
53
 
54
+ /**
55
+ * @param array $construction
56
+ * @return string
57
+ */
58
+ protected function imagePath(array $construction)
59
+ {
60
+ $params = $this->_getIncludeParameters($construction[2]);
61
 
62
+ if (!isset($params['url'])) {
63
+ Mage::throwException('Undefined url parameter for media directive.');
 
64
  }
65
 
66
+ return $params['url'];
67
  }
68
+ }
app/code/community/Cloudinary/Cloudinary/Model/Cms/Synchronisation.php CHANGED
@@ -2,7 +2,9 @@
2
 
3
  use CloudinaryExtension\Image\Synchronizable;
4
 
5
- class Cloudinary_Cloudinary_Model_Cms_Synchronisation extends Mage_Core_Model_Abstract implements Synchronizable
 
 
6
  {
7
 
8
  protected function _construct()
@@ -15,15 +17,8 @@ class Cloudinary_Cloudinary_Model_Cms_Synchronisation extends Mage_Core_Model_Ab
15
  return $this->getData('filename');
16
  }
17
 
18
- public function setValue($fileName)
19
- {
20
- $this->setData('basename', basename($fileName));
21
- return $this;
22
- }
23
-
24
  public function getRelativePath(){
25
- $helperConfig = Mage::helper('cloudinary_cloudinary/configuration');
26
- return $helperConfig->getMigratedPath($this->getFilename());
27
  }
28
 
29
  public function tagAsSynchronized()
@@ -31,8 +26,7 @@ class Cloudinary_Cloudinary_Model_Cms_Synchronisation extends Mage_Core_Model_Ab
31
  $this->setData('media_gallery_id', null);
32
  $this->setData('cloudinary_synchronisation_id', null);
33
  $this->setData('image_name', $this->getRelativePath());
34
- Cloudinary_Cloudinary_Model_Logger::getInstance()->debugLog( json_encode($this->toArray(), JSON_PRETTY_PRINT));
35
  $this->save();
36
  }
37
-
38
  }
2
 
3
  use CloudinaryExtension\Image\Synchronizable;
4
 
5
+ class Cloudinary_Cloudinary_Model_Cms_Synchronisation
6
+ extends Mage_Core_Model_Abstract
7
+ implements Synchronizable
8
  {
9
 
10
  protected function _construct()
17
  return $this->getData('filename');
18
  }
19
 
 
 
 
 
 
 
20
  public function getRelativePath(){
21
+ return Mage::getModel('cloudinary_cloudinary/configuration')->getMigratedPath($this->getFilename());
 
22
  }
23
 
24
  public function tagAsSynchronized()
26
  $this->setData('media_gallery_id', null);
27
  $this->setData('cloudinary_synchronisation_id', null);
28
  $this->setData('image_name', $this->getRelativePath());
29
+
30
  $this->save();
31
  }
 
32
  }
app/code/community/Cloudinary/Cloudinary/Model/Cms/Template/Filter.php CHANGED
@@ -1,27 +1,46 @@
1
  <?php
2
 
 
3
  use CloudinaryExtension\Image;
 
 
4
 
5
  class Cloudinary_Cloudinary_Model_Cms_Template_Filter extends Mage_Widget_Model_Template_Filter
6
  {
7
- use Cloudinary_Cloudinary_Model_PreConditionsValidator;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  public function mediaDirective($construction)
10
  {
11
- if ($this->_isEnabled()) {
12
- $imagePath = $this->_getImagePath($construction[2]);
13
-
14
- if ($this->_imageShouldComeFromCloudinary($imagePath)) {
15
- return Mage::getModel('cloudinary_cloudinary/image')->getUrl($imagePath);
16
- }
17
- }
18
- return parent::mediaDirective($construction);
19
  }
20
 
21
- private function _getImagePath($directiveParams)
22
  {
23
  $params = $this->_getIncludeParameters($directiveParams);
24
  return $params['url'];
25
  }
26
-
27
- }
1
  <?php
2
 
3
+ use CloudinaryExtension\CloudinaryImageProvider;
4
  use CloudinaryExtension\Image;
5
+ use CloudinaryExtension\Image\ImageFactory;
6
+ use CloudinaryExtension\UrlGenerator;
7
 
8
  class Cloudinary_Cloudinary_Model_Cms_Template_Filter extends Mage_Widget_Model_Template_Filter
9
  {
10
+ private $imageFactory;
11
+ private $urlGenerator;
12
+
13
+ public function __construct()
14
+ {
15
+ $configuration = Mage::getModel('cloudinary_cloudinary/configuration');
16
+ $this->imageFactory = new ImageFactory(
17
+ $configuration,
18
+ Mage::getModel('cloudinary_cloudinary/synchronizationChecker')
19
+ );
20
+
21
+ $this->urlGenerator = new UrlGenerator(
22
+ $configuration,
23
+ CloudinaryImageProvider::fromConfiguration($configuration)
24
+ );
25
+
26
+ parent::__construct();
27
+ }
28
 
29
  public function mediaDirective($construction)
30
  {
31
+ $imagePath = $this->getImagePath($construction[2]);
32
+
33
+ $image = $this->imageFactory->build(
34
+ $imagePath,
35
+ function() use($construction) { return parent::mediaDirective($construction);}
36
+ );
37
+
38
+ return $this->urlGenerator->generateFor($image);
39
  }
40
 
41
+ private function getImagePath($directiveParams)
42
  {
43
  $params = $this->_getIncludeParameters($directiveParams);
44
  return $params['url'];
45
  }
46
+ }
 
app/code/community/Cloudinary/Cloudinary/Model/Cms/Uploader.php CHANGED
@@ -5,61 +5,31 @@ use CloudinaryExtension\Image;
5
 
6
  class Cloudinary_Cloudinary_Model_Cms_Uploader extends Mage_Core_Model_File_Uploader
7
  {
8
- use Cloudinary_Cloudinary_Model_PreConditionsValidator;
9
-
10
- private $requiredParams = ['path', 'file', 'type'];
11
-
12
  protected function _afterSave($result)
13
  {
14
  parent::_afterSave($result);
15
 
16
- if ($this->shouldUpload($result)) {
17
- $this->upload($result);
18
- }
19
 
20
- return $this;
21
- }
22
 
23
- private function upload($result)
24
- {
25
- $imageProvider = CloudinaryImageProvider::fromConfiguration($this->_getConfigHelper()->buildConfiguration());
26
- $imageProvider->upload(Image::fromPath($result['path'] . DIRECTORY_SEPARATOR . $result['file']));
27
- Mage::getModel('cloudinary_cloudinary/cms_synchronisation')->setValue($result['file'])->tagAsSynchronized();
28
- }
29
 
30
- /**
31
- * @param array $result
32
- *
33
- * @return boolean
34
- */
35
- private function shouldUpload($result)
36
- {
37
- return $this->hasRequiredParams($result) && $this->isImage($result);
38
- }
39
 
40
- /**
41
- * @param array $result
42
- *
43
- * @return boolean
44
- */
45
- private function hasRequiredParams($result)
46
- {
47
- foreach ($this->requiredParams as $requiredParam) {
48
- if (empty($result[$requiredParam])) {
49
- return false;
50
- }
51
  }
52
 
53
- return true;
54
  }
55
 
56
- /**
57
- * @param array $result
58
- *
59
- * @return boolean
60
- */
61
- private function isImage($result)
62
  {
63
- return strpos($result['type'], 'image') !== false;
 
 
64
  }
65
  }
5
 
6
  class Cloudinary_Cloudinary_Model_Cms_Uploader extends Mage_Core_Model_File_Uploader
7
  {
 
 
 
 
8
  protected function _afterSave($result)
9
  {
10
  parent::_afterSave($result);
11
 
12
+ $configuration = Mage::getModel('cloudinary_cloudinary/configuration');
 
 
13
 
14
+ if ($configuration->isEnabled() && !empty($result['path']) && !empty($result['file'])) {
15
+ $imageProvider = CloudinaryImageProvider::fromConfiguration($configuration);
16
 
17
+ $fullPath = rtrim($result['path'], '/') . DIRECTORY_SEPARATOR . $result['file'];
18
+ $relativePath = $configuration->isFolderedMigration() ? $configuration->getMigratedPath($fullPath) : '';
 
 
 
 
19
 
20
+ $image = Image::fromPath($fullPath, $relativePath);
21
+ $imageProvider->upload($image);
 
 
 
 
 
 
 
22
 
23
+ $this->_trackSynchronisation((string)$image);
 
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
+ return $this;
27
  }
28
 
29
+ private function _trackSynchronisation($fileName)
 
 
 
 
 
30
  {
31
+ Mage::getModel('cloudinary_cloudinary/cms_synchronisation')
32
+ ->setFilename($fileName)
33
+ ->tagAsSynchronized();
34
  }
35
  }
app/code/community/Cloudinary/Cloudinary/Model/Cms/Wysiwyg/Images/Storage.php CHANGED
@@ -1,46 +1,76 @@
1
  <?php
2
 
3
- use CloudinaryExtension\CloudinaryImageProvider;
4
  use CloudinaryExtension\Image;
5
  use CloudinaryExtension\Image\Transformation;
6
  use CloudinaryExtension\Image\Transformation\Dimensions;
 
 
 
 
7
 
8
  class Cloudinary_Cloudinary_Model_Cms_Wysiwyg_Images_Storage extends Mage_Cms_Model_Wysiwyg_Images_Storage
9
  {
10
- use Cloudinary_Cloudinary_Model_PreConditionsValidator;
 
 
 
11
 
12
- public function getThumbnailUrl($filePath, $checkFile = false)
13
- {
14
- if ($this->_imageShouldComeFromCloudinary($filePath)) {
15
- $imageProvider = $this->_buildImageProvider();
16
- $imageDimensions = $this->_buildImageDimensions();
17
- $defaultTransformation = $this->_getConfigHelper()->buildConfiguration()->getDefaultTransformation();
18
 
19
- return (string)$imageProvider->transformImage(
20
- Image::fromPath($filePath),
21
- $defaultTransformation->withDimensions($imageDimensions)
22
- );
23
- }
24
- return parent::getThumbnailUrl($filePath, $checkFile);
25
- }
26
 
27
- private function _buildImageProvider()
28
  {
29
- return CloudinaryImageProvider::fromConfiguration($this->_getConfigHelper()->buildConfiguration());
 
 
 
 
 
 
 
 
 
 
30
  }
31
 
32
- private function _buildImageDimensions()
 
 
 
 
 
33
  {
34
- return Dimensions::fromWidthAndHeight(
35
- $this->getConfigData('resize_width'),
36
- $this->getConfigData('resize_height')
 
 
 
 
 
 
 
 
 
 
37
  );
38
  }
39
 
 
 
 
 
 
40
  public function uploadFile($targetPath, $type = null)
41
  {
42
-
43
- if(!$this->_getConfigHelper()->isEnabled()) {
44
  return parent::uploadFile($targetPath, $type);
45
  }
46
 
@@ -53,7 +83,7 @@ class Cloudinary_Cloudinary_Model_Cms_Wysiwyg_Images_Storage extends Mage_Cms_Mo
53
  $result = $uploader->save($targetPath);
54
 
55
  if (!$result) {
56
- Mage::throwException( Mage::helper('cms')->__('Cannot upload file.') );
57
  }
58
 
59
  // create thumbnail
@@ -69,5 +99,4 @@ class Cloudinary_Cloudinary_Model_Cms_Wysiwyg_Images_Storage extends Mage_Cms_Mo
69
 
70
  return $result;
71
  }
72
-
73
- }
1
  <?php
2
 
 
3
  use CloudinaryExtension\Image;
4
  use CloudinaryExtension\Image\Transformation;
5
  use CloudinaryExtension\Image\Transformation\Dimensions;
6
+ use CloudinaryExtension\Image\ImageFactory;
7
+ use CloudinaryExtension\UrlGenerator;
8
+ use CloudinaryExtension\CloudinaryImageProvider;
9
+ use CloudinaryExtension\ConfigurationInterface;
10
 
11
  class Cloudinary_Cloudinary_Model_Cms_Wysiwyg_Images_Storage extends Mage_Cms_Model_Wysiwyg_Images_Storage
12
  {
13
+ /**
14
+ * @var ImageFactory
15
+ */
16
+ private $_imageFactory;
17
 
18
+ /**
19
+ * @var UrlGenerator
20
+ */
21
+ private $_urlGenerator;
 
 
22
 
23
+ /**
24
+ * @var ConfigurationInterface
25
+ */
26
+ private $_configuration;
 
 
 
27
 
28
+ public function __construct()
29
  {
30
+ $this->_configuration = Mage::getModel('cloudinary_cloudinary/configuration');
31
+
32
+ $this->_imageFactory = new ImageFactory(
33
+ $this->_configuration,
34
+ Mage::getModel('cloudinary_cloudinary/synchronizationChecker')
35
+ );
36
+
37
+ $this->_urlGenerator = new UrlGenerator(
38
+ $this->_configuration,
39
+ CloudinaryImageProvider::fromConfiguration($this->_configuration)
40
+ );
41
  }
42
 
43
+ /**
44
+ * @param string $filePath
45
+ * @param bool $checkFile
46
+ * @return string
47
+ */
48
+ public function getThumbnailUrl($filePath, $checkFile = false)
49
  {
50
+ $image = $this->_imageFactory->build(
51
+ $filePath,
52
+ function() use($filePath, $checkFile) {
53
+ return parent::getThumbnailUrl($filePath, $checkFile);
54
+ }
55
+ );
56
+
57
+ return $this->_urlGenerator->generateWithDimensions(
58
+ $image,
59
+ Dimensions::fromWidthAndHeight(
60
+ $this->getConfigData('resize_width'),
61
+ $this->getConfigData('resize_height')
62
+ )
63
  );
64
  }
65
 
66
+ /**
67
+ * @param string $targetPath
68
+ * @param null|string $type
69
+ * @return array
70
+ */
71
  public function uploadFile($targetPath, $type = null)
72
  {
73
+ if (!$this->_configuration->isEnabled()) {
 
74
  return parent::uploadFile($targetPath, $type);
75
  }
76
 
83
  $result = $uploader->save($targetPath);
84
 
85
  if (!$result) {
86
+ Mage::throwException(Mage::helper('cms')->__('Cannot upload file.'));
87
  }
88
 
89
  // create thumbnail
99
 
100
  return $result;
101
  }
102
+ }
 
app/code/community/Cloudinary/Cloudinary/Model/Configuration.php ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ use CloudinaryExtension\Cloud;
3
+ use CloudinaryExtension\ConfigurationBuilder;
4
+ use CloudinaryExtension\ConfigurationInterface;
5
+ use CloudinaryExtension\Credentials;
6
+ use CloudinaryExtension\Image\Transformation;
7
+ use CloudinaryExtension\Image\Transformation\Dpr;
8
+ use CloudinaryExtension\Image\Transformation\FetchFormat;
9
+ use CloudinaryExtension\Image\Transformation\Gravity;
10
+ use CloudinaryExtension\Image\Transformation\Quality;
11
+ use CloudinaryExtension\Security\CloudinaryEnvironmentVariable;
12
+ use CloudinaryExtension\UploadConfig;
13
+
14
+ class Cloudinary_Cloudinary_Model_Configuration implements ConfigurationInterface
15
+ {
16
+ const CONFIG_PATH_ENABLED = 'cloudinary/cloud/cloudinary_enabled';
17
+ const STATUS_ENABLED = 1;
18
+ const STATUS_DISABLED = 0;
19
+ const USER_PLATFORM_TEMPLATE = 'CloudinaryMagento/%s (Magento %s)';
20
+ const CONFIG_PATH_ENVIRONMENT_VARIABLE = 'cloudinary/setup/cloudinary_environment_variable';
21
+ const CONFIG_SMART_SERVING = 'cloudinary/configuration/cloudinary_smart_serving';
22
+ const CONFIG_DEFAULT_GRAVITY = 'cloudinary/transformations/cloudinary_gravity';
23
+ const CONFIG_DEFAULT_QUALITY = 'cloudinary/transformations/cloudinary_image_quality';
24
+ const CONFIG_DEFAULT_DPR = 'cloudinary/transformations/cloudinary_image_dpr';
25
+ const CONFIG_DEFAULT_FETCH_FORMAT = 'cloudinary/transformations/cloudinary_fetch_format';
26
+ const CONFIG_CDN_SUBDOMAIN = 'cloudinary/configuration/cloudinary_cdn_subdomain';
27
+ const CONFIG_FOLDERED_MIGRATION = 'cloudinary/configuration/cloudinary_foldered_migration';
28
+
29
+ private $environmentVariable;
30
+
31
+ private $folderTranslator;
32
+
33
+ public function __construct()
34
+ {
35
+ $this->folderTranslator = Mage::getModel('cloudinary_cloudinary/magentoFolderTranslator');
36
+ }
37
+
38
+ /**
39
+ * @return Cloud
40
+ */
41
+ public function getCloud()
42
+ {
43
+ return $this->getEnvironmentVariable()->getCloud();
44
+ }
45
+
46
+ /**
47
+ * @return Credentials
48
+ */
49
+ public function getCredentials()
50
+ {
51
+ return $this->getEnvironmentVariable()->getCredentials();
52
+ }
53
+
54
+ /**
55
+ * @return Transformation
56
+ */
57
+ public function getDefaultTransformation()
58
+ {
59
+ $transformation = Transformation::builder()
60
+ ->withGravity(Gravity::fromString($this->getDefaultGravity()))
61
+ ->withFetchFormat(FetchFormat::fromString($this->getFetchFormat()))
62
+ ->withQuality(Quality::fromString($this->getImageQuality()))
63
+ ->withDpr(Dpr::fromString($this->getImageDpr()));
64
+
65
+ if ($this->isSmartServing()){
66
+ $transformation
67
+ ->addFlags(['lossy'])
68
+ ->withFetchFormat(FetchFormat::fromString(FetchFormat::FETCH_FORMAT_AUTO))
69
+ ->withoutFormat();
70
+ }
71
+ return $transformation;
72
+ }
73
+
74
+ /**
75
+ * @return boolean
76
+ */
77
+ public function getCdnSubdomainStatus()
78
+ {
79
+ return Mage::getStoreConfig(self::CONFIG_CDN_SUBDOMAIN);
80
+ }
81
+
82
+ /**
83
+ * @return string
84
+ */
85
+ public function getUserPlatform()
86
+ {
87
+ return sprintf(
88
+ self::USER_PLATFORM_TEMPLATE,
89
+ Mage::getConfig()->getModuleConfig('Cloudinary_Cloudinary')->version,
90
+ Mage::getVersion()
91
+ );
92
+ }
93
+
94
+ /**
95
+ * @return UploadConfig
96
+ */
97
+ public function getUploadConfig()
98
+ {
99
+ return UploadConfig::fromBooleanValues(true, false, false);
100
+ }
101
+
102
+ /**
103
+ * @return boolean
104
+ */
105
+ public function isEnabled()
106
+ {
107
+ return Mage::getStoreConfigFlag(self::CONFIG_PATH_ENABLED);
108
+ }
109
+
110
+ public function enable()
111
+ {
112
+ $this->setStoreConfig(self::CONFIG_PATH_ENABLED, self::STATUS_ENABLED);
113
+ }
114
+
115
+ public function disable()
116
+ {
117
+ $this->setStoreConfig(self::CONFIG_PATH_ENABLED, self::STATUS_DISABLED);
118
+ }
119
+
120
+ public function getFormatsToPreserve() {
121
+ return ['png', 'webp', 'gif', 'svg'];
122
+ }
123
+
124
+ public function validateCredentials()
125
+ {
126
+ try {
127
+ $api = new \Cloudinary\Api();
128
+ return $api->ping((new ConfigurationBuilder($this))->build());
129
+ } catch (Exception $e) {
130
+ Mage::logException($e);
131
+ }
132
+ return false;
133
+ }
134
+
135
+ public function getMigratedPath($file)
136
+ {
137
+ if ($this->isFolderedMigration()) {
138
+ $result = $this->folderTranslator->translate($file);
139
+ } else {
140
+ $result = basename($file);
141
+ }
142
+ return $result;
143
+ }
144
+
145
+ public function reverseMigratedPathIfNeeded($migratedPath)
146
+ {
147
+ if ($this->isFolderedMigration()) {
148
+ return $this->folderTranslator->reverse($migratedPath);
149
+ }
150
+ return $migratedPath;
151
+ }
152
+
153
+ public function isFolderedMigration()
154
+ {
155
+ return Mage::getStoreConfigFlag(self::CONFIG_FOLDERED_MIGRATION);
156
+ }
157
+
158
+ private function setStoreConfig($configPath, $value)
159
+ {
160
+ Mage::getModel('core/config')->saveConfig($configPath, $value)->reinit();
161
+ }
162
+
163
+ /**
164
+ * @return CloudinaryEnvironmentVariable
165
+ */
166
+ private function getEnvironmentVariable()
167
+ {
168
+ if (is_null($this->environmentVariable)) {
169
+ $value = Mage::helper('core')->decrypt(Mage::getStoreConfig(self::CONFIG_PATH_ENVIRONMENT_VARIABLE));
170
+ $this->environmentVariable = CloudinaryEnvironmentVariable::fromString($value);
171
+ }
172
+ return $this->environmentVariable;
173
+ }
174
+
175
+ /**
176
+ * Smart serving means lossy compression and automatic fetch format.
177
+ * @return bool
178
+ */
179
+ private function isSmartServing()
180
+ {
181
+ return Mage::getStoreConfigFlag(self::CONFIG_SMART_SERVING);
182
+ }
183
+
184
+ private function getDefaultGravity()
185
+ {
186
+ return Mage::getStoreConfig(self::CONFIG_DEFAULT_GRAVITY);
187
+ }
188
+
189
+ /**
190
+ * @return null|string
191
+ */
192
+ private function getFetchFormat()
193
+ {
194
+ if (Mage::getStoreConfigFlag(self::CONFIG_DEFAULT_FETCH_FORMAT)) {
195
+ return FetchFormat::FETCH_FORMAT_AUTO;
196
+ }
197
+ return null;
198
+ }
199
+
200
+ private function getImageQuality()
201
+ {
202
+ return Mage::getStoreConfig(self::CONFIG_DEFAULT_QUALITY);
203
+ }
204
+
205
+ private function getImageDpr()
206
+ {
207
+ return Mage::getStoreConfig(self::CONFIG_DEFAULT_DPR);
208
+ }
209
+ }
app/code/community/Cloudinary/Cloudinary/Model/Cron.php CHANGED
@@ -16,7 +16,9 @@ class Cloudinary_Cloudinary_Model_Cron extends Mage_Core_Model_Abstract
16
  ->load(Cloudinary_Cloudinary_Model_Migration::CLOUDINARY_MIGRATION_ID);
17
 
18
  $batchUploader = new BatchUploader(
19
- CloudinaryImageProvider::fromConfiguration(Mage::helper('cloudinary_cloudinary/configuration')->buildConfiguration()),
 
 
20
  $migrationTask,
21
  Mage::getModel('cloudinary_cloudinary/logger'),
22
  null
16
  ->load(Cloudinary_Cloudinary_Model_Migration::CLOUDINARY_MIGRATION_ID);
17
 
18
  $batchUploader = new BatchUploader(
19
+ CloudinaryImageProvider::fromConfiguration(
20
+ Mage::getModel('cloudinary_cloudinary/configuration')
21
+ ),
22
  $migrationTask,
23
  Mage::getModel('cloudinary_cloudinary/logger'),
24
  null
app/code/community/Cloudinary/Cloudinary/Model/Image.php CHANGED
@@ -2,22 +2,20 @@
2
 
3
  use CloudinaryExtension\CloudinaryImageProvider;
4
  use CloudinaryExtension\Image;
 
5
 
6
 
7
  class Cloudinary_Cloudinary_Model_Image extends Mage_Core_Model_Abstract
8
  {
9
- use Cloudinary_Cloudinary_Model_PreConditionsValidator;
10
-
11
- private $_folder;
12
-
13
  public function upload(array $imageDetails)
14
  {
15
- if ($this->_getConfigHelper()->isFolderedMigration()) {
16
- $this->_folder = $this->_getConfigHelper()->getMigratedPath($imageDetails['file']);
17
- }
 
 
18
 
19
- $imageManager = $this->_getImageProvider();
20
- $imageManager->upload(Image::fromPath($this->_imageFullPathFromImageDetails($imageDetails), $this->_folder));
21
 
22
  Mage::getModel('cloudinary_cloudinary/synchronisation')
23
  ->setValueId($imageDetails['value_id'])
@@ -33,7 +31,7 @@ class Cloudinary_Cloudinary_Model_Image extends Mage_Core_Model_Abstract
33
  private function _getImageDetailFromKey(array $imageDetails, $key)
34
  {
35
  if (!array_key_exists($key, $imageDetails)) {
36
- throw new Cloudinary_Cloudinary_Model_Exception_BadFilePathException("Invalid image data structure. Missing " . $key);
37
  }
38
  return $imageDetails[$key];
39
  }
@@ -42,21 +40,4 @@ class Cloudinary_Cloudinary_Model_Image extends Mage_Core_Model_Abstract
42
  {
43
  return Mage::getSingleton('catalog/product_media_config')->getBaseMediaPath();
44
  }
45
-
46
- public function deleteImage($imageName)
47
- {
48
- $this->_getImageProvider()->deleteImage(Cloudinary_Cloudinary_Helper_Image::newApiImage($imageName));
49
- }
50
-
51
- public function getUrl($imagePath)
52
- {
53
- $imageProvider = $this->_getImageProvider();
54
-
55
- return (string)$imageProvider->transformImage(Cloudinary_Cloudinary_Helper_Image::newApiImage($imagePath));
56
- }
57
-
58
- private function _getImageProvider()
59
- {
60
- return CloudinaryImageProvider::fromConfiguration($this->_getConfigHelper()->buildConfiguration());
61
- }
62
  }
2
 
3
  use CloudinaryExtension\CloudinaryImageProvider;
4
  use CloudinaryExtension\Image;
5
+ use Cloudinary_Cloudinary_Model_Exception_BadFilePathException as BadFilePathException;
6
 
7
 
8
  class Cloudinary_Cloudinary_Model_Image extends Mage_Core_Model_Abstract
9
  {
 
 
 
 
10
  public function upload(array $imageDetails)
11
  {
12
+ $configuration = Mage::getModel('cloudinary_cloudinary/configuration');
13
+ $imageManager = CloudinaryImageProvider::fromConfiguration($configuration);
14
+
15
+ $fullPath = $this->_imageFullPathFromImageDetails($imageDetails);
16
+ $relativePath = $configuration->isFolderedMigration() ? $configuration->getMigratedPath($fullPath) : '';
17
 
18
+ $imageManager->upload(Image::fromPath($fullPath, $relativePath));
 
19
 
20
  Mage::getModel('cloudinary_cloudinary/synchronisation')
21
  ->setValueId($imageDetails['value_id'])
31
  private function _getImageDetailFromKey(array $imageDetails, $key)
32
  {
33
  if (!array_key_exists($key, $imageDetails)) {
34
+ throw new BadFilePathException("Invalid image data structure. Missing " . $key);
35
  }
36
  return $imageDetails[$key];
37
  }
40
  {
41
  return Mage::getSingleton('catalog/product_media_config')->getBaseMediaPath();
42
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  }
app/code/community/Cloudinary/Cloudinary/Model/Logger.php CHANGED
@@ -28,7 +28,6 @@ class Cloudinary_Cloudinary_Model_Logger extends Mage_Core_Model_Abstract implem
28
 
29
  /**
30
  * Add extra information to a log entry: class and funcion name from which the log is called
31
- * @param $message
32
  * @return string
33
  */
34
  public static function getSignature()
@@ -37,12 +36,4 @@ class Cloudinary_Cloudinary_Model_Logger extends Mage_Core_Model_Abstract implem
37
  $logSignature = sprintf(self::SIGNATURE_TEMPLATE, $parentTrace['class'], $parentTrace['function']);
38
  return $logSignature;
39
  }
40
-
41
- /**
42
- * @return Cloudinary_Cloudinary_Model_Logger
43
- */
44
- public static function getInstance()
45
- {
46
- return Mage::getModel('cloudinary_cloudinary/logger');
47
- }
48
  }
28
 
29
  /**
30
  * Add extra information to a log entry: class and funcion name from which the log is called
 
31
  * @return string
32
  */
33
  public static function getSignature()
36
  $logSignature = sprintf(self::SIGNATURE_TEMPLATE, $parentTrace['class'], $parentTrace['function']);
37
  return $logSignature;
38
  }
 
 
 
 
 
 
 
 
39
  }
app/code/community/Cloudinary/Cloudinary/Model/MagentoFolderTranslator.php CHANGED
@@ -27,7 +27,6 @@ class Cloudinary_Cloudinary_Model_MagentoFolderTranslator implements \Cloudinary
27
  {
28
  $baseName = basename($path);
29
  $result = $this->unifiedDirName($path);
30
- $debug = $result;
31
 
32
  $baseDir = Mage::getBaseDir();
33
 
@@ -50,7 +49,7 @@ class Cloudinary_Cloudinary_Model_MagentoFolderTranslator implements \Cloudinary
50
  $result = $this->mediaDir . $result;
51
  }
52
  $result .= $baseName;
53
- Cloudinary_Cloudinary_Model_Logger::getInstance()->debugLog("$path => $debug => $result");
54
  return $result;
55
  }
56
 
27
  {
28
  $baseName = basename($path);
29
  $result = $this->unifiedDirName($path);
 
30
 
31
  $baseDir = Mage::getBaseDir();
32
 
49
  $result = $this->mediaDir . $result;
50
  }
51
  $result .= $baseName;
52
+
53
  return $result;
54
  }
55
 
app/code/community/Cloudinary/Cloudinary/Model/MediaCollectionCounter.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
-
3
- class Cloudinary_Cloudinary_Model_MediaCollectionCounter implements Countable
4
- {
5
-
6
- private $_collections = array();
7
-
8
- public function addCollection(Cloudinary_Cloudinary_Model_Resource_Media_Collection_Interface $collection)
9
- {
10
- $this->_collections[] = $collection;
11
-
12
- return $this;
13
- }
14
-
15
- public function count()
16
- {
17
- $mediaCount = 0;
18
- foreach ($this->_collections as $collection) {
19
- $mediaCount += $collection->getSize();
20
- }
21
- return $mediaCount;
22
- }
23
-
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/community/Cloudinary/Cloudinary/Model/Observer.php CHANGED
@@ -1,69 +1,108 @@
1
  <?php
2
 
 
 
 
 
 
 
 
 
 
3
  class Cloudinary_Cloudinary_Model_Observer extends Mage_Core_Model_Abstract
4
  {
5
-
6
  const CLOUDINARY_CONFIG_SECTION = 'cloudinary';
7
-
8
- public function loadCustomAutoloaders(Varien_Event_Observer $event)
 
 
 
 
 
 
9
  {
10
  Mage::helper('cloudinary_cloudinary/autoloader')->register();
11
 
12
  return $event;
13
  }
14
 
15
- public function uploadImagesToCloudinary(Varien_Event_Observer $event)
 
 
 
16
  {
17
- if (Mage::helper('cloudinary_cloudinary/configuration')->isEnabled()) {
18
  $cloudinaryImage = Mage::getModel('cloudinary_cloudinary/image');
19
 
20
- foreach ($this->_getImagesToUpload($event->getProduct()) as $image) {
21
  $cloudinaryImage->upload($image);
22
  }
23
  }
24
  }
25
 
26
- public function validateCloudinaryCredentials(Varien_Event_Observer $observer)
 
 
 
27
  {
28
- $configObject = $observer->getEvent()->getObject();
29
- if ($this->_isNotCloudinaryConfigurationSection($configObject)) {
30
- return;
31
- }
32
-
33
- try {
34
- $this->_validateEnvironmentVariableFromConfigObject($configObject);
35
- } catch (Exception $e) {
36
- $this->_addErrorMessageToAdminSession($e);
37
- $this->_logException($e);
38
  }
39
  }
40
 
41
- private function _getImagesToUpload(Mage_Catalog_Model_Product $product)
 
 
 
42
  {
43
- return Mage::getModel('cloudinary_cloudinary/catalog_product_media')->newImagesForProduct($product);
44
- }
45
 
46
- public function deleteImagesFromCloudinary(Varien_Event_Observer $event)
47
- {
48
- $cloudinaryImage = Mage::getModel('cloudinary_cloudinary/image');
49
 
50
- foreach ($this->_getImagesToDelete($event->getProduct()) as $image) {
51
- $cloudinaryImage->deleteImage($image['file']);
 
 
52
  }
 
 
 
 
 
 
 
 
 
 
53
  }
54
 
55
- private function _getImagesToDelete(Mage_Catalog_Model_Product $product)
 
 
 
 
 
56
  {
57
  $productMedia = Mage::getModel('cloudinary_cloudinary/catalog_product_media');
58
  return $productMedia->removedImagesForProduct($product);
59
  }
60
 
61
- private function _flattenConfigData(Mage_Adminhtml_Model_Config_Data $configObject)
 
 
 
 
 
62
  {
63
  $configData = array();
64
  $groups = $configObject->getGroups();
65
 
66
- if ($this->_containsSetup($groups)) {
67
  $configData = array_map(
68
  function($field) {
69
  return $field['value'];
@@ -73,34 +112,4 @@ class Cloudinary_Cloudinary_Model_Observer extends Mage_Core_Model_Abstract
73
  }
74
  return $configData;
75
  }
76
-
77
- private function _isNotCloudinaryConfigurationSection(Mage_Adminhtml_Model_Config_Data $configObject)
78
- {
79
- return $configObject->getSection() != self::CLOUDINARY_CONFIG_SECTION;
80
- }
81
-
82
- private function _validateEnvironmentVariableFromConfigObject(Mage_Adminhtml_Model_Config_Data $configObject)
83
- {
84
- $configData = $this->_flattenConfigData($configObject);
85
- $cloudinaryConfiguration = Mage::helper('cloudinary_cloudinary/configuration_validation');
86
-
87
- $cloudinaryConfiguration->validateEnvironmentVariable(
88
- $configData['cloudinary_environment_variable']
89
- );
90
- }
91
-
92
- private function _addErrorMessageToAdminSession($e)
93
- {
94
- Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
95
- }
96
-
97
- private function _logException($e)
98
- {
99
- Mage::logException($e);
100
- }
101
-
102
- private function _containsSetup($groups)
103
- {
104
- return array_key_exists('setup', $groups);
105
- }
106
- }
1
  <?php
2
 
3
+ use CloudinaryExtension\CloudinaryImageProvider;
4
+ use CloudinaryExtension\CredentialValidator;
5
+ use CloudinaryExtension\Exception\InvalidCredentials;
6
+ use CloudinaryExtension\Image;
7
+ use CloudinaryExtension\Security\CloudinaryEnvironmentVariable;
8
+ use Mage_Adminhtml_Model_Config_Data as ConfigData;
9
+ use Mage_Catalog_Model_Product as Product;
10
+ use Varien_Event_Observer as EventObserver;
11
+
12
  class Cloudinary_Cloudinary_Model_Observer extends Mage_Core_Model_Abstract
13
  {
 
14
  const CLOUDINARY_CONFIG_SECTION = 'cloudinary';
15
+ const ERROR_WRONG_CREDENTIALS = 'There was a problem validating your Cloudinary credentials.';
16
+
17
+ /**
18
+ * @param EventObserver $event
19
+ *
20
+ * @return EventObserver
21
+ */
22
+ public function loadCustomAutoloaders(EventObserver $event)
23
  {
24
  Mage::helper('cloudinary_cloudinary/autoloader')->register();
25
 
26
  return $event;
27
  }
28
 
29
+ /**
30
+ * @param EventObserver $event
31
+ */
32
+ public function uploadImagesToCloudinary(EventObserver $event)
33
  {
34
+ if (Mage::getModel('cloudinary_cloudinary/configuration')->isEnabled()) {
35
  $cloudinaryImage = Mage::getModel('cloudinary_cloudinary/image');
36
 
37
+ foreach ($this->getImagesToUpload($event->getProduct()) as $image) {
38
  $cloudinaryImage->upload($image);
39
  }
40
  }
41
  }
42
 
43
+ /**
44
+ * @param EventObserver $event
45
+ */
46
+ public function deleteImagesFromCloudinary(EventObserver $event)
47
  {
48
+ $cloudinaryImagePovider = CloudinaryImageProvider::fromConfiguration(
49
+ Mage::getModel('cloudinary_cloudinary/configuration')
50
+ );
51
+ foreach ($this->getImagesToDelete($event->getProduct()) as $image) {
52
+ $cloudinaryImagePovider->delete(Image::fromPath($image['file']));
 
 
 
 
 
53
  }
54
  }
55
 
56
+ /**
57
+ * @param EventObserver $observer
58
+ */
59
+ public function validateCloudinaryCredentials(EventObserver $observer)
60
  {
61
+ $credentialValidator = new CredentialValidator();
 
62
 
63
+ $configObject = $observer->getEvent()->getObject();
64
+ if ($configObject->getSection() == self::CLOUDINARY_CONFIG_SECTION) {
65
+ $configData = $this->flattenConfigData($configObject);
66
 
67
+ $environmentVariable = CloudinaryEnvironmentVariable::fromString($configData['cloudinary_environment_variable']);
68
+ if (!$credentialValidator->validate($environmentVariable->getCredentials())) {
69
+ Mage::getSingleton('adminhtml/session')->addError(self::ERROR_WRONG_CREDENTIALS);
70
+ }
71
  }
72
+ }
73
+
74
+ /**
75
+ * @param Product $product
76
+ *
77
+ * @return array
78
+ */
79
+ private function getImagesToUpload(Product $product)
80
+ {
81
+ return Mage::getModel('cloudinary_cloudinary/catalog_product_media')->newImagesForProduct($product);
82
  }
83
 
84
+ /**
85
+ * @param Product $product
86
+ *
87
+ * @return array
88
+ */
89
+ private function getImagesToDelete(Product $product)
90
  {
91
  $productMedia = Mage::getModel('cloudinary_cloudinary/catalog_product_media');
92
  return $productMedia->removedImagesForProduct($product);
93
  }
94
 
95
+ /**
96
+ * @param ConfigData $configObject
97
+ *
98
+ * @return array
99
+ */
100
+ private function flattenConfigData(ConfigData $configObject)
101
  {
102
  $configData = array();
103
  $groups = $configObject->getGroups();
104
 
105
+ if (array_key_exists('setup', $groups)) {
106
  $configData = array_map(
107
  function($field) {
108
  return $field['value'];
112
  }
113
  return $configData;
114
  }
115
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/community/Cloudinary/Cloudinary/Model/PreConditionsValidator.php DELETED
@@ -1,29 +0,0 @@
1
- <?php
2
-
3
- trait Cloudinary_Cloudinary_Model_PreConditionsValidator
4
- {
5
- private function _isEnabled()
6
- {
7
- return $this->_getConfigHelper()->isEnabled();
8
- }
9
-
10
- private function _isImageInCloudinary($imageName)
11
- {
12
- return Mage::getSingleton('cloudinary_cloudinary/syncedImages')->isImageInCloudinary($imageName);
13
- }
14
-
15
- /**
16
- * @return Cloudinary_Cloudinary_Helper_Configuration
17
- */
18
- private function _getConfigHelper()
19
- {
20
- return Mage::helper('cloudinary_cloudinary/configuration');
21
- }
22
-
23
- private function _imageShouldComeFromCloudinary($file)
24
- {
25
- $relativePath = $this->_getConfigHelper()->getMigratedPath($file);
26
- $result = $this->_isEnabled() && $this->_isImageInCloudinary($relativePath);
27
- return $result;
28
- }
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/community/Cloudinary/Cloudinary/Model/Resource/Cms/Synchronisation/Collection.php CHANGED
@@ -15,6 +15,8 @@ class Cloudinary_Cloudinary_Model_Resource_Cms_Synchronisation_Collection
15
 
16
  public function __construct()
17
  {
 
 
18
  $this->addTargetDir(Mage::helper('cms/wysiwyg_images')->getStorageRoot());
19
  $this->setItemObjectClass('cloudinary_cloudinary/cms_synchronisation');
20
  $this->setFilesFilter(
@@ -38,38 +40,34 @@ class Cloudinary_Cloudinary_Model_Resource_Cms_Synchronisation_Collection
38
 
39
  public function findUnsynchronisedImages()
40
  {
41
- $helperConfig = Mage::helper('cloudinary_cloudinary/configuration');
42
  if ($helperConfig->isFolderedMigration()){
43
  $this->addFieldToFilter('filename', array('nin' => $this->_getSynchronisedImageNames()));
44
  } else {
45
  $this->addFieldToFilter('basename', array('nin' => $this->_getSynchronisedImageNames()));
46
  }
47
 
48
- Cloudinary_Cloudinary_Model_Logger::getInstance()->debugLog(json_encode($this->toArray(), JSON_PRETTY_PRINT));
49
  return $this->getItems();
50
  }
51
 
52
  private function _getSynchronisedImageNames()
53
  {
54
- $helperConfig = Cloudinary_Cloudinary_Helper_Configuration::getInstance();
55
  $result = array_map(
56
- function ($itemData) use ($helperConfig) {
57
  $imageName = $itemData['image_name'];
58
- return $helperConfig->reverseMigratedPathIfNeeded($imageName);
59
  },
60
  $this->_getSynchronisedImageData()
61
  );
62
- Cloudinary_Cloudinary_Model_Logger::getInstance()->debugLog(print_r($result, true));
63
  return $result;
64
  }
65
 
66
  private function _getSynchronisedImageData()
67
  {
68
- $result = Mage::getResourceModel('cloudinary_cloudinary/synchronisation_collection')
69
  ->addFieldToSelect('image_name')
70
  ->addFieldToFilter('media_gallery_id', array('null' => true))
71
  ->getData();
72
- return $result;
73
  }
74
-
75
  }
15
 
16
  public function __construct()
17
  {
18
+ $categoryImages = Mage::getBaseDir('media') . DS . 'catalog' . DS . 'category';
19
+ $this->addTargetDir($categoryImages);
20
  $this->addTargetDir(Mage::helper('cms/wysiwyg_images')->getStorageRoot());
21
  $this->setItemObjectClass('cloudinary_cloudinary/cms_synchronisation');
22
  $this->setFilesFilter(
40
 
41
  public function findUnsynchronisedImages()
42
  {
43
+ $helperConfig = Mage::getModel('cloudinary_cloudinary/configuration');
44
  if ($helperConfig->isFolderedMigration()){
45
  $this->addFieldToFilter('filename', array('nin' => $this->_getSynchronisedImageNames()));
46
  } else {
47
  $this->addFieldToFilter('basename', array('nin' => $this->_getSynchronisedImageNames()));
48
  }
49
 
 
50
  return $this->getItems();
51
  }
52
 
53
  private function _getSynchronisedImageNames()
54
  {
 
55
  $result = array_map(
56
+ function ($itemData) {
57
  $imageName = $itemData['image_name'];
58
+ return Mage::getModel('cloudinary_cloudinary/configuration')->reverseMigratedPathIfNeeded($imageName);
59
  },
60
  $this->_getSynchronisedImageData()
61
  );
62
+
63
  return $result;
64
  }
65
 
66
  private function _getSynchronisedImageData()
67
  {
68
+ return Mage::getResourceModel('cloudinary_cloudinary/synchronisation_collection')
69
  ->addFieldToSelect('image_name')
70
  ->addFieldToFilter('media_gallery_id', array('null' => true))
71
  ->getData();
 
72
  }
 
73
  }
app/code/community/Cloudinary/Cloudinary/Model/Resource/Media/Collection/Interface.php DELETED
@@ -1,8 +0,0 @@
1
- <?php
2
-
3
- interface Cloudinary_Cloudinary_Model_Resource_Media_Collection_Interface
4
- {
5
-
6
- public function getSize();
7
-
8
- }
 
 
 
 
 
 
 
 
app/code/community/Cloudinary/Cloudinary/Model/Resource/Synchronisation/Collection.php CHANGED
@@ -45,7 +45,6 @@ class Cloudinary_Cloudinary_Model_Resource_Synchronisation_Collection
45
  ->where("cloudinary_synchronisation_id is null and value not in ($syncedImagesQuery)")
46
  ->limit($limit);
47
 
48
- Cloudinary_Cloudinary_Model_Logger::getInstance()->debugLog(print_r($this->toArray(), true));
49
  return $this->getItems();
50
  }
51
 
45
  ->where("cloudinary_synchronisation_id is null and value not in ($syncedImagesQuery)")
46
  ->limit($limit);
47
 
 
48
  return $this->getItems();
49
  }
50
 
app/code/community/Cloudinary/Cloudinary/Model/SyncedImages.php DELETED
@@ -1,31 +0,0 @@
1
- <?php
2
-
3
- class Cloudinary_Cloudinary_Model_SyncedImages
4
- {
5
- protected $_syncronisation;
6
- protected $_syncedImages = [];
7
-
8
- public function __construct($arguments)
9
- {
10
- if(!isset($arguments['synchronisation'])) {
11
- $arguments['synchronisation'] = Mage::getModel('cloudinary_cloudinary/synchronisation');
12
- }
13
- $this->_syncronisation = $arguments['synchronisation'];
14
-
15
- }
16
-
17
- public function isImageInCloudinary($imageName)
18
- {
19
- if (!isset($this->_syncedImages[$imageName])) {
20
- $coll = $this->_syncronisation->getCollection();
21
- $table = $coll->getMainTable();
22
- // case sensitive check
23
-
24
- $query = "select 1 from $table where binary image_name = '$imageName' limit 1";
25
-
26
- $this->_syncedImages[$imageName] = ($coll->getConnection()->query($query)->fetchColumn() > 0);
27
- }
28
-
29
- return $this->_syncedImages[$imageName];
30
- }
31
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/community/Cloudinary/Cloudinary/Model/Synchronisation.php CHANGED
@@ -16,7 +16,7 @@ class Cloudinary_Cloudinary_Model_Synchronisation extends Mage_Core_Model_Abstra
16
  $this->setData('media_gallery_id', $this['value_id']);
17
  $this->setData('media_gallery_value', $this['value']);
18
  $this->unsetData('value_id');
19
- Cloudinary_Cloudinary_Model_Logger::getInstance()->debugLog( json_encode($this->toArray(), JSON_PRETTY_PRINT));
20
  $this->save();
21
  }
22
 
@@ -31,8 +31,8 @@ class Cloudinary_Cloudinary_Model_Synchronisation extends Mage_Core_Model_Abstra
31
 
32
  public function getRelativePath()
33
  {
34
- $helperConfig = Mage::helper('cloudinary_cloudinary/configuration');
35
- return $helperConfig->getMigratedPath($this->getFilename());
36
  }
37
 
38
  private function _baseMediaPath()
16
  $this->setData('media_gallery_id', $this['value_id']);
17
  $this->setData('media_gallery_value', $this['value']);
18
  $this->unsetData('value_id');
19
+
20
  $this->save();
21
  }
22
 
31
 
32
  public function getRelativePath()
33
  {
34
+ return Mage::getModel('cloudinary_cloudinary/configuration')
35
+ ->getMigratedPath($this->getFilename());
36
  }
37
 
38
  private function _baseMediaPath()
app/code/community/Cloudinary/Cloudinary/Model/SynchronizationChecker.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use CloudinaryExtension\Image\SynchronizationChecker as SynchronizationCheckerInterface;
4
+
5
+ class Cloudinary_Cloudinary_Model_SynchronizationChecker implements SynchronizationCheckerInterface
6
+ {
7
+ public function isSynchronized($imageName)
8
+ {
9
+ if (!$imageName) {
10
+ return false;
11
+ }
12
+ $coll = Mage::getModel('cloudinary_cloudinary/synchronisation')->getCollection();
13
+ $table = $coll->getMainTable();
14
+ // case sensitive check
15
+ $query = "select count(*) from $table where binary image_name = '$imageName' limit 1";
16
+ return $coll->getConnection()->query($query)->fetchColumn() > 0;
17
+ }
18
+ }
app/code/community/Cloudinary/Cloudinary/controllers/Adminhtml/CloudinaryController.php CHANGED
@@ -3,13 +3,15 @@
3
  class Cloudinary_Cloudinary_Adminhtml_CloudinaryController extends Mage_Adminhtml_Controller_Action
4
  {
5
  private $_migrationTask;
6
-
 
 
7
  private $_cloudinaryConfig;
8
 
9
  public function preDispatch()
10
  {
11
  $this->_migrationTask = Mage::getModel('cloudinary_cloudinary/migration')->load(Cloudinary_Cloudinary_Model_Migration::CLOUDINARY_MIGRATION_ID);
12
- $this->_cloudinaryConfig = Mage::helper('cloudinary_cloudinary/configuration');
13
 
14
  parent::preDispatch();
15
  }
@@ -18,6 +20,13 @@ class Cloudinary_Cloudinary_Adminhtml_CloudinaryController extends Mage_Adminhtm
18
  {
19
  $layout = $this->loadLayout();
20
 
 
 
 
 
 
 
 
21
  if ($this->_migrationTask->hasStarted()) {
22
  $layout->_addContent($this->_buildMetaRefreshBlock());
23
  }
@@ -25,6 +34,11 @@ class Cloudinary_Cloudinary_Adminhtml_CloudinaryController extends Mage_Adminhtm
25
  $this->renderLayout();
26
  }
27
 
 
 
 
 
 
28
  public function startMigrationAction()
29
  {
30
  $this->_migrationTask->start();
@@ -41,8 +55,11 @@ class Cloudinary_Cloudinary_Adminhtml_CloudinaryController extends Mage_Adminhtm
41
 
42
  public function enableCloudinaryAction()
43
  {
44
- $this->_cloudinaryConfig->enable();
45
-
 
 
 
46
  $this->_redirectToManageCloudinary();
47
  }
48
 
@@ -57,7 +74,7 @@ class Cloudinary_Cloudinary_Adminhtml_CloudinaryController extends Mage_Adminhtm
57
  {
58
  $items = Mage::getModel('cloudinary_cloudinary/migrationError')->getCollection()->getItems();
59
 
60
- foreach ($items as $error){
61
  $error->delete();
62
  }
63
 
3
  class Cloudinary_Cloudinary_Adminhtml_CloudinaryController extends Mage_Adminhtml_Controller_Action
4
  {
5
  private $_migrationTask;
6
+ /**
7
+ * @var Cloudinary_Cloudinary_Helper_Configuration
8
+ */
9
  private $_cloudinaryConfig;
10
 
11
  public function preDispatch()
12
  {
13
  $this->_migrationTask = Mage::getModel('cloudinary_cloudinary/migration')->load(Cloudinary_Cloudinary_Model_Migration::CLOUDINARY_MIGRATION_ID);
14
+ $this->_cloudinaryConfig = Mage::getModel('cloudinary_cloudinary/configuration');
15
 
16
  parent::preDispatch();
17
  }
20
  {
21
  $layout = $this->loadLayout();
22
 
23
+ if (!$this->_cloudinaryConfig->validateCredentials()) {
24
+ $link = '<a href="/admin/system_config/edit/section/cloudinary/">here</a>';
25
+ $this->_getSession()->addError(
26
+ "Please enter your Cloudinary Credentials $link to Activate Cloudinary"
27
+ );
28
+ }
29
+
30
  if ($this->_migrationTask->hasStarted()) {
31
  $layout->_addContent($this->_buildMetaRefreshBlock());
32
  }
34
  $this->renderLayout();
35
  }
36
 
37
+ public function configAction()
38
+ {
39
+ $this->_redirect("*/system_config/edit/section/cloudinary/");
40
+ }
41
+
42
  public function startMigrationAction()
43
  {
44
  $this->_migrationTask->start();
55
 
56
  public function enableCloudinaryAction()
57
  {
58
+ if (!$this->_cloudinaryConfig->validateCredentials()) {
59
+ $this->_getSession()->addError('Validating credentials failed. Cloudinary stays disabled');
60
+ } else {
61
+ $this->_cloudinaryConfig->enable();
62
+ }
63
  $this->_redirectToManageCloudinary();
64
  }
65
 
74
  {
75
  $items = Mage::getModel('cloudinary_cloudinary/migrationError')->getCollection()->getItems();
76
 
77
+ foreach ($items as $error) {
78
  $error->delete();
79
  }
80
 
app/code/community/Cloudinary/Cloudinary/etc/config.xml CHANGED
@@ -2,7 +2,7 @@
2
  <config>
3
  <modules>
4
  <Cloudinary_Cloudinary>
5
- <version>1.2.2</version>
6
  </Cloudinary_Cloudinary>
7
  </modules>
8
  <global>
2
  <config>
3
  <modules>
4
  <Cloudinary_Cloudinary>
5
+ <version>2.0.0</version>
6
  </Cloudinary_Cloudinary>
7
  </modules>
8
  <global>
lib/Cloudinary/Api.php CHANGED
@@ -81,6 +81,14 @@ class Api {
81
  $uri = array("resources", $resource_type, $type, $public_id);
82
  return $this->call_api("get", $uri, $this->only($options, array("exif", "colors", "faces", "image_metadata", "phash", "pages", "coordinates", "max_results")), $options);
83
  }
 
 
 
 
 
 
 
 
84
 
85
  function update($public_id, $options=array()) {
86
  $resource_type = \Cloudinary::option_get($options, "resource_type", "image");
@@ -108,7 +116,7 @@ class Api {
108
  $resource_type = \Cloudinary::option_get($options, "resource_type", "image");
109
  $type = \Cloudinary::option_get($options, "type", "upload");
110
  $uri = array("resources", $resource_type, $type);
111
- return $this->call_api("delete", $uri, array_merge(array("public_ids"=>$public_ids), $this->only($options, array("keep_original", "invalidate"))), $options);
112
  }
113
 
114
  function delete_resources_by_prefix($prefix, $options=array()) {
@@ -148,14 +156,18 @@ class Api {
148
 
149
  function transformation($transformation, $options=array()) {
150
  $uri = array("transformations", $this->transformation_string($transformation));
151
- return $this->call_api("get", $uri, $this->only($options, array("max_results")), $options);
152
  }
153
 
154
  function delete_transformation($transformation, $options=array()) {
155
  $uri = array("transformations", $this->transformation_string($transformation));
156
- return $this->call_api("delete", $uri, array(), $options);
 
 
 
 
157
  }
158
-
159
  # updates - currently only supported update is the "allowed_for_strict" boolean flag
160
  function update_transformation($transformation, $updates=array(), $options=array()) {
161
  $uri = array("transformations", $this->transformation_string($transformation));
@@ -203,6 +215,90 @@ class Api {
203
  function subfolders($of_folder_path, $options=array()) {
204
  return $this->call_api("get", array("folders", $of_folder_path), array(), $options);
205
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
  function call_api($method, $uri, $params, &$options) {
208
  $prefix = \Cloudinary::option_get($options, "upload_prefix", \Cloudinary::config_get("upload_prefix", "https://api.cloudinary.com"));
@@ -213,8 +309,32 @@ class Api {
213
  $api_secret = \Cloudinary::option_get($options, "api_secret", \Cloudinary::config_get("api_secret"));
214
  if (!$api_secret) throw new \InvalidArgumentException("Must supply api_secret");
215
  $api_url = implode("/", array_merge(array($prefix, "v1_1", $cloud_name), $uri));
216
- $api_url .= "?" . preg_replace("/%5B\d+%5D/", "%5B%5D", http_build_query($params));
217
- $ch = curl_init($api_url);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  curl_setopt($ch, CURLOPT_HEADER, 1);
219
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
220
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
@@ -224,7 +344,7 @@ class Api {
224
  curl_setopt($ch, CURLOPT_CAINFO,realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR."cacert.pem");
225
  curl_setopt($ch, CURLOPT_USERAGENT, \Cloudinary::userAgent());
226
  curl_setopt($ch, CURLOPT_PROXY, \Cloudinary::option_get($options, "api_proxy", \Cloudinary::config_get("api_proxy")));
227
- $response = $this->execute($ch);
228
  $curl_error = NULL;
229
  if(curl_errno($ch))
230
  {
@@ -298,6 +418,23 @@ class Api {
298
  protected function transformation_string($transformation) {
299
  return is_string($transformation) ? $transformation : \Cloudinary::generate_transformation_string($transformation);
300
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  }
302
 
303
  }
81
  $uri = array("resources", $resource_type, $type, $public_id);
82
  return $this->call_api("get", $uri, $this->only($options, array("exif", "colors", "faces", "image_metadata", "phash", "pages", "coordinates", "max_results")), $options);
83
  }
84
+
85
+ function restore($public_ids, $options=array()) {
86
+ $resource_type = \Cloudinary::option_get($options, "resource_type", "image");
87
+ $type = \Cloudinary::option_get($options, "type", "upload");
88
+ $uri = array("resources", $resource_type, $type, "restore");
89
+ $params = array_merge($options, array("public_ids" => $public_ids));
90
+ return $this->call_api("post", $uri, $params, $options);
91
+ }
92
 
93
  function update($public_id, $options=array()) {
94
  $resource_type = \Cloudinary::option_get($options, "resource_type", "image");
116
  $resource_type = \Cloudinary::option_get($options, "resource_type", "image");
117
  $type = \Cloudinary::option_get($options, "type", "upload");
118
  $uri = array("resources", $resource_type, $type);
119
+ return $this->call_api("delete", $uri, array_merge(array("public_ids"=>$public_ids), $this->only($options, array("keep_original", "invalidate", "transformation"))), $options);
120
  }
121
 
122
  function delete_resources_by_prefix($prefix, $options=array()) {
156
 
157
  function transformation($transformation, $options=array()) {
158
  $uri = array("transformations", $this->transformation_string($transformation));
159
+ return $this->call_api("get", $uri, $this->only($options, array("next_cursor", "max_results")), $options);
160
  }
161
 
162
  function delete_transformation($transformation, $options=array()) {
163
  $uri = array("transformations", $this->transformation_string($transformation));
164
+ $params = array();
165
+ if (isset($options["invalidate"])) {
166
+ $params["invalidate"] = $options["invalidate"];
167
+ }
168
+ return $this->call_api("delete", $uri, $params, $options);
169
  }
170
+
171
  # updates - currently only supported update is the "allowed_for_strict" boolean flag
172
  function update_transformation($transformation, $updates=array(), $options=array()) {
173
  $uri = array("transformations", $this->transformation_string($transformation));
215
  function subfolders($of_folder_path, $options=array()) {
216
  return $this->call_api("get", array("folders", $of_folder_path), array(), $options);
217
  }
218
+
219
+ function upload_mappings($options=array()) {
220
+ return $this->call_api("get", array("upload_mappings"), $this->only($options, array("next_cursor", "max_results")), $options);
221
+ }
222
+
223
+ function upload_mapping($name, $options=array()) {
224
+ $uri = array("upload_mappings");
225
+ $params = array("folder"=>$name);
226
+ return $this->call_api("get", $uri, $params, $options);
227
+ }
228
+
229
+ function delete_upload_mapping($name, $options=array()) {
230
+ $uri = array("upload_mappings");
231
+ $params = array("folder"=>$name);
232
+ return $this->call_api("delete", $uri, $params, $options);
233
+ }
234
+
235
+ function update_upload_mapping($name, $options=array()) {
236
+ $uri = array("upload_mappings");
237
+ $params = array("folder"=>$name);
238
+ return $this->call_api("put", $uri, array_merge($params, $this->only($options, array("template"))), $options);
239
+ }
240
+
241
+ function create_upload_mapping($name, $options=array()) {
242
+ $uri = array("upload_mappings");
243
+ $params = array("folder"=>$name);
244
+ return $this->call_api("post", $uri, array_merge($params, $this->only($options, array("template"))), $options);
245
+ }
246
+
247
+ /**
248
+ * List all streaming profiles associated with the current customer
249
+ * @param array $options options
250
+ * @return Api\Response An array with a "data" key for results
251
+ */
252
+ function list_streaming_profiles($options=array()) {
253
+ return $this->call_api("get", array("streaming_profiles"), array(), $options);
254
+ }
255
+
256
+ /**
257
+ * Get the information of a single streaming profile
258
+ * @param $name the name of the profile
259
+ * @param array $options other options
260
+ * @return Api\Response An array with a "data" key for results
261
+ */
262
+ function get_streaming_profile($name, $options=array()) {
263
+ $uri = array("streaming_profiles/" . $name);
264
+ return $this->call_api("get", $uri, array(), $options);
265
+ }
266
+
267
+ /**
268
+ * Delete a streaming profile information. Predefined profiles are restored to the default setting.
269
+ * @param $name the name of the streaming profile to delete
270
+ * @param array $options additional options
271
+ * @return Api\Response
272
+ */
273
+ function delete_streaming_profile($name, $options=array()) {
274
+ $uri = array("streaming_profiles/" . $name);
275
+ return $this->call_api("delete", $uri, array(), $options);
276
+ }
277
+
278
+ /**
279
+ * Update an existing streaming profile
280
+ * @param $name the name of the prodile
281
+ * @param array $options additional options
282
+ * @return Api\Response
283
+ */
284
+ function update_streaming_profile($name, $options=array()) {
285
+ $uri = array("streaming_profiles/" . $name);
286
+ $params = $this->prepare_streaming_profile_params($options);
287
+ return $this->call_api("put", $uri, $params, $options);
288
+ }
289
+
290
+ /**
291
+ * Create a new streaming profile
292
+ * @param $name the name of the new profile. if the name is of a predefined profile, the profile will be modified.
293
+ * @param array $options additional options
294
+ * @return Api\Response
295
+ */
296
+ function create_streaming_profile($name, $options = array()) {
297
+ $uri = array("streaming_profiles");
298
+ $params = $this->prepare_streaming_profile_params($options);
299
+ $params["name"] = $name;
300
+ return $this->call_api("post", $uri, $params, $options);
301
+ }
302
 
303
  function call_api($method, $uri, $params, &$options) {
304
  $prefix = \Cloudinary::option_get($options, "upload_prefix", \Cloudinary::config_get("upload_prefix", "https://api.cloudinary.com"));
309
  $api_secret = \Cloudinary::option_get($options, "api_secret", \Cloudinary::config_get("api_secret"));
310
  if (!$api_secret) throw new \InvalidArgumentException("Must supply api_secret");
311
  $api_url = implode("/", array_merge(array($prefix, "v1_1", $cloud_name), $uri));
312
+ $params = array_filter($params,function($v){ return !is_null($v) && ($v !== "" );});
313
+ if ($method == "get")
314
+ {
315
+ $api_url .= "?" . preg_replace("/%5B\d+%5D/", "%5B%5D", http_build_query($params));
316
+
317
+ }
318
+
319
+ $ch = curl_init($api_url);
320
+
321
+ if ($method != "get")
322
+ {
323
+ $post_params = array();
324
+ foreach ($params as $key => $value) {
325
+ if (is_array($value)) {
326
+ $i = 0;
327
+ foreach ($value as $item) {
328
+ $post_params[$key . "[$i]"] = $item;
329
+ $i++;
330
+ }
331
+ } else {
332
+ $post_params[$key] = $value;
333
+ }
334
+ }
335
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $post_params);
336
+
337
+ }
338
  curl_setopt($ch, CURLOPT_HEADER, 1);
339
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
340
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
344
  curl_setopt($ch, CURLOPT_CAINFO,realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR."cacert.pem");
345
  curl_setopt($ch, CURLOPT_USERAGENT, \Cloudinary::userAgent());
346
  curl_setopt($ch, CURLOPT_PROXY, \Cloudinary::option_get($options, "api_proxy", \Cloudinary::config_get("api_proxy")));
347
+ $response = $this->execute($ch);
348
  $curl_error = NULL;
349
  if(curl_errno($ch))
350
  {
418
  protected function transformation_string($transformation) {
419
  return is_string($transformation) ? $transformation : \Cloudinary::generate_transformation_string($transformation);
420
  }
421
+
422
+ /**
423
+ * Prepare streaming profile parameters for API calls
424
+ * @param $options the options passed to the API
425
+ * @return array A single profile parameters
426
+ */
427
+ protected function prepare_streaming_profile_params($options) {
428
+ $params = $this->only($options, array("display_name"));
429
+ if (isset($options['representations'])) {
430
+ $array_map = array_map(
431
+ function ($representation) {
432
+ return array("transformation" => \Cloudinary::generate_transformation_string($representation));
433
+ }, $options['representations']);
434
+ $params["representations"] = json_encode($array_map);
435
+ }
436
+ return $params;
437
+ }
438
  }
439
 
440
  }
lib/Cloudinary/AuthToken.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Cloudinary;
4
+
5
+
6
+ class AuthToken {
7
+
8
+ /**
9
+ * Generate an authorization token.
10
+ * Options:
11
+ * string key - the secret key required to sign the token
12
+ * string ip - the IP address of the client
13
+ * number start_time - the start time of the token in seconds from epoch
14
+ * string expiration - the expiration time of the token in seconds from epoch
15
+ * string duration - the duration of the token (from start_time)
16
+ * string acl - the ACL for the token
17
+ * string url - the URL to authentication in case of a URL token
18
+ *
19
+ * @param array $options token configuration
20
+ *
21
+ * @return string the authorization token
22
+ * @throws Error if both expiration and duration were not provided
23
+ */
24
+ public static function generate($options=array()){
25
+ $key = \Cloudinary::option_get($options, "key");
26
+ if(!isset($key)) throw new \Cloudinary\Error("Missing authentication token key configuration");
27
+ $name = \Cloudinary::option_get($options, "token_name", "__cld_token__");
28
+ $start = \Cloudinary::option_get($options, "start_time");
29
+ $expiration = \Cloudinary::option_get($options, "expiration");
30
+ $ip = \Cloudinary::option_get($options, "ip");
31
+ $acl = \Cloudinary::option_get($options, "acl");
32
+ $url = \Cloudinary::option_get($options, "url");
33
+ $duration = \Cloudinary::option_get($options, "duration");
34
+
35
+ if(!strcasecmp($start, "now")) {
36
+ $start = time();
37
+ } elseif (is_numeric($start)) {
38
+ $start = 0 + $start;
39
+ }
40
+ if(!isset($expiration)){
41
+ if(isset($duration)){
42
+ $expiration = (isset($start) ? $start : time()) + $duration;
43
+ } else {
44
+ throw new \Cloudinary\Error("Must provide 'expiration' or 'duration'.");
45
+ }
46
+ }
47
+ $token = array();
48
+ if(isset($ip)) array_push($token, "ip=$ip");
49
+ if(isset($start)) array_push($token, "st=$start");
50
+ array_push($token, "exp=$expiration");
51
+ if(isset($acl)) array_push($token, "acl=" . self::escape_to_lower($acl));
52
+ $to_sign = $token;
53
+ if(isset($url)) array_push($to_sign, "url=" . self::escape_to_lower($url));
54
+ $auth = self::digest(join("~", $to_sign), $key);
55
+ array_push($token, "hmac=$auth");
56
+ return "$name=" . join("~", $token);
57
+ }
58
+
59
+ private static function digest($message, $key = NULL) {
60
+ if(!isset($key)) $key = \Cloudinary::config_get("akamai_key");
61
+ $bin_key = pack("H*", $key);
62
+ return hash_hmac( "sha256", $message, $bin_key);
63
+ }
64
+
65
+ private static function escape_to_lower($url) {
66
+ $escaped_url = rawurlencode( $url );
67
+ $escaped_url = preg_replace_callback("/(%..)/", function($match) {
68
+ return strtolower($match[1]);
69
+ }, $escaped_url);
70
+ return $escaped_url;
71
+ }
72
+ }
lib/Cloudinary/Cloudinary.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
 
3
  class Cloudinary {
4
 
@@ -10,9 +11,9 @@ class Cloudinary {
10
  const RANGE_VALUE_RE = '/^(?P<value>(\d+\.)?\d+)(?P<modifier>[%pP])?$/';
11
  const RANGE_RE = '/^(\d+\.)?\d+[%pP]?\.\.(\d+\.)?\d+[%pP]?$/';
12
 
13
- const VERSION = "1.1.4";
14
  /** @internal Do not change this value */
15
- const USER_AGENT = "CloudinaryPHP/1.1.4";
16
 
17
  /**
18
  * Additional information to be passed with the USER_AGENT, e.g. "CloudinaryMagento/1.0.1". This value is set in platform-specific
@@ -48,6 +49,8 @@ class Cloudinary {
48
  }
49
  }
50
 
 
 
51
  public static function config($values = NULL) {
52
  if (self::$config == NULL) {
53
  self::reset_config();
@@ -70,7 +73,7 @@ class Cloudinary {
70
  if (isset($uri["query"])) {
71
  parse_str($uri["query"], $q_params);
72
  }
73
- $private_cdn = isset($uri["path"]) && $uri["path"] != "/";
74
  $config = array_merge($q_params, array(
75
  "cloud_name" => $uri["host"],
76
  "api_key" => $uri["user"],
@@ -115,26 +118,30 @@ class Cloudinary {
115
  return array($value);
116
  }
117
  }
118
-
119
  public static function encode_array($array) {
120
  return implode(",", Cloudinary::build_array($array));
121
  }
122
-
123
  public static function encode_double_array($array) {
124
  $array = Cloudinary::build_array($array);
125
  if (count($array) > 0 && !is_array($array[0])) {
126
  return Cloudinary::encode_array($array);
127
  } else {
128
- $array = array_map('Cloudinary::encode_array', $array);
129
  }
130
-
131
  return implode("|", $array);
132
  }
133
-
134
  public static function encode_assoc_array($array) {
135
  if (Cloudinary::is_assoc($array)){
136
  $encoded = array();
137
  foreach ($array as $key => $value) {
 
 
 
 
138
  array_push($encoded, $key . '=' . $value);
139
  }
140
  return implode("|", $encoded);
@@ -142,7 +149,7 @@ class Cloudinary {
142
  return $array;
143
  }
144
  }
145
-
146
  private static function is_assoc($array) {
147
  if (!is_array($array)) return FALSE;
148
  return $array != array_values($array);
@@ -177,7 +184,7 @@ class Cloudinary {
177
 
178
  $no_html_sizes = $has_layer || !empty($angle) || $crop == "fit" || $crop == "limit" || $responsive_width;
179
 
180
- if (strlen($width) == 0 || $width && ($width == "auto" || floatval($width) < 1 || $no_html_sizes)) unset($options["width"]);
181
  if (strlen($height) == 0 || $height && (floatval($height) < 1 || $no_html_sizes)) unset($options["height"]);
182
 
183
  $background = Cloudinary::option_consume($options, "background");
@@ -197,12 +204,7 @@ class Cloudinary {
197
  $effect = Cloudinary::option_consume($options, "effect");
198
  if (is_array($effect)) $effect = implode(":", $effect);
199
 
200
- $border = Cloudinary::option_consume($options, "border");
201
- if (is_array($border)) {
202
- $border_width = Cloudinary::option_get($border, "width", "2");
203
- $border_color = preg_replace("/^#/", 'rgb:', Cloudinary::option_get($border, "color", "black"));
204
- $border = $border_width . "px_solid_" . $border_color;
205
- }
206
 
207
  $flags = implode(Cloudinary::build_array(Cloudinary::option_consume($options, "flags")), ".");
208
  $dpr = Cloudinary::option_consume($options, "dpr", Cloudinary::config_get("dpr"));
@@ -213,25 +215,31 @@ class Cloudinary {
213
  $offset = Cloudinary::split_range(Cloudinary::option_consume($options, "offset"));
214
  if (!empty($offset)) {
215
  $start_offset = Cloudinary::norm_range_value($offset[0]);
216
- $end_offset = Cloudinary::norm_range_value($offset[1]);
217
  }
218
-
219
  $video_codec = Cloudinary::process_video_codec_param(Cloudinary::option_consume($options, "video_codec"));
220
 
 
 
 
 
221
  $params = array(
222
- "a" => $angle,
223
- "b" => $background,
224
- "bo" => $border,
225
- "c" => $crop,
226
- "co" => $color,
227
  "dpr" => $dpr,
228
  "du" => $duration,
229
- "e" => $effect,
230
  "eo" => $end_offset,
231
- "fl" => $flags,
232
- "h" => $height,
 
233
  "so" => $start_offset,
234
  "t" => $named_transformation,
 
235
  "vc" => $video_codec,
236
  "w" => $width);
237
 
@@ -246,13 +254,11 @@ class Cloudinary {
246
  "dn" => "density",
247
  "f" => "fetch_format",
248
  "g" => "gravity",
249
- "l" => "overlay",
250
  "o" => "opacity",
251
  "p" => "prefix",
252
  "pg" => "page",
253
  "q" => "quality",
254
  "r" => "radius",
255
- "u" => "underlay",
256
  "vs" => "video_sampling",
257
  "x" => "x",
258
  "y" => "y",
@@ -266,6 +272,9 @@ class Cloudinary {
266
  $param_filter = function($value) { return $value === 0 || $value === '0' || trim($value) == true; };
267
  $params = array_filter($params, $param_filter);
268
  ksort($params);
 
 
 
269
  $join_pair = function($key, $value) { return $key . "_" . $value; };
270
  $transformation = implode(",", array_map($join_pair, array_keys($params), array_values($params)));
271
  $raw_transformation = Cloudinary::option_consume($options, "raw_transformation");
@@ -275,15 +284,147 @@ class Cloudinary {
275
  $responsive_width_transformation = Cloudinary::config_get("responsive_width_transformation", Cloudinary::$DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION);
276
  array_push($base_transformations, Cloudinary::generate_transformation_string($responsive_width_transformation));
277
  }
278
- if ($width == "auto" || $responsive_width) {
279
  $options["responsive"] = true;
280
  }
281
- if ($dpr == "auto") {
282
  $options["hidpi"] = true;
283
  }
284
  return implode("/", array_filter($base_transformations));
285
  }
286
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  private static function split_range($range) {
288
  if (is_array($range) && count($range) >= 2) {
289
  return array($range[0], end($range));
@@ -298,9 +439,9 @@ class Cloudinary {
298
  if (empty($value)) {
299
  return NULL;
300
  }
301
-
302
  preg_match(Cloudinary::RANGE_VALUE_RE, $value, $matches);
303
-
304
  if (empty($matches)) {
305
  return NULL;
306
  }
@@ -353,6 +494,12 @@ class Cloudinary {
353
  $api_secret = Cloudinary::option_consume($options, "api_secret", Cloudinary::config_get("api_secret"));
354
  $url_suffix = Cloudinary::option_consume($options, "url_suffix", Cloudinary::config_get("url_suffix"));
355
  $use_root_path = Cloudinary::option_consume($options, "use_root_path", Cloudinary::config_get("use_root_path"));
 
 
 
 
 
 
356
 
357
  if (!$private_cdn and !empty($url_suffix)) {
358
  throw new InvalidArgumentException("URL Suffix only supported in private CDN");
@@ -368,24 +515,37 @@ class Cloudinary {
368
  $sources = Cloudinary::finalize_source($source, $format, $url_suffix);
369
  $source = $sources["source"];
370
  $source_to_sign = $sources["source_to_sign"];
371
-
372
  if (strpos($source_to_sign, "/") && !preg_match("/^https?:\//", $source_to_sign) && !preg_match("/^v[0-9]+/", $source_to_sign) && empty($version)) {
373
  $version = "1";
374
  }
375
  $version = $version ? "v" . $version : NULL;
376
-
377
  $signature = NULL;
378
- if ($sign_url) {
379
  $to_sign = implode("/", array_filter(array($transformation, $source_to_sign)));
380
  $signature = str_replace(array('+','/','='), array('-','_',''), base64_encode(sha1($to_sign . $api_secret, TRUE)));
381
  $signature = 's--' . substr($signature, 0, 8) . '--';
382
  }
383
 
384
- $prefix = Cloudinary::unsigned_download_url_prefix($source, $cloud_name, $private_cdn, $cdn_subdomain, $secure_cdn_subdomain,
385
  $cname, $secure, $secure_distribution);
386
 
387
- return preg_replace("/([^:])\/+/", "$1/", implode("/", array_filter(array($prefix, $resource_type_and_type,
388
- $signature, $transformation, $version, $source))));
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  }
390
 
391
  private static function finalize_source($source, $format, $url_suffix) {
@@ -406,22 +566,25 @@ class Cloudinary {
406
  }
407
  }
408
  return array("source" => $source, "source_to_sign" => $source_to_sign);
409
- }
410
 
411
  private static function finalize_resource_type($resource_type, $type, $url_suffix, $use_root_path, $shorten) {
412
- if (empty($type)) {
413
- $type = "upload";
414
  }
415
 
416
  if (!empty($url_suffix)) {
417
  if ($resource_type == "image" && $type == "upload") {
418
  $resource_type = "images";
419
  $type = NULL;
420
- } else if ($resource_type == "raw" && $type == "upload") {
 
 
 
421
  $resource_type = "files";
422
  $type = NULL;
423
  } else {
424
- throw new InvalidArgumentException("URL Suffix only supported for image/upload and raw/upload");
425
  }
426
  }
427
 
@@ -450,7 +613,7 @@ class Cloudinary {
450
  // cdn_subdomain and secure_cdn_subdomain
451
  // 1) Customers in shared distribution (e.g. res.cloudinary.com)
452
  // if cdn_domain is true uses res-[1-5].cloudinary.com for both http and https. Setting secure_cdn_subdomain to false disables this for https.
453
- // 2) Customers with private cdn
454
  // if cdn_domain is true uses cloudname-res-[1-5].cloudinary.com for http
455
  // if secure_cdn_domain is true uses cloudname-res-[1-5].cloudinary.com for https (please contact support if you require this)
456
  // 3) Customers with cname
@@ -472,22 +635,26 @@ class Cloudinary {
472
  }
473
 
474
  if ($secure_cdn_subdomain) {
475
- $secure_distribution = str_replace('res.cloudinary.com', "res-" . ((crc32($source) % 5) + 1) . ".cloudinary.com", $secure_distribution);
476
  }
477
 
478
  $prefix = "https://" . $secure_distribution;
479
  } else if ($cname) {
480
- $subdomain = $cdn_subdomain ? "a" . ((crc32($source) % 5) + 1) . '.' : "";
481
  $prefix = "http://" . $subdomain . $cname;
482
  } else {
483
- $host = implode(array($private_cdn ? $cloud_name . "-" : "", "res", $cdn_subdomain ? "-" . ((crc32($source) % 5) + 1) : "", ".cloudinary.com"));
484
  $prefix = "http://" . $host;
485
  }
486
  if ($shared_domain) {
487
  $prefix = $prefix . '/' . $cloud_name;
488
  }
489
  return $prefix;
490
- }
 
 
 
 
491
 
492
  // [<resource_type>/][<image_type>/][v<version>/]<public_id>[.<format>][#<signature>]
493
  // Warning: $options are being destructively updated!
@@ -521,8 +688,8 @@ class Cloudinary {
521
 
522
  public static function cloudinary_api_url($action = 'upload', $options = array()) {
523
  $cloudinary = Cloudinary::option_get($options, "upload_prefix", Cloudinary::config_get("upload_prefix", "https://api.cloudinary.com"));
524
- $cloud_name = Cloudinary::config_get("cloud_name");
525
- if (!$cloud_name) throw new InvalidArgumentException("Must supply cloud_name in tag or in configuration");
526
  $resource_type = Cloudinary::option_get($options, "resource_type", "image");
527
  return implode("/", array($cloudinary, "v1_1", $cloud_name, $resource_type, $action));
528
  }
@@ -536,25 +703,129 @@ class Cloudinary {
536
  (isset($result["format"]) ? "." . $result["format"] : "") . "#" . $result["signature"];
537
  }
538
 
 
 
539
  public static function zip_download_url($tag, $options=array()) {
540
  $params = array("timestamp"=>time(), "tag"=>$tag, "transformation" => \Cloudinary::generate_transformation_string($options));
541
  $params = Cloudinary::sign_request($params, $options);
542
- return Cloudinary::cloudinary_api_url("download_tag.zip", $options) . "?" . http_build_query($params);
543
  }
544
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  public static function private_download_url($public_id, $format, $options = array()) {
546
  $cloudinary_params = Cloudinary::sign_request(array(
547
- "timestamp"=>time(),
548
- "public_id"=>$public_id,
549
- "format"=>$format,
550
- "type"=>Cloudinary::option_get($options, "type"),
551
- "attachment"=>Cloudinary::option_get($options, "attachment"),
552
- "expires_at"=>Cloudinary::option_get($options, "expires_at")
553
  ), $options);
554
 
555
- return Cloudinary::cloudinary_api_url("download", $options) . "?" . http_build_query($cloudinary_params);
556
  }
557
-
558
  public static function sign_request($params, &$options) {
559
  $api_key = Cloudinary::option_get($options, "api_key", Cloudinary::config_get("api_key"));
560
  if (!$api_key) throw new \InvalidArgumentException("Must supply api_key");
@@ -566,7 +837,7 @@ class Cloudinary {
566
 
567
  $params["signature"] = Cloudinary::api_sign_request($params, $api_secret);
568
  $params["api_key"] = $api_key;
569
-
570
  return $params;
571
  }
572
 
@@ -574,7 +845,11 @@ class Cloudinary {
574
  $params = array();
575
  foreach ($params_to_sign as $param => $value) {
576
  if (isset($value) && $value !== "") {
577
- $params[$param] = is_array($value) ? implode(",", $value) : $value;
 
 
 
 
578
  }
579
  }
580
  ksort($params);
@@ -590,20 +865,20 @@ class Cloudinary {
590
  $value = $v;
591
  if (is_int($k)) {
592
  $key = $v;
593
- $value = "";
594
  }
595
  if (is_array($only) && array_search($key, $only) !== FALSE || !is_array($only)) {
596
  $attrs[$key] = $value;
597
  }
598
  }
599
  ksort($attrs);
600
-
601
- $join_pair = function($key, $value) {
602
  $out = $key;
603
  if (!empty($value)) {
604
  $out .= '=\'' . $value . '\'';
605
  }
606
- return $out;
607
  };
608
  return implode(" ", array_map($join_pair, array_keys($attrs), array_values($attrs)));
609
  }
1
  <?php
2
+ require_once 'AuthToken.php';
3
 
4
  class Cloudinary {
5
 
11
  const RANGE_VALUE_RE = '/^(?P<value>(\d+\.)?\d+)(?P<modifier>[%pP])?$/';
12
  const RANGE_RE = '/^(\d+\.)?\d+[%pP]?\.\.(\d+\.)?\d+[%pP]?$/';
13
 
14
+ const VERSION = "1.6.2";
15
  /** @internal Do not change this value */
16
+ const USER_AGENT = "CloudinaryPHP/1.6.2";
17
 
18
  /**
19
  * Additional information to be passed with the USER_AGENT, e.g. "CloudinaryMagento/1.0.1". This value is set in platform-specific
49
  }
50
  }
51
 
52
+ public static function is_not_null ($var) { return !is_null($var);}
53
+
54
  public static function config($values = NULL) {
55
  if (self::$config == NULL) {
56
  self::reset_config();
73
  if (isset($uri["query"])) {
74
  parse_str($uri["query"], $q_params);
75
  }
76
+ $private_cdn = isset($uri["path"]) && $uri["path"] != "/";
77
  $config = array_merge($q_params, array(
78
  "cloud_name" => $uri["host"],
79
  "api_key" => $uri["user"],
118
  return array($value);
119
  }
120
  }
121
+
122
  public static function encode_array($array) {
123
  return implode(",", Cloudinary::build_array($array));
124
  }
125
+
126
  public static function encode_double_array($array) {
127
  $array = Cloudinary::build_array($array);
128
  if (count($array) > 0 && !is_array($array[0])) {
129
  return Cloudinary::encode_array($array);
130
  } else {
131
+ $array = array_map('Cloudinary::encode_array', $array);
132
  }
133
+
134
  return implode("|", $array);
135
  }
136
+
137
  public static function encode_assoc_array($array) {
138
  if (Cloudinary::is_assoc($array)){
139
  $encoded = array();
140
  foreach ($array as $key => $value) {
141
+ $value = !empty($value)
142
+ ? preg_replace('/([\|=])/', '\\\$1', $value)
143
+ : $value;
144
+
145
  array_push($encoded, $key . '=' . $value);
146
  }
147
  return implode("|", $encoded);
149
  return $array;
150
  }
151
  }
152
+
153
  private static function is_assoc($array) {
154
  if (!is_array($array)) return FALSE;
155
  return $array != array_values($array);
184
 
185
  $no_html_sizes = $has_layer || !empty($angle) || $crop == "fit" || $crop == "limit" || $responsive_width;
186
 
187
+ if (strlen($width) == 0 || $width && (substr($width, 0, 4) == "auto" || floatval($width) < 1 || $no_html_sizes)) unset($options["width"]);
188
  if (strlen($height) == 0 || $height && (floatval($height) < 1 || $no_html_sizes)) unset($options["height"]);
189
 
190
  $background = Cloudinary::option_consume($options, "background");
204
  $effect = Cloudinary::option_consume($options, "effect");
205
  if (is_array($effect)) $effect = implode(":", $effect);
206
 
207
+ $border = Cloudinary::process_border(Cloudinary::option_consume($options, "border"));
 
 
 
 
 
208
 
209
  $flags = implode(Cloudinary::build_array(Cloudinary::option_consume($options, "flags")), ".");
210
  $dpr = Cloudinary::option_consume($options, "dpr", Cloudinary::config_get("dpr"));
215
  $offset = Cloudinary::split_range(Cloudinary::option_consume($options, "offset"));
216
  if (!empty($offset)) {
217
  $start_offset = Cloudinary::norm_range_value($offset[0]);
218
+ $end_offset = Cloudinary::norm_range_value($offset[1]);
219
  }
220
+
221
  $video_codec = Cloudinary::process_video_codec_param(Cloudinary::option_consume($options, "video_codec"));
222
 
223
+ $overlay = Cloudinary::process_layer(Cloudinary::option_consume($options, "overlay"), "overlay");
224
+ $underlay = Cloudinary::process_layer(Cloudinary::option_consume($options, "underlay"), "underlay");
225
+ $if = Cloudinary::process_if(Cloudinary::option_consume($options, "if"));
226
+
227
  $params = array(
228
+ "a" => $angle,
229
+ "b" => $background,
230
+ "bo" => $border,
231
+ "c" => $crop,
232
+ "co" => $color,
233
  "dpr" => $dpr,
234
  "du" => $duration,
235
+ "e" => $effect,
236
  "eo" => $end_offset,
237
+ "fl" => $flags,
238
+ "h" => $height,
239
+ "l" => $overlay,
240
  "so" => $start_offset,
241
  "t" => $named_transformation,
242
+ "u" => $underlay,
243
  "vc" => $video_codec,
244
  "w" => $width);
245
 
254
  "dn" => "density",
255
  "f" => "fetch_format",
256
  "g" => "gravity",
 
257
  "o" => "opacity",
258
  "p" => "prefix",
259
  "pg" => "page",
260
  "q" => "quality",
261
  "r" => "radius",
 
262
  "vs" => "video_sampling",
263
  "x" => "x",
264
  "y" => "y",
272
  $param_filter = function($value) { return $value === 0 || $value === '0' || trim($value) == true; };
273
  $params = array_filter($params, $param_filter);
274
  ksort($params);
275
+ if (isset($if)) {
276
+ $params = array_merge(array("if"=>$if), $params);
277
+ }
278
  $join_pair = function($key, $value) { return $key . "_" . $value; };
279
  $transformation = implode(",", array_map($join_pair, array_keys($params), array_values($params)));
280
  $raw_transformation = Cloudinary::option_consume($options, "raw_transformation");
284
  $responsive_width_transformation = Cloudinary::config_get("responsive_width_transformation", Cloudinary::$DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION);
285
  array_push($base_transformations, Cloudinary::generate_transformation_string($responsive_width_transformation));
286
  }
287
+ if (substr($width, 0, 4) == "auto" || $responsive_width) {
288
  $options["responsive"] = true;
289
  }
290
+ if (substr($dpr, 0, 4) == "auto") {
291
  $options["hidpi"] = true;
292
  }
293
  return implode("/", array_filter($base_transformations));
294
  }
295
+
296
+ private static $LAYER_KEYWORD_PARAMS = array(
297
+ "font_weight"=>"normal", "font_style"=>"normal", "text_decoration"=>"none", "text_align"=>NULL, "stroke"=>"none"
298
+ );
299
+
300
+ private static function text_style( $layer, $layer_parameter) {
301
+ $font_family = Cloudinary::option_get($layer, "font_family");
302
+ $font_size = Cloudinary::option_get($layer, "font_size");
303
+ $keywords = array();
304
+ foreach (Cloudinary::$LAYER_KEYWORD_PARAMS as $attr=>$default_value) {
305
+ $attr_value = Cloudinary::option_get($layer, $attr, $default_value);
306
+ if ($attr_value != $default_value) {
307
+ array_push($keywords, $attr_value);
308
+ }
309
+ }
310
+ $letter_spacing = Cloudinary::option_get($layer, "letter_spacing");
311
+ if ($letter_spacing != NULL) {
312
+ array_push($keywords, "letter_spacing_$letter_spacing");
313
+ }
314
+ $line_spacing = Cloudinary::option_get($layer, "line_spacing");
315
+ if ($line_spacing != NULL) {
316
+ array_push($keywords, "line_spacing_$line_spacing");
317
+ }
318
+ $has_text_options = $font_size != NULL || $font_family != NULL || !empty($keywords);
319
+ if (!$has_text_options) {
320
+ return NULL;
321
+ }
322
+ if ($font_family == NULL) {
323
+ throw new InvalidArgumentException("Must supply font_family for text in $layer_parameter");
324
+ }
325
+ if ($font_size == NULL) {
326
+ throw new InvalidArgumentException("Must supply font_size for text in $layer_parameter");
327
+ }
328
+ array_unshift($keywords, $font_size);
329
+ array_unshift($keywords, $font_family);
330
+ return implode("_", array_filter($keywords, 'Cloudinary::is_not_null'));
331
+ }
332
+
333
+ private static function process_layer($layer, $layer_parameter) {
334
+ if (is_array($layer)) {
335
+ $resource_type = Cloudinary::option_get($layer, "resource_type");
336
+ $type = Cloudinary::option_get($layer, "type");
337
+ $text = Cloudinary::option_get($layer, "text");
338
+ $text_style = NULL;
339
+ $public_id = Cloudinary::option_get($layer, "public_id");
340
+ $format = Cloudinary::option_get($layer, "format");
341
+ $components = array();
342
+
343
+ if ($public_id != NULL){
344
+ $public_id = str_replace("/", ":", $public_id);
345
+ if($format != NULL) $public_id = $public_id . "." . $format;
346
+ }
347
+
348
+ if ($text == NULL && $resource_type != "text"){
349
+ if ($public_id == NULL) {
350
+ throw new InvalidArgumentException("Must supply public_id for $resource_type $layer_parameter");
351
+ }
352
+ if($resource_type == "subtitles") {
353
+ $text_style = Cloudinary::text_style($layer, $layer_parameter);
354
+ }
355
+
356
+ } else {
357
+ $resource_type = "text";
358
+ $type = NULL; // type is ignored for text layers
359
+ $text_style = Cloudinary::text_style($layer, $layer_parameter); #FIXME duplicate
360
+ if($text != NULL) {
361
+ if(!($public_id != NULL xor $text_style != NULL)) {
362
+ throw new InvalidArgumentException("Must supply either style parameters or a public_id when providing text parameter in a text $layer_parameter");
363
+ }
364
+ $text = Cloudinary::smart_escape($text);
365
+ $text = str_replace("%2C", "%252C", $text);
366
+ $text = str_replace("/", "%252F", $text);
367
+ }
368
+ }
369
+ if($resource_type != "image") array_push($components, $resource_type);
370
+ if($type != "upload") array_push($components, $type);
371
+ array_push($components, $text_style);
372
+ array_push($components, $public_id);
373
+ array_push($components, $text);
374
+ $layer = implode(":", array_filter($components, 'Cloudinary::is_not_null'));
375
+ }
376
+ return $layer;
377
+ }
378
+
379
+ private static $IF_OPERATORS = array(
380
+ "=" => 'eq',
381
+ "!=" => 'ne',
382
+ "<" => 'lt',
383
+ ">" => 'gt',
384
+ "<=" => 'lte',
385
+ ">=" => 'gte',
386
+ "&&" => 'and',
387
+ "||" => 'or');
388
+ private static $IF_PARAMETERS = array(
389
+ "width" => 'w',
390
+ "height" => 'h',
391
+ "page_count" => "pc",
392
+ "face_count" => "fc",
393
+ "aspect_ratio" => "ar"
394
+ );
395
+
396
+ private static function translate_if( $source )
397
+ {
398
+ if (isset(self::$IF_OPERATORS[$source[0]])) {
399
+ return self::$IF_OPERATORS[$source[0]];
400
+ } elseif (isset(self::$IF_PARAMETERS[$source[0]])) {
401
+ return self::$IF_PARAMETERS[$source[0]];
402
+ } else {
403
+ return $source[0];
404
+ }
405
+ }
406
+
407
+ private static $IF_REPLACE_RE;
408
+ private static function process_if($if) {
409
+ if (empty(self::$IF_REPLACE_RE)) {
410
+ self::$IF_REPLACE_RE = '/(' . implode('|', array_keys(self::$IF_PARAMETERS)) . '|[=<>&|!]+)/';
411
+ }
412
+ if (isset($if)) {
413
+ $if = preg_replace('/[ _]+/', '_', $if);
414
+ $if = preg_replace_callback(self::$IF_REPLACE_RE, array("Cloudinary", "translate_if"), $if);
415
+ }
416
+ return $if;
417
+ }
418
+
419
+ private static function process_border($border) {
420
+ if (is_array($border)) {
421
+ $border_width = Cloudinary::option_get($border, "width", "2");
422
+ $border_color = preg_replace("/^#/", 'rgb:', Cloudinary::option_get($border, "color", "black"));
423
+ $border = $border_width . "px_solid_" . $border_color;
424
+ }
425
+ return $border;
426
+ }
427
+
428
  private static function split_range($range) {
429
  if (is_array($range) && count($range) >= 2) {
430
  return array($range[0], end($range));
439
  if (empty($value)) {
440
  return NULL;
441
  }
442
+
443
  preg_match(Cloudinary::RANGE_VALUE_RE, $value, $matches);
444
+
445
  if (empty($matches)) {
446
  return NULL;
447
  }
494
  $api_secret = Cloudinary::option_consume($options, "api_secret", Cloudinary::config_get("api_secret"));
495
  $url_suffix = Cloudinary::option_consume($options, "url_suffix", Cloudinary::config_get("url_suffix"));
496
  $use_root_path = Cloudinary::option_consume($options, "use_root_path", Cloudinary::config_get("use_root_path"));
497
+ $auth_token = Cloudinary::option_consume($options, "auth_token");
498
+ if (is_array($auth_token) ) {
499
+ $auth_token = array_merge(self::config_get("auth_token", array()), $auth_token);
500
+ } elseif (is_null($auth_token)) {
501
+ $auth_token = self::config_get("auth_token");
502
+ }
503
 
504
  if (!$private_cdn and !empty($url_suffix)) {
505
  throw new InvalidArgumentException("URL Suffix only supported in private CDN");
515
  $sources = Cloudinary::finalize_source($source, $format, $url_suffix);
516
  $source = $sources["source"];
517
  $source_to_sign = $sources["source_to_sign"];
518
+
519
  if (strpos($source_to_sign, "/") && !preg_match("/^https?:\//", $source_to_sign) && !preg_match("/^v[0-9]+/", $source_to_sign) && empty($version)) {
520
  $version = "1";
521
  }
522
  $version = $version ? "v" . $version : NULL;
523
+
524
  $signature = NULL;
525
+ if ($sign_url && !$auth_token) {
526
  $to_sign = implode("/", array_filter(array($transformation, $source_to_sign)));
527
  $signature = str_replace(array('+','/','='), array('-','_',''), base64_encode(sha1($to_sign . $api_secret, TRUE)));
528
  $signature = 's--' . substr($signature, 0, 8) . '--';
529
  }
530
 
531
+ $prefix = Cloudinary::unsigned_download_url_prefix($source, $cloud_name, $private_cdn, $cdn_subdomain, $secure_cdn_subdomain,
532
  $cname, $secure, $secure_distribution);
533
 
534
+ $source = preg_replace( "/([^:])\/+/", "$1/", implode( "/", array_filter( array(
535
+ $prefix,
536
+ $resource_type_and_type,
537
+ $signature,
538
+ $transformation,
539
+ $version,
540
+ $source
541
+ ) ) ) );
542
+
543
+ if( $sign_url && $auth_token) {
544
+ $path = parse_url($source, PHP_URL_PATH);
545
+ $token = \Cloudinary\AuthToken::generate(array_merge($auth_token, array( "url" => $path)));
546
+ $source = $source . "?" . $token;
547
+ }
548
+ return $source;
549
  }
550
 
551
  private static function finalize_source($source, $format, $url_suffix) {
566
  }
567
  }
568
  return array("source" => $source, "source_to_sign" => $source_to_sign);
569
+ }
570
 
571
  private static function finalize_resource_type($resource_type, $type, $url_suffix, $use_root_path, $shorten) {
572
+ if (empty($type)) {
573
+ $type = "upload";
574
  }
575
 
576
  if (!empty($url_suffix)) {
577
  if ($resource_type == "image" && $type == "upload") {
578
  $resource_type = "images";
579
  $type = NULL;
580
+ } else if ($resource_type == "image" && $type == "private") {
581
+ $resource_type = "private_images";
582
+ $type = NULL;
583
+ } else if ($resource_type == "raw" && $type == "upload") {
584
  $resource_type = "files";
585
  $type = NULL;
586
  } else {
587
+ throw new InvalidArgumentException("URL Suffix only supported for image/upload, image/private and raw/upload");
588
  }
589
  }
590
 
613
  // cdn_subdomain and secure_cdn_subdomain
614
  // 1) Customers in shared distribution (e.g. res.cloudinary.com)
615
  // if cdn_domain is true uses res-[1-5].cloudinary.com for both http and https. Setting secure_cdn_subdomain to false disables this for https.
616
+ // 2) Customers with private cdn
617
  // if cdn_domain is true uses cloudname-res-[1-5].cloudinary.com for http
618
  // if secure_cdn_domain is true uses cloudname-res-[1-5].cloudinary.com for https (please contact support if you require this)
619
  // 3) Customers with cname
635
  }
636
 
637
  if ($secure_cdn_subdomain) {
638
+ $secure_distribution = str_replace('res.cloudinary.com', "res-" . Cloudinary::domain_shard($source) . ".cloudinary.com", $secure_distribution);
639
  }
640
 
641
  $prefix = "https://" . $secure_distribution;
642
  } else if ($cname) {
643
+ $subdomain = $cdn_subdomain ? "a" . Cloudinary::domain_shard($source) . '.' : "";
644
  $prefix = "http://" . $subdomain . $cname;
645
  } else {
646
+ $host = implode(array($private_cdn ? $cloud_name . "-" : "", "res", $cdn_subdomain ? "-" . Cloudinary::domain_shard($source) : "", ".cloudinary.com"));
647
  $prefix = "http://" . $host;
648
  }
649
  if ($shared_domain) {
650
  $prefix = $prefix . '/' . $cloud_name;
651
  }
652
  return $prefix;
653
+ }
654
+
655
+ private static function domain_shard($source) {
656
+ return (((crc32($source) % 5) + 5) % 5 + 1);
657
+ }
658
 
659
  // [<resource_type>/][<image_type>/][v<version>/]<public_id>[.<format>][#<signature>]
660
  // Warning: $options are being destructively updated!
688
 
689
  public static function cloudinary_api_url($action = 'upload', $options = array()) {
690
  $cloudinary = Cloudinary::option_get($options, "upload_prefix", Cloudinary::config_get("upload_prefix", "https://api.cloudinary.com"));
691
+ $cloud_name = Cloudinary::option_get($options, "cloud_name", Cloudinary::config_get("cloud_name"));
692
+ if (!$cloud_name) throw new InvalidArgumentException("Must supply cloud_name in options or in configuration");
693
  $resource_type = Cloudinary::option_get($options, "resource_type", "image");
694
  return implode("/", array($cloudinary, "v1_1", $cloud_name, $resource_type, $action));
695
  }
703
  (isset($result["format"]) ? "." . $result["format"] : "") . "#" . $result["signature"];
704
  }
705
 
706
+ # Utility method that uses the deprecated ZIP download API.
707
+ # @deprecated Replaced by {download_zip_url} that uses the more advanced and robust archive generation and download API
708
  public static function zip_download_url($tag, $options=array()) {
709
  $params = array("timestamp"=>time(), "tag"=>$tag, "transformation" => \Cloudinary::generate_transformation_string($options));
710
  $params = Cloudinary::sign_request($params, $options);
711
+ return Cloudinary::cloudinary_api_url("download_tag.zip", $options) . "?" . http_build_query($params);
712
  }
713
+
714
+
715
+ # Returns a URL that when invokes creates an archive and returns it.
716
+ # @param options [Hash]
717
+ # @option options [String] resource_type The resource type of files to include in the archive. Must be one of image | video | raw
718
+ # @option options [String] type (upload) The specific file type of resources upload|private|authenticated
719
+ # @option options [String|Array] tags (nil) list of tags to include in the archive
720
+ # @option options [String|Array<String>] public_ids (nil) list of public_ids to include in the archive
721
+ # @option options [String|Array<String>] prefixes (nil) Optional list of prefixes of public IDs (e.g., folders).
722
+ # @option options [String|Array<String>] transformations Optional list of transformations.
723
+ # The derived images of the given transformations are included in the archive. Using the string representation of
724
+ # multiple chained transformations as we use for the 'eager' upload parameter.
725
+ # @option options [String] mode (create) return the generated archive file or to store it as a raw resource and
726
+ # return a JSON with URLs for accessing the archive. Possible values download, create
727
+ # @option options [String] target_format (zip)
728
+ # @option options [String] target_public_id Optional public ID of the generated raw resource.
729
+ # Relevant only for the create mode. If not specified, random public ID is generated.
730
+ # @option options [boolean] flatten_folders (false) If true, flatten public IDs with folders to be in the root of the archive.
731
+ # Add numeric counter to the file name in case of a name conflict.
732
+ # @option options [boolean] flatten_transformations (false) If true, and multiple transformations are given,
733
+ # flatten the folder structure of derived images and store the transformation details on the file name instead.
734
+ # @option options [boolean] use_original_filename Use the original file name of included images (if available) instead of the public ID.
735
+ # @option options [boolean] async (false) If true, return immediately and perform the archive creation in the background.
736
+ # Relevant only for the create mode.
737
+ # @option options [String] notification_url Optional URL to send an HTTP post request (webhook) when the archive creation is completed.
738
+ # @option options [String|Array<String] target_tags Optional array. Allows assigning one or more tag to the generated archive file (for later housekeeping via the admin API).
739
+ # @option options [String] keep_derived (false) keep the derived images used for generating the archive
740
+ # @return [String] archive url
741
+ public static function download_archive_url($options=array()) {
742
+ $options["mode"] = "download";
743
+ $params = Cloudinary::build_archive_params($options);
744
+ $params = Cloudinary::sign_request($params, $options);
745
+ return Cloudinary::cloudinary_api_url("generate_archive", $options) . "?" . preg_replace("/%5B\d+%5D/", "%5B%5D", http_build_query($params));
746
+ }
747
+
748
+ # Returns a URL that when invokes creates an zip archive and returns it.
749
+ # @see download_archive_url
750
+ public static function download_zip_url($options=array()) {
751
+ $options["target_format"] = "zip";
752
+ return Cloudinary::download_archive_url($options);
753
+ }
754
+
755
+ /**
756
+ * Generate an authorization token.
757
+ * Options:
758
+ * string key - the secret key required to sign the token
759
+ * string ip - the IP address of the client
760
+ * number start_time - the start time of the token in seconds from epoch
761
+ * string expiration - the expiration time of the token in seconds from epoch
762
+ * string duration - the duration of the token (from start_time)
763
+ * string acl - the ACL for the token
764
+ * string url - the URL to authentication in case of a URL token
765
+ *
766
+ * @param array $options token configuration, merge with the global configuration "auth_token".
767
+ * @return string the authorization token
768
+ */
769
+ public static function generate_auth_token($options){
770
+ $token_options = array_merge(self::config_get("auth_token", array()), $options);
771
+ return \Cloudinary\AuthToken::generate($token_options);
772
+ }
773
+
774
+ # Returns a Hash of parameters used to create an archive
775
+ # @param [Hash] options
776
+ # @private
777
+ public static function build_archive_params(&$options)
778
+ {
779
+ $params = array(
780
+ "allow_missing" => \Cloudinary::option_get($options, "allow_missing"),
781
+ "async" => \Cloudinary::option_get($options, "async"),
782
+ "expires_at" => \Cloudinary::option_get($options, "expires_at"),
783
+ "flatten_folders" => \Cloudinary::option_get($options, "flatten_folders"),
784
+ "flatten_transformations" => \Cloudinary::option_get($options, "flatten_transformations"),
785
+ "keep_derived" => \Cloudinary::option_get($options, "keep_derived"),
786
+ "mode" => \Cloudinary::option_get($options, "mode"),
787
+ "notification_url" => \Cloudinary::option_get($options, "notification_url"),
788
+ "phash" => \Cloudinary::option_get($options, "phash"),
789
+ "prefixes" => \Cloudinary::build_array(\Cloudinary::option_get($options, "prefixes")),
790
+ "public_ids" => \Cloudinary::build_array(\Cloudinary::option_get($options, "public_ids")),
791
+ "skip_transformation_name" => \Cloudinary::option_get($options, "skip_transformation_name"),
792
+ "tags" => \Cloudinary::build_array(\Cloudinary::option_get($options, "tags")),
793
+ "target_format" => \Cloudinary::option_get($options, "target_format"),
794
+ "target_public_id" => \Cloudinary::option_get($options, "target_public_id"),
795
+ "target_tags" => \Cloudinary::build_array(\Cloudinary::option_get($options, "target_tags")),
796
+ "timestamp" => time(),
797
+ "transformations" => \Cloudinary::build_eager(\Cloudinary::option_get($options, "transformations")),
798
+ "type" => \Cloudinary::option_get($options, "type"),
799
+ "use_original_filename" => \Cloudinary::option_get($options, "use_original_filename"),
800
+ );
801
+ array_walk($params, function (&$value, $key){ $value = (is_bool($value) ? ($value ? "1" : "0") : $value);});
802
+ return array_filter($params,function($v){ return !is_null($v) && ($v !== "" );});
803
+ }
804
+
805
+ public static function build_eager($transformations) {
806
+ $eager = array();
807
+ foreach (\Cloudinary::build_array($transformations) as $trans) {
808
+ $transformation = $trans;
809
+ $format = \Cloudinary::option_consume($transformation, "format");
810
+ $single_eager = implode("/", array_filter(array(\Cloudinary::generate_transformation_string($transformation), $format)));
811
+ array_push($eager, $single_eager);
812
+ }
813
+ return implode("|", $eager);
814
+ }
815
+
816
  public static function private_download_url($public_id, $format, $options = array()) {
817
  $cloudinary_params = Cloudinary::sign_request(array(
818
+ "timestamp" => time(),
819
+ "public_id" => $public_id,
820
+ "format" => $format,
821
+ "type" => Cloudinary::option_get($options, "type"),
822
+ "attachment" => Cloudinary::option_get($options, "attachment"),
823
+ "expires_at" => Cloudinary::option_get($options, "expires_at")
824
  ), $options);
825
 
826
+ return Cloudinary::cloudinary_api_url("download", $options) . "?" . http_build_query($cloudinary_params);
827
  }
828
+
829
  public static function sign_request($params, &$options) {
830
  $api_key = Cloudinary::option_get($options, "api_key", Cloudinary::config_get("api_key"));
831
  if (!$api_key) throw new \InvalidArgumentException("Must supply api_key");
837
 
838
  $params["signature"] = Cloudinary::api_sign_request($params, $api_secret);
839
  $params["api_key"] = $api_key;
840
+
841
  return $params;
842
  }
843
 
845
  $params = array();
846
  foreach ($params_to_sign as $param => $value) {
847
  if (isset($value) && $value !== "") {
848
+ if (!is_array($value)) {
849
+ $params[$param] = $value;
850
+ } else if (count($value) > 0) {
851
+ $params[$param] = implode(",", $value);
852
+ }
853
  }
854
  }
855
  ksort($params);
865
  $value = $v;
866
  if (is_int($k)) {
867
  $key = $v;
868
+ $value = "";
869
  }
870
  if (is_array($only) && array_search($key, $only) !== FALSE || !is_array($only)) {
871
  $attrs[$key] = $value;
872
  }
873
  }
874
  ksort($attrs);
875
+
876
+ $join_pair = function($key, $value) {
877
  $out = $key;
878
  if (!empty($value)) {
879
  $out .= '=\'' . $value . '\'';
880
  }
881
+ return $out;
882
  };
883
  return implode(" ", array_map($join_pair, array_keys($attrs), array_values($attrs)));
884
  }
lib/Cloudinary/Helpers.php CHANGED
@@ -75,9 +75,10 @@ namespace {
75
  if (isset($options["html_width"])) $options["width"] = Cloudinary::option_consume($options, "html_width");
76
  if (isset($options["html_height"])) $options["height"] = Cloudinary::option_consume($options, "html_height");
77
 
 
78
  $responsive = Cloudinary::option_consume($options, "responsive");
79
  $hidpi = Cloudinary::option_consume($options, "hidpi");
80
- if ($responsive || $hidpi) {
81
  $options["data-src"] = $source;
82
  $classes = array($responsive ? "cld-responsive" : "cld-hidpi");
83
  $current_class = Cloudinary::option_consume($options, "class");
75
  if (isset($options["html_width"])) $options["width"] = Cloudinary::option_consume($options, "html_width");
76
  if (isset($options["html_height"])) $options["height"] = Cloudinary::option_consume($options, "html_height");
77
 
78
+ $client_hints = Cloudinary::option_consume($options, "client_hints", Cloudinary::config_get("client_hints"));
79
  $responsive = Cloudinary::option_consume($options, "responsive");
80
  $hidpi = Cloudinary::option_consume($options, "hidpi");
81
+ if (($responsive || $hidpi) && !$client_hints) {
82
  $options["data-src"] = $source;
83
  $classes = array($responsive ? "cld-responsive" : "cld-hidpi");
84
  $current_class = Cloudinary::option_consume($options, "class");
lib/Cloudinary/Uploader.php CHANGED
@@ -44,7 +44,8 @@ namespace Cloudinary {
44
  "type" => \Cloudinary::option_get($options, "type"),
45
  "unique_filename" => \Cloudinary::option_get($options, "unique_filename"),
46
  "upload_preset" => \Cloudinary::option_get($options, "upload_preset"),
47
- "use_filename" => \Cloudinary::option_get($options, "use_filename")
 
48
  );
49
  array_walk($params, function (&$value, $key){ $value = (is_bool($value) ? ($value ? "1" : "0") : $value);});
50
  return array_filter($params,function($v){ return !is_null($v) && ($v !== "" );});
@@ -130,28 +131,17 @@ namespace Cloudinary {
130
  "type" => \Cloudinary::option_get($options, "type"),
131
  "from_public_id" => $from_public_id,
132
  "to_public_id" => $to_public_id,
133
- "overwrite" => \Cloudinary::option_get($options, "overwrite")
 
 
134
  );
135
  return Uploader::call_api("rename", $params, $options);
136
  }
137
 
138
  public static function explicit($public_id, $options = array())
139
  {
140
- $params = array(
141
- "callback" => \Cloudinary::option_get($options, "callback"),
142
- "context" => \Cloudinary::encode_assoc_array(\Cloudinary::option_get($options, "context")),
143
- "custom_coordinates" => \Cloudinary::encode_double_array(\Cloudinary::option_get($options, "custom_coordinates")),
144
- "eager" => Uploader::build_eager(\Cloudinary::option_get($options, "eager")),
145
- "eager_async" => \Cloudinary::option_get($options, "eager_async"),
146
- "eager_notification_url" => \Cloudinary::option_get($options, "eager_notification_url"),
147
- "face_coordinates" => \Cloudinary::encode_double_array(\Cloudinary::option_get($options, "face_coordinates")),
148
- "headers" => Uploader::build_custom_headers(\Cloudinary::option_get($options, "headers")),
149
- "invalidate" => \Cloudinary::option_get($options, "invalidate"),
150
- "public_id" => $public_id,
151
- "tags" => \Cloudinary::encode_array(\Cloudinary::option_get($options, "tags")),
152
- "timestamp" => time(),
153
- "type" => \Cloudinary::option_get($options, "type")
154
- );
155
  return Uploader::call_api("explicit", $params, $options);
156
  }
157
 
@@ -197,12 +187,9 @@ namespace Cloudinary {
197
  return Uploader::call_api("explode", $params, $options);
198
  }
199
 
200
- // options may include 'exclusive' (boolean) which causes clearing this tag from all other resources
201
  public static function add_tag($tag, $public_ids = array(), $options = array())
202
  {
203
- $exclusive = \Cloudinary::option_get($options, "exclusive");
204
- $command = $exclusive ? "set_exclusive" : "add";
205
- return Uploader::call_tags_api($tag, $command, $public_ids, $options);
206
  }
207
 
208
  public static function remove_tag($tag, $public_ids = array(), $options = array())
@@ -227,6 +214,26 @@ namespace Cloudinary {
227
  return Uploader::call_api("tags", $params, $options);
228
  }
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  private static $TEXT_PARAMS = array("public_id", "font_family", "font_size", "font_color", "text_align", "font_weight", "font_style", "background", "opacity", "text_decoration");
231
 
232
  public static function text($text, $options = array())
@@ -238,6 +245,22 @@ namespace Cloudinary {
238
  return Uploader::call_api("text", $params, $options);
239
  }
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  public static function call_api($action, $params, $options = array(), $file = NULL)
242
  {
243
  $return_error = \Cloudinary::option_get($options, "return_error");
@@ -277,6 +300,10 @@ namespace Cloudinary {
277
  curl_setopt($ch, CURLOPT_POST, true);
278
  $timeout = \Cloudinary::option_get($options, "timeout", \Cloudinary::config_get("timeout", 60));
279
  curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
 
 
 
 
280
  curl_setopt($ch, CURLOPT_POSTFIELDS, $post_params);
281
  curl_setopt($ch, CURLOPT_CAINFO,realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR."cacert.pem");
282
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); # no effect since PHP 5.1.3
@@ -318,15 +345,26 @@ namespace Cloudinary {
318
  }
319
  return $result;
320
  }
 
321
  protected static function build_eager($transformations) {
322
- $eager = array();
323
- foreach (\Cloudinary::build_array($transformations) as $trans) {
324
- $transformation = $trans;
325
- $format = \Cloudinary::option_consume($transformation, "format");
326
- $single_eager = implode("/", array_filter(array(\Cloudinary::generate_transformation_string($transformation), $format)));
327
- array_push($eager, $single_eager);
 
 
 
 
 
 
 
 
 
 
328
  }
329
- return implode("|", $eager);
330
  }
331
 
332
  protected static function build_custom_headers($headers) {
44
  "type" => \Cloudinary::option_get($options, "type"),
45
  "unique_filename" => \Cloudinary::option_get($options, "unique_filename"),
46
  "upload_preset" => \Cloudinary::option_get($options, "upload_preset"),
47
+ "use_filename" => \Cloudinary::option_get($options, "use_filename"),
48
+ "responsive_breakpoints" => Uploader::build_responsive_breakpoints(\Cloudinary::option_get($options, "responsive_breakpoints"))
49
  );
50
  array_walk($params, function (&$value, $key){ $value = (is_bool($value) ? ($value ? "1" : "0") : $value);});
51
  return array_filter($params,function($v){ return !is_null($v) && ($v !== "" );});
131
  "type" => \Cloudinary::option_get($options, "type"),
132
  "from_public_id" => $from_public_id,
133
  "to_public_id" => $to_public_id,
134
+ "invalidate" => \Cloudinary::option_get($options, "invalidate"),
135
+ "overwrite" => \Cloudinary::option_get($options, "overwrite"),
136
+ "to_type" => \Cloudinary::option_get($options, "to_type"),
137
  );
138
  return Uploader::call_api("rename", $params, $options);
139
  }
140
 
141
  public static function explicit($public_id, $options = array())
142
  {
143
+ $options["public_id"] = $public_id;
144
+ $params = Uploader::build_upload_params($options);
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  return Uploader::call_api("explicit", $params, $options);
146
  }
147
 
187
  return Uploader::call_api("explode", $params, $options);
188
  }
189
 
 
190
  public static function add_tag($tag, $public_ids = array(), $options = array())
191
  {
192
+ return Uploader::call_tags_api($tag, "add", $public_ids, $options);
 
 
193
  }
194
 
195
  public static function remove_tag($tag, $public_ids = array(), $options = array())
214
  return Uploader::call_api("tags", $params, $options);
215
  }
216
 
217
+ public static function add_context($context, $public_ids = array(), $options = array()) {
218
+ return Uploader::call_context_api($context, 'add', $public_ids, $options);
219
+ }
220
+
221
+ public static function remove_all_context($public_ids = array(), $options = array()) {
222
+ return Uploader::call_context_api(null, 'remove_all', $public_ids, $options);
223
+ }
224
+
225
+ public static function call_context_api($context, $command, $public_ids = array(), &$options = array())
226
+ {
227
+ $params = array(
228
+ "timestamp" => time(),
229
+ "context" => $context,
230
+ "public_ids" => \Cloudinary::build_array($public_ids),
231
+ "type" => \Cloudinary::option_get($options, "type"),
232
+ "command" => $command
233
+ );
234
+ return Uploader::call_api("context", $params, $options);
235
+ }
236
+
237
  private static $TEXT_PARAMS = array("public_id", "font_family", "font_size", "font_color", "text_align", "font_weight", "font_style", "background", "opacity", "text_decoration");
238
 
239
  public static function text($text, $options = array())
245
  return Uploader::call_api("text", $params, $options);
246
  }
247
 
248
+ # Creates a new archive in the server and returns information in JSON format
249
+ public static function create_archive($options = array(), $target_format = NULL)
250
+ {
251
+ $params = \Cloudinary::build_archive_params($options);
252
+ if ($target_format != NULL) {
253
+ $params["target_format"] = $target_format;
254
+ }
255
+ return Uploader::call_api("generate_archive", $params, $options);
256
+ }
257
+
258
+ # Creates a new zip archive in the server and returns information in JSON format
259
+ public static function create_zip($options = array())
260
+ {
261
+ return Uploader::create_archive($options, "zip");
262
+ }
263
+
264
  public static function call_api($action, $params, $options = array(), $file = NULL)
265
  {
266
  $return_error = \Cloudinary::option_get($options, "return_error");
300
  curl_setopt($ch, CURLOPT_POST, true);
301
  $timeout = \Cloudinary::option_get($options, "timeout", \Cloudinary::config_get("timeout", 60));
302
  curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
303
+ $connection_timeout = \Cloudinary::option_get($options, "connection_timeout", \Cloudinary::config_get("connection_timeout"));
304
+ if ($connection_timeout != NULL) {
305
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connection_timeout);
306
+ }
307
  curl_setopt($ch, CURLOPT_POSTFIELDS, $post_params);
308
  curl_setopt($ch, CURLOPT_CAINFO,realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR."cacert.pem");
309
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); # no effect since PHP 5.1.3
345
  }
346
  return $result;
347
  }
348
+
349
  protected static function build_eager($transformations) {
350
+ return \Cloudinary::build_eager($transformations);
351
+ }
352
+
353
+ protected static function build_responsive_breakpoints($breakpoints) {
354
+ if (!$breakpoints) {
355
+ return NULL;
356
+ }
357
+ $breakpoints_params = array();
358
+ foreach (\Cloudinary::build_array($breakpoints) as $breakpoint_settings) {
359
+ if ($breakpoint_settings) {
360
+ $transformation = \Cloudinary::option_consume($breakpoint_settings, "transformation");
361
+ if ($transformation) {
362
+ $breakpoint_settings["transformation"] = \Cloudinary::generate_transformation_string($transformation);
363
+ }
364
+ array_push($breakpoints_params, $breakpoint_settings);
365
+ }
366
  }
367
+ return json_encode($breakpoints_params);
368
  }
369
 
370
  protected static function build_custom_headers($headers) {
lib/CloudinaryExtension/CloudinaryImageManager.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace CloudinaryExtension;
3
+
4
+ /**
5
+ * Class CloudinaryImageManager
6
+ * @package CloudinaryExtension
7
+ */
8
+ class CloudinaryImageManager
9
+ {
10
+ /**
11
+ * @var ImageProvider
12
+ */
13
+ private $cloudinaryImageProvider;
14
+
15
+ /**
16
+ * @var SynchroniseAssetsRepositoryInterface
17
+ */
18
+ private $synchronisationRepository;
19
+
20
+ /**
21
+ * CloudinaryImageManager constructor.
22
+ *
23
+ * @param ImageProvider $cloudinaryImageProvider
24
+ * @param SynchroniseAssetsRepositoryInterface $synchronisationRepository
25
+ */
26
+ public function __construct(
27
+ ImageProvider $cloudinaryImageProvider,
28
+ SynchroniseAssetsRepositoryInterface $synchronisationRepository
29
+ ) {
30
+ $this->cloudinaryImageProvider = $cloudinaryImageProvider;
31
+ $this->synchronisationRepository = $synchronisationRepository;
32
+ }
33
+
34
+ /**
35
+ * @param Image $image
36
+ */
37
+ public function uploadAndSynchronise(Image $image)
38
+ {
39
+ $this->cloudinaryImageProvider->upload($image);
40
+ $this->synchronisationRepository->saveAsSynchronized($image->getRelativePath());
41
+
42
+ }
43
+
44
+ /**
45
+ * @param Image $image
46
+ */
47
+ public function removeAndUnSynchronise(Image $image)
48
+ {
49
+ $this->cloudinaryImageProvider->delete($image);
50
+ $this->synchronisationRepository->removeSynchronised($image->getRelativePath());
51
+ }
52
+ }
lib/CloudinaryExtension/CloudinaryImageProvider.php CHANGED
@@ -9,81 +9,88 @@ use CloudinaryExtension\Exception\FileAlreadyExists;
9
  use CloudinaryExtension\Exception\MigrationError;
10
  use CloudinaryExtension\Image\Transformation;
11
  use CloudinaryExtension\Security;
 
 
12
 
13
  class CloudinaryImageProvider implements ImageProvider
14
  {
15
  private $configuration;
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
- private $uploadConfig = array(
18
- "use_filename" => true,
19
- "unique_filename" => false,
20
- "overwrite" => false
21
- );
22
-
23
- private function __construct(Configuration $configuration)
24
- {
25
  $this->configuration = $configuration;
 
 
 
26
  $this->authorise();
27
  }
28
 
29
- public static function fromConfiguration(Configuration $configuration)
30
- {
31
- return new CloudinaryImageProvider($configuration);
 
 
 
 
32
  }
33
 
34
  public function upload(Image $image)
35
  {
36
- try{
37
- $imagePath = (string)$image;
38
- $uploadOptionsAndFolder = $this->uploadConfig + ["folder" => $image->getRelativeFolder()];
39
- $uploadResult = Uploader::upload($imagePath, $uploadOptionsAndFolder);
 
 
 
40
 
41
- if ($uploadResult['existing'] == 1) {
42
- MigrationError::throwWith($image, MigrationError::CODE_FILE_ALREADY_EXISTS);
43
- }
44
- return $uploadResult;
45
  } catch (\Exception $e) {
46
  MigrationError::throwWith($image, MigrationError::CODE_API_ERROR, $e->getMessage());
47
  }
48
  }
49
 
50
- public function transformImage(Image $image, Transformation $transformation = null)
51
  {
52
- if ($transformation === null) {
53
- $transformation = $this->configuration->getDefaultTransformation();
54
- }
55
- return Image::fromPath(\cloudinary_url($image->getId(), $transformation->build()), $image->getRelativePath());
56
  }
57
 
58
- public function validateCredentials()
59
  {
60
- $signedValidationUrl = $this->getSignedValidationUrl();
61
- return $this->validationResult($signedValidationUrl);
62
  }
63
 
64
- public function deleteImage(Image $image)
65
  {
66
  Uploader::destroy($image->getId());
67
  }
68
 
69
- private function authorise()
70
- {
71
- Cloudinary::config($this->configuration->build());
72
- Cloudinary::$USER_PLATFORM = $this->configuration->getUserPlatform();
73
- }
74
-
75
- private function getSignedValidationUrl()
76
  {
77
- $consoleUrl = Security\ConsoleUrl::fromPath("media_library/cms");
78
- return (string)Security\SignedConsoleUrl::fromConsoleUrlAndCredentials(
79
- $consoleUrl,
80
- $this->configuration->getCredentials()
81
- );
82
  }
83
 
84
- private function validationResult($signedValidationUrl)
85
  {
86
- $request = new ValidateRemoteUrlRequest($signedValidationUrl);
87
- return $request->validate();
88
  }
89
- }
9
  use CloudinaryExtension\Exception\MigrationError;
10
  use CloudinaryExtension\Image\Transformation;
11
  use CloudinaryExtension\Security;
12
+ use CloudinaryExtension\Image\Transformation\Format;
13
+ use CloudinaryExtension\Image\Transformation\FetchFormat;
14
 
15
  class CloudinaryImageProvider implements ImageProvider
16
  {
17
  private $configuration;
18
+ /**
19
+ * @var UploadResponseValidator
20
+ */
21
+ private $uploadResponseValidator;
22
+ /**
23
+ * @var ConfigurationBuilder
24
+ */
25
+ private $configurationBuilder;
26
+ /**
27
+ * @var CredentialValidator
28
+ */
29
+ private $credentialValidator;
30
 
31
+ public function __construct(
32
+ ConfigurationInterface $configuration,
33
+ ConfigurationBuilder $configurationBuilder,
34
+ UploadResponseValidator $uploadResponseValidator,
35
+ CredentialValidator $credentialValidator
36
+ ) {
 
 
37
  $this->configuration = $configuration;
38
+ $this->uploadResponseValidator = $uploadResponseValidator;
39
+ $this->configurationBuilder = $configurationBuilder;
40
+ $this->credentialValidator = $credentialValidator;
41
  $this->authorise();
42
  }
43
 
44
+ public static function fromConfiguration(ConfigurationInterface $configuration){
45
+ return new CloudinaryImageProvider(
46
+ $configuration,
47
+ new ConfigurationBuilder($configuration),
48
+ new UploadResponseValidator(),
49
+ new CredentialValidator()
50
+ );
51
  }
52
 
53
  public function upload(Image $image)
54
  {
55
+ try {
56
+ $uploadResult = Uploader::upload(
57
+ (string)$image,
58
+ $this->configuration->getUploadConfig()->toArray() + [ "folder" => $image->getRelativeFolder()]
59
+ );
60
+
61
+ return $this->uploadResponseValidator->validateResponse($image, $uploadResult);
62
 
 
 
 
 
63
  } catch (\Exception $e) {
64
  MigrationError::throwWith($image, MigrationError::CODE_API_ERROR, $e->getMessage());
65
  }
66
  }
67
 
68
+ public function retrieveTransformed(Image $image, Transformation $transformation)
69
  {
70
+ return Image::fromPath(
71
+ \cloudinary_url($image->getId(), $transformation->build() + ["secure" => true]),
72
+ $image->getRelativePath()
73
+ );
74
  }
75
 
76
+ public function retrieve(Image $image)
77
  {
78
+ return $this->retrieveTransformed($image, $this->configuration->getDefaultTransformation());
 
79
  }
80
 
81
+ public function delete(Image $image)
82
  {
83
  Uploader::destroy($image->getId());
84
  }
85
 
86
+ public function validateCredentials()
 
 
 
 
 
 
87
  {
88
+ return $this->credentialValidator->validate($this->configuration->getCredentials());
 
 
 
 
89
  }
90
 
91
+ private function authorise()
92
  {
93
+ Cloudinary::config($this->configurationBuilder->build());
94
+ Cloudinary::$USER_PLATFORM = $this->configuration->getUserPlatform();
95
  }
96
+ }
lib/CloudinaryExtension/Configuration.php DELETED
@@ -1,97 +0,0 @@
1
- <?php
2
-
3
- namespace CloudinaryExtension;
4
-
5
- use CloudinaryExtension\Image\Transformation;
6
- use CloudinaryExtension\Security\EnvironmentVariable;
7
-
8
- class Configuration
9
- {
10
- private $credentials;
11
-
12
- private $cloud;
13
-
14
- private $defaultTransformation;
15
-
16
- private $cdnSubdomain = true;
17
-
18
- private $userPlatform = '';
19
-
20
- private function __construct(Cloud $cloud,Credentials $credentials)
21
- {
22
- $this->cdnSubdomain = false;
23
- $this->credentials = $credentials;
24
- $this->cloud = $cloud;
25
- $this->defaultTransformation = Transformation::builder();
26
- }
27
-
28
- public static function fromCloudAndCredentials(Cloud $cloud, Credentials $credentials)
29
- {
30
- return new Configuration($cloud, $credentials);
31
- }
32
-
33
- public static function fromEnvironmentVariable(EnvironmentVariable $environmentVariable)
34
- {
35
- return new Configuration($environmentVariable->getCloud(), $environmentVariable->getCredentials());
36
- }
37
-
38
- public function getCloud()
39
- {
40
- return $this->cloud;
41
- }
42
-
43
- public function getCredentials()
44
- {
45
- return $this->credentials;
46
- }
47
-
48
- public function getDefaultTransformation()
49
- {
50
- return $this->defaultTransformation;
51
- }
52
-
53
- public function build()
54
- {
55
- $configuration = $this->getMandatoryConfiguration();
56
- if($this->cdnSubdomain) {
57
- $configuration['cdn_subdomain'] = true;
58
- }
59
-
60
- return $configuration;
61
- }
62
-
63
- public function enableCdnSubdomain()
64
- {
65
- $this->cdnSubdomain = true;
66
- }
67
-
68
- public function getCdnSubdomainStatus()
69
- {
70
- return $this->cdnSubdomain;
71
- }
72
-
73
- private function getMandatoryConfiguration()
74
- {
75
- return array(
76
- "cloud_name" => (string)$this->cloud,
77
- "api_key" => (string)$this->credentials->getKey(),
78
- "api_secret" => (string)$this->credentials->getSecret()
79
- );
80
- }
81
-
82
- /**
83
- * @return string
84
- */
85
- public function getUserPlatform()
86
- {
87
- return $this->userPlatform;
88
- }
89
-
90
- /**
91
- * @param string $userPlatform
92
- */
93
- public function setUserPlatform($userPlatform)
94
- {
95
- $this->userPlatform = $userPlatform;
96
- }
97
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/CloudinaryExtension/ConfigurationBuilder.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CloudinaryExtension;
4
+
5
+ class ConfigurationBuilder
6
+ {
7
+ /**
8
+ * @var ConfigurationInterface
9
+ */
10
+ private $configuration;
11
+
12
+ public function __construct(ConfigurationInterface $configuration)
13
+ {
14
+ $this->configuration = $configuration;
15
+ }
16
+
17
+ public function build()
18
+ {
19
+ $config = [
20
+ "cloud_name" => (string)$this->configuration->getCloud(),
21
+ "api_key" => (string)$this->configuration->getCredentials()->getKey(),
22
+ "api_secret" => (string)$this->configuration->getCredentials()->getSecret()
23
+ ];
24
+
25
+ if ($this->configuration->getCdnSubdomainStatus()) {
26
+ $config['cdn_subdomain'] = true;
27
+ }
28
+ return $config;
29
+ }
30
+ }
lib/CloudinaryExtension/ConfigurationInterface.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CloudinaryExtension;
4
+
5
+ use CloudinaryExtension\Cloud;
6
+ use CloudinaryExtension\Credentials;
7
+ use CloudinaryExtension\Image\Transformation;
8
+
9
+ interface ConfigurationInterface
10
+ {
11
+ /**
12
+ * @return Cloud
13
+ */
14
+ public function getCloud();
15
+
16
+ /**
17
+ * @return Credentials
18
+ */
19
+ public function getCredentials();
20
+
21
+ /**
22
+ * @return Transformation
23
+ */
24
+ public function getDefaultTransformation();
25
+
26
+ /**
27
+ * @return boolean
28
+ */
29
+ public function getCdnSubdomainStatus();
30
+
31
+ /**
32
+ * @return string
33
+ */
34
+ public function getUserPlatform();
35
+
36
+ /**
37
+ * @return UploadConfig
38
+ */
39
+ public function getUploadConfig();
40
+
41
+ /**
42
+ * @return boolean
43
+ */
44
+ public function isEnabled();
45
+
46
+ /**
47
+ * @return array
48
+ */
49
+ public function getFormatsToPreserve();
50
+
51
+ /**
52
+ * @param string $file
53
+ *
54
+ * @return string
55
+ */
56
+ public function getMigratedPath($file);
57
+
58
+ /**
59
+ * @return void
60
+ */
61
+ public function enable();
62
+
63
+ /**
64
+ * @return void
65
+ */
66
+ public function disable();
67
+ }
lib/CloudinaryExtension/CredentialValidator.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CloudinaryExtension;
4
+
5
+ class CredentialValidator
6
+ {
7
+ public function validate(Credentials $credentials)
8
+ {
9
+ $signedValidationUrl = (string)Security\SignedConsoleUrl::fromConsoleUrlAndCredentials(
10
+ Security\ConsoleUrl::fromPath("media_library/cms"),
11
+ $credentials
12
+ );
13
+
14
+ $request = new ValidateRemoteUrlRequest($signedValidationUrl);
15
+ return $request->validate();
16
+
17
+ }
18
+ }
lib/CloudinaryExtension/Credentials.php CHANGED
@@ -12,12 +12,17 @@ class Credentials
12
  private $key;
13
  private $secret;
14
 
15
- public function __construct(Key $key,Secret $secret)
16
  {
17
  $this->key = $key;
18
  $this->secret = $secret;
19
  }
20
 
 
 
 
 
 
21
  public function getKey()
22
  {
23
  return $this->key;
12
  private $key;
13
  private $secret;
14
 
15
+ private function __construct(Key $key,Secret $secret)
16
  {
17
  $this->key = $key;
18
  $this->secret = $secret;
19
  }
20
 
21
+ public static function fromKeyAndSecret(Key $key,Secret $secret)
22
+ {
23
+ return new Credentials($key, $secret);
24
+ }
25
+
26
  public function getKey()
27
  {
28
  return $this->key;
lib/CloudinaryExtension/Image.php CHANGED
@@ -2,10 +2,12 @@
2
 
3
  namespace CloudinaryExtension;
4
 
5
- class Image
6
  {
7
  private $imagePath;
 
8
  private $relativePath;
 
9
  private $pathInfo;
10
 
11
  private function __construct($imagePath, $relativePath = '')
@@ -39,7 +41,7 @@ class Image
39
  public function getId()
40
  {
41
  if ($this->relativePath) {
42
- return $this->getRelativeFolder() . DS . $this->pathInfo['filename'];
43
  } else {
44
  return $this->pathInfo['filename'];
45
  }
2
 
3
  namespace CloudinaryExtension;
4
 
5
+ class Image implements ImageInterface
6
  {
7
  private $imagePath;
8
+
9
  private $relativePath;
10
+
11
  private $pathInfo;
12
 
13
  private function __construct($imagePath, $relativePath = '')
41
  public function getId()
42
  {
43
  if ($this->relativePath) {
44
+ return $this->getRelativeFolder() . DIRECTORY_SEPARATOR . $this->pathInfo['filename'];
45
  } else {
46
  return $this->pathInfo['filename'];
47
  }
lib/CloudinaryExtension/Image/ImageFactory.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CloudinaryExtension\Image;
4
+
5
+ use CloudinaryExtension\ConfigurationInterface;
6
+ use CloudinaryExtension\Image\SynchronizationChecker;
7
+ use CloudinaryExtension\Image;
8
+
9
+ class ImageFactory
10
+ {
11
+ /**
12
+ * @var ConfigurationInterface
13
+ */
14
+ private $configuration;
15
+
16
+ /**
17
+ * @var SynchronizationChecker
18
+ */
19
+ private $synchronizationChecker;
20
+
21
+ /**
22
+ * ImageFactory constructor.
23
+ * @param ConfigurationInterface $configuration
24
+ * @param SynchronizationChecker $synchronizationChecker
25
+ */
26
+ public function __construct(ConfigurationInterface $configuration, SynchronizationChecker $synchronizationChecker)
27
+ {
28
+ $this->configuration = $configuration;
29
+ $this->synchronizationChecker = $synchronizationChecker;
30
+ }
31
+
32
+ /**
33
+ * @param $imagePath
34
+ * @return Image
35
+ */
36
+ public function build($imagePath, callable $localPathGenerator)
37
+ {
38
+ $migratedPath = $this->configuration->getMigratedPath($imagePath);
39
+
40
+ if ($this->configuration->isEnabled() && $this->synchronizationChecker->isSynchronized($migratedPath)) {
41
+ return Image::fromPath($imagePath, $migratedPath);
42
+ } else {
43
+ return new LocalImage($localPathGenerator);
44
+ }
45
+ }
46
+ }
lib/CloudinaryExtension/Image/LocalImage.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace CloudinaryExtension\Image;
3
+
4
+ use CloudinaryExtension\ImageInterface;
5
+
6
+ class LocalImage implements ImageInterface
7
+ {
8
+ /**
9
+ * @var callable
10
+ */
11
+ private $localPathGenerator;
12
+
13
+ /**
14
+ * LocalImage constructor.
15
+ */
16
+ public function __construct($localPathGenerator)
17
+ {
18
+ $this->localPathGenerator = $localPathGenerator;
19
+ }
20
+
21
+ public function __toString()
22
+ {
23
+ return call_user_func($this->localPathGenerator);
24
+ }
25
+ }
lib/CloudinaryExtension/Image/Synchronizable.php CHANGED
@@ -4,7 +4,18 @@ namespace CloudinaryExtension\Image;
4
 
5
  interface Synchronizable
6
  {
 
 
 
7
  public function getFilename();
 
 
 
 
8
  public function getRelativePath();
 
 
 
 
9
  public function tagAsSynchronized();
10
  }
4
 
5
  interface Synchronizable
6
  {
7
+ /**
8
+ * @return string
9
+ */
10
  public function getFilename();
11
+
12
+ /**
13
+ * @return string
14
+ */
15
  public function getRelativePath();
16
+
17
+ /**
18
+ * @return void
19
+ */
20
  public function tagAsSynchronized();
21
  }
lib/CloudinaryExtension/Image/SynchronizationChecker.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CloudinaryExtension\Image;
4
+
5
+ interface SynchronizationChecker
6
+ {
7
+ /**
8
+ * @return boolean
9
+ */
10
+ public function isSynchronized($imageName);
11
+ }
lib/CloudinaryExtension/Image/Transformation.php CHANGED
@@ -8,86 +8,89 @@ use CloudinaryExtension\Image\Transformation\FetchFormat;
8
  use CloudinaryExtension\Image\Transformation\Format;
9
  use CloudinaryExtension\Image\Transformation\Gravity;
10
  use CloudinaryExtension\Image\Transformation\Quality;
 
11
 
12
  class Transformation
13
  {
14
  private $gravity;
15
-
16
  private $dimensions;
17
-
18
  private $crop;
19
-
20
  private $fetchFormat;
21
-
22
  private $quality;
23
-
24
  private $format;
25
-
26
  private $dpr;
 
 
27
 
28
  public function __construct()
29
  {
30
  $this->fetchFormat = FetchFormat::fromString(Format::FETCH_FORMAT_AUTO);
31
  $this->crop = 'pad';
32
  $this->format = Format::fromExtension('jpg');
33
- $this->validFormats = array('gif', 'jpg', 'png', 'svg');
 
34
  }
35
 
36
  public function withGravity(Gravity $gravity)
37
  {
38
  $this->gravity = $gravity;
39
- $this->crop = ((string) $gravity) ? 'crop' : 'pad';
40
-
41
  return $this;
42
  }
43
 
44
  public function withDimensions(Dimensions $dimensions)
45
  {
46
  $this->dimensions = $dimensions;
 
 
47
 
 
 
 
48
  return $this;
49
  }
50
 
51
  public function withFetchFormat(FetchFormat $fetchFormat)
52
  {
53
  $this->fetchFormat = $fetchFormat;
54
-
55
  return $this;
56
  }
57
 
58
  public function withFormat(Format $format)
59
  {
60
- if (in_array((string) $format, $this->validFormats)) {
61
  $this->format = $format;
62
  }
63
 
64
  return $this;
65
  }
66
 
 
 
 
 
 
 
67
  public function withQuality(Quality $quality)
68
  {
69
  $this->quality = $quality;
70
-
71
  return $this;
72
  }
73
 
74
  public function withDpr(Dpr $dpr)
75
  {
76
  $this->dpr = $dpr;
77
-
78
  return $this;
79
  }
80
 
81
- public function withCrop(Crop $crop)
82
  {
83
- $this->crop = $crop;
84
-
85
- return $this;
86
  }
87
 
88
- public function withOptimisationDisabled()
89
  {
90
- $this->withFetchFormat(FetchFormat::fromString(''));
91
  return $this;
92
  }
93
 
@@ -99,14 +102,15 @@ class Transformation
99
  public function build()
100
  {
101
  return array(
102
- 'fetch_format' => (string) $this->fetchFormat,
103
- 'quality' => (string) $this->quality,
104
- 'crop' => (string) $this->crop,
105
- 'gravity' => (string) $this->gravity ?: null,
106
  'width' => $this->dimensions ? $this->dimensions->getWidth() : null,
107
  'height' => $this->dimensions ? $this->dimensions->getHeight() : null,
108
- 'format' => (string) $this->format,
109
- 'dpr' => (string) $this->dpr
 
110
  );
111
  }
112
  }
8
  use CloudinaryExtension\Image\Transformation\Format;
9
  use CloudinaryExtension\Image\Transformation\Gravity;
10
  use CloudinaryExtension\Image\Transformation\Quality;
11
+ use CloudinaryExtension\Image\Transformation\Crop;
12
 
13
  class Transformation
14
  {
15
  private $gravity;
 
16
  private $dimensions;
 
17
  private $crop;
 
18
  private $fetchFormat;
 
19
  private $quality;
 
20
  private $format;
 
21
  private $dpr;
22
+ private $validFormats;
23
+ private $flags;
24
 
25
  public function __construct()
26
  {
27
  $this->fetchFormat = FetchFormat::fromString(Format::FETCH_FORMAT_AUTO);
28
  $this->crop = 'pad';
29
  $this->format = Format::fromExtension('jpg');
30
+ $this->validFormats = array('gif', 'jpg', 'png', 'svg', 'webp');
31
+ $this->flags = [];
32
  }
33
 
34
  public function withGravity(Gravity $gravity)
35
  {
36
  $this->gravity = $gravity;
37
+ $this->crop = ((string)$gravity) ? 'crop' : 'pad';
 
38
  return $this;
39
  }
40
 
41
  public function withDimensions(Dimensions $dimensions)
42
  {
43
  $this->dimensions = $dimensions;
44
+ return $this;
45
+ }
46
 
47
+ public function withCrop(Crop $crop)
48
+ {
49
+ $this->crop = $crop;
50
  return $this;
51
  }
52
 
53
  public function withFetchFormat(FetchFormat $fetchFormat)
54
  {
55
  $this->fetchFormat = $fetchFormat;
 
56
  return $this;
57
  }
58
 
59
  public function withFormat(Format $format)
60
  {
61
+ if (in_array((string)$format, $this->validFormats)) {
62
  $this->format = $format;
63
  }
64
 
65
  return $this;
66
  }
67
 
68
+ public function withoutFormat()
69
+ {
70
+ $this->format = null;
71
+ return $this;
72
+ }
73
+
74
  public function withQuality(Quality $quality)
75
  {
76
  $this->quality = $quality;
 
77
  return $this;
78
  }
79
 
80
  public function withDpr(Dpr $dpr)
81
  {
82
  $this->dpr = $dpr;
 
83
  return $this;
84
  }
85
 
86
+ public function withOptimisationDisabled()
87
  {
88
+ return $this->withFetchFormat(FetchFormat::fromString(''));
 
 
89
  }
90
 
91
+ public function addFlags(array $flags = [])
92
  {
93
+ $this->flags += $flags;
94
  return $this;
95
  }
96
 
102
  public function build()
103
  {
104
  return array(
105
+ 'fetch_format' => (string)$this->fetchFormat,
106
+ 'quality' => (string)$this->quality,
107
+ 'crop' => (string)$this->crop,
108
+ 'gravity' => (string)$this->gravity ?: null,
109
  'width' => $this->dimensions ? $this->dimensions->getWidth() : null,
110
  'height' => $this->dimensions ? $this->dimensions->getHeight() : null,
111
+ 'format' => (string)$this->format,
112
+ 'dpr' => (string)$this->dpr,
113
+ 'flags' => $this->flags
114
  );
115
  }
116
  }
lib/CloudinaryExtension/Image/Transformation/Crop.php CHANGED
@@ -4,6 +4,9 @@ namespace CloudinaryExtension\Image\Transformation;
4
 
5
  class Crop
6
  {
 
 
 
7
  private $value;
8
 
9
  private function __construct($value)
@@ -16,6 +19,16 @@ class Crop
16
  return new Crop($value);
17
  }
18
 
 
 
 
 
 
 
 
 
 
 
19
  public function __toString()
20
  {
21
  return $this->value;
4
 
5
  class Crop
6
  {
7
+ const PAD = 'pad';
8
+ const FIT = 'fit';
9
+
10
  private $value;
11
 
12
  private function __construct($value)
19
  return new Crop($value);
20
  }
21
 
22
+ public static function pad()
23
+ {
24
+ return new Crop(self::PAD);
25
+ }
26
+
27
+ public static function fit()
28
+ {
29
+ return new Crop(self::FIT);
30
+ }
31
+
32
  public function __toString()
33
  {
34
  return $this->value;
lib/CloudinaryExtension/Image/Transformation/Dimensions.php CHANGED
@@ -36,7 +36,6 @@ class Dimensions
36
  } else if (!$dimensions->getHeight()) {
37
  return Dimensions::square($dimensions->getWidth());
38
  }
39
-
40
  return $dimensions;
41
  }
42
 
36
  } else if (!$dimensions->getHeight()) {
37
  return Dimensions::square($dimensions->getWidth());
38
  }
 
39
  return $dimensions;
40
  }
41
 
lib/CloudinaryExtension/ImageInterface.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CloudinaryExtension;
4
+
5
+ interface ImageInterface
6
+ {
7
+ /**
8
+ * @return string
9
+ */
10
+ public function __toString();
11
+ }
lib/CloudinaryExtension/ImageProvider.php CHANGED
@@ -7,7 +7,8 @@ use CloudinaryExtension\Image\Transformation;
7
  interface ImageProvider
8
  {
9
  public function upload(Image $image);
10
- public function transformImage(Image $image, Transformation $transformation);
11
- public function deleteImage(Image $image);
 
12
  public function validateCredentials();
13
  }
7
  interface ImageProvider
8
  {
9
  public function upload(Image $image);
10
+ public function retrieveTransformed(Image $image, Transformation $transformation);
11
+ public function retrieve(Image $image);
12
+ public function delete(Image $image);
13
  public function validateCredentials();
14
  }
lib/CloudinaryExtension/Security/CloudinaryEnvironmentVariable.php CHANGED
@@ -8,16 +8,17 @@ use CloudinaryExtension\Credentials;
8
 
9
  class CloudinaryEnvironmentVariable implements EnvironmentVariable
10
  {
 
11
  private $environmentVariable;
12
 
13
  private function __construct($environmentVariable)
14
  {
15
  $this->environmentVariable = (string)$environmentVariable;
16
- $cloudinaryUrl = str_replace('CLOUDINARY_URL=', '', $environmentVariable);
17
- if ($this->isUrlValid($cloudinaryUrl)) {
18
- Cloudinary::config_from_url($cloudinaryUrl);
 
19
  }
20
-
21
  }
22
 
23
  public static function fromString($environmentVariable)
@@ -32,7 +33,7 @@ class CloudinaryEnvironmentVariable implements EnvironmentVariable
32
 
33
  public function getCredentials()
34
  {
35
- return new Credentials(
36
  Key::fromString(Cloudinary::config_get('api_key')),
37
  Secret::fromString(Cloudinary::config_get('api_secret'))
38
  );
@@ -43,8 +44,4 @@ class CloudinaryEnvironmentVariable implements EnvironmentVariable
43
  return $this->environmentVariable;
44
  }
45
 
46
- private function isUrlValid($cloudinaryUrl)
47
- {
48
- return parse_url($cloudinaryUrl, PHP_URL_SCHEME) == "cloudinary";
49
- }
50
- }
8
 
9
  class CloudinaryEnvironmentVariable implements EnvironmentVariable
10
  {
11
+
12
  private $environmentVariable;
13
 
14
  private function __construct($environmentVariable)
15
  {
16
  $this->environmentVariable = (string)$environmentVariable;
17
+ try {
18
+ Cloudinary::config_from_url(str_replace('CLOUDINARY_URL=', '', $environmentVariable));
19
+ } catch (\Exception $e){
20
+ throw new \CloudinaryExtension\Exception\InvalidCredentials('Cloudinary config creation from environment variable failed');
21
  }
 
22
  }
23
 
24
  public static function fromString($environmentVariable)
33
 
34
  public function getCredentials()
35
  {
36
+ return Credentials::fromKeyAndSecret(
37
  Key::fromString(Cloudinary::config_get('api_key')),
38
  Secret::fromString(Cloudinary::config_get('api_secret'))
39
  );
44
  return $this->environmentVariable;
45
  }
46
 
47
+ }
 
 
 
 
lib/CloudinaryExtension/SynchroniseAssetsRepositoryInterface.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CloudinaryExtension;
4
+
5
+ interface SynchroniseAssetsRepositoryInterface
6
+ {
7
+ /**
8
+ * @param string $imagePath
9
+ * @return mixed
10
+ */
11
+ public function saveAsSynchronized($imagePath);
12
+
13
+ /**
14
+ * @param string g$imagePath
15
+ * @return mixed
16
+ */
17
+ public function removeSynchronised($imagePath);
18
+ }
lib/CloudinaryExtension/UploadConfig.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by PhpStorm.
4
+ * User: danielk
5
+ * Date: 19/01/16
6
+ * Time: 14:30
7
+ */
8
+
9
+ namespace CloudinaryExtension;
10
+
11
+
12
+ class UploadConfig
13
+ {
14
+ /**
15
+ * @var boolean
16
+ */
17
+ private $useFilename;
18
+
19
+ /**
20
+ * @var boolean
21
+ */
22
+ private $uniqueFilename;
23
+
24
+ /**
25
+ * @var boolean
26
+ */
27
+ private $overwrite;
28
+
29
+ private function __construct($useFilename, $uniqueFilename, $overwrite)
30
+ {
31
+ $this->useFilename = $useFilename;
32
+ $this->uniqueFilename = $uniqueFilename;
33
+ $this->overwrite = $overwrite;
34
+ }
35
+
36
+ public static function fromBooleanValues($useFilename, $uniqueFilename, $overwrite)
37
+ {
38
+ return new UploadConfig($useFilename, $uniqueFilename, $overwrite);
39
+ }
40
+
41
+ /**
42
+ * @return boolean
43
+ */
44
+ public function useFilename()
45
+ {
46
+ return $this->useFilename;
47
+ }
48
+
49
+ /**
50
+ * @return boolean
51
+ */
52
+ public function uniqueFilename()
53
+ {
54
+ return $this->uniqueFilename;
55
+ }
56
+
57
+ /**
58
+ * @return boolean
59
+ */
60
+ public function overwrite()
61
+ {
62
+ return $this->overwrite;
63
+ }
64
+
65
+ public function toArray()
66
+ {
67
+ return [
68
+ "use_filename" => $this->useFilename,
69
+ "unique_filename" => $this->uniqueFilename,
70
+ "overwrite" => $this->overwrite,
71
+ ] ;
72
+ }
73
+ }
lib/CloudinaryExtension/UploadResponseValidator.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CloudinaryExtension;
4
+
5
+ use CloudinaryExtension\Exception\MigrationError;
6
+
7
+ class UploadResponseValidator
8
+ {
9
+ public function validateResponse($image, $uploadResponse)
10
+ {
11
+ if ($uploadResponse['existing'] == 1) {
12
+ MigrationError::throwWith($image, MigrationError::CODE_FILE_ALREADY_EXISTS);
13
+ }
14
+
15
+ return $uploadResponse;
16
+ }
17
+ }
lib/CloudinaryExtension/UrlGenerator.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CloudinaryExtension;
4
+
5
+ use CloudinaryExtension\ImageInterface;
6
+ use CloudinaryExtension\Image\LocalImage;
7
+ use CloudinaryExtension\Image\Transformation;
8
+ use CloudinaryExtension\Image\Transformation\Dimensions;
9
+
10
+ class UrlGenerator
11
+ {
12
+ /**
13
+ * @var ConfigurationInterface
14
+ */
15
+ private $configuration;
16
+
17
+ /**
18
+ * @var ImageProvider
19
+ */
20
+ private $imageProvider;
21
+
22
+ /**
23
+ * @param ConfigurationInterface $configuration
24
+ * @param ImageProvider $imageProvider
25
+ */
26
+ public function __construct(ConfigurationInterface $configuration, ImageProvider $imageProvider)
27
+ {
28
+ $this->configuration = $configuration;
29
+ $this->imageProvider = $imageProvider;
30
+ }
31
+
32
+ /**
33
+ * @param ImageInterface $image
34
+ * @param Transformation $transformation
35
+ *
36
+ * @return string
37
+ */
38
+ public function generateFor(ImageInterface $image, Transformation $transformation = null)
39
+ {
40
+ if ($image instanceof LocalImage) {
41
+ return (string)$image;
42
+ }
43
+
44
+ $transformation = clone ($transformation ?: $this->configuration->getDefaultTransformation());
45
+
46
+ if (in_array($image->getExtension(), $this->configuration->getFormatsToPreserve())) {
47
+ $transformation->withoutFormat();
48
+ }
49
+
50
+ return (string)$this->imageProvider->retrieveTransformed($image, $transformation);
51
+ }
52
+
53
+ /**
54
+ * @param Image $image
55
+ * @param Dimensions $dimensions
56
+ *
57
+ * @return string
58
+ */
59
+ public function generateWithDimensions(ImageInterface $image, Dimensions $dimensions)
60
+ {
61
+ $transformation = clone $this->configuration->getDefaultTransformation();
62
+
63
+ return $this->generateFor($image, $transformation->withDimensions($dimensions));
64
+ }
65
+ }
package.xml CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Cloudinary_Cloudinary</name>
4
- <version>1.2.2</version>
5
  <stability>stable</stability>
6
  <license>MIT License (MITL)</license>
7
  <channel>community</channel>
@@ -14,9 +14,9 @@ Release Highlights:&#xD;
14
  &#xD;
15
  * Foldered migration</notes>
16
  <authors><author><name>Cloudinary</name><user>cloudinary</user><email>accounts+magento@cloudinary.com</email></author></authors>
17
- <date>2017-02-27</date>
18
- <time>14:54:24</time>
19
- <contents><target name="magecommunity"><dir name="Cloudinary"><dir name="Cloudinary"><dir name="Block"><dir name="Adminhtml"><dir name="Manage"><file name="Grid.php" hash="b6a05f6ba08c5ba0d08846a7b0a06776"/></dir><file name="Manage.php" hash="c525e34955df149b70d3a7fdde427672"/><dir name="Page"><file name="Menu.php" hash="891d6a4c075ba03c9a20658076c86ad0"/></dir><dir name="System"><dir name="Config"><file name="Signup.php" hash="235c27f236e45900eb94dea0181027cc"/></dir></dir></dir></dir><dir name="Helper"><file name="Autoloader.php" hash="393b3e2fc25e63ca28157152d2542b18"/><dir name="Configuration"><file name="Validation.php" hash="6d17d39ba39f67888701fadf0fe3de62"/></dir><file name="Configuration.php" hash="00e1e81c5a13efd10905f901c2eb3f02"/><file name="Console.php" hash="e4ca7f9bf450b05383def130b2819ce0"/><file name="Data.php" hash="42c9d44f1bbe530e30cf5379846dea65"/><file name="Image.php" hash="fff6ca342ec49809089be01063bcdbe7"/><dir name="Util"><file name="ArrayUtils.php" hash="dbf5b1f86213f6e1ea34b1523b2b9ffe"/></dir></dir><dir name="Model"><dir name="Catalog"><dir name="Product"><file name="Image.php" hash="98ca81f05ab59b4c01ffe36ce86c7803"/><dir name="Media"><file name="Config.php" hash="ff27ccd9fc2becce9feae31ffd1d59e2"/></dir><file name="Media.php" hash="05726616a07d7d08933e9654e6107283"/></dir></dir><dir name="Cms"><dir name="Adminhtml"><dir name="Template"><file name="Filter.php" hash="4ef453061d790fff6b772286e90439f2"/></dir></dir><file name="Synchronisation.php" hash="3bf5d872b6451cf3ce6f83ec92104415"/><dir name="Template"><file name="Filter.php" hash="5ec9589ef22b1e9c88b20c3272d01f8c"/></dir><file name="Uploader.php" hash="6f3923330d573af7d5687aec6120dd12"/><dir name="Wysiwyg"><dir name="Images"><file name="Storage.php" hash="b3eae9a3a4810d9de5ab6a91243d047d"/></dir></dir></dir><file name="CollectionCounter.php" hash="e69953aee5d966a3ec13d33533f017e0"/><file name="Cron.php" hash="13ea00a9e40622912bf46ed367f9a214"/><dir name="Exception"><file name="BadFilePathException.php" hash="68135da8dfe2f0589a531b4bd36e3330"/></dir><file name="Image.php" hash="0377e2c24c5e2f23357a55d744098fda"/><file name="Logger.php" hash="226893f4a59d1431330688f455975d61"/><file name="MagentoFolderTranslator.php" hash="ad11d373bc6e193b689d29f16f5f6480"/><file name="MediaCollectionCounter.php" hash="9f20a1494c52937e85ffd0869892ff78"/><file name="Migration.php" hash="e923053b36d2ab469362b3590935ecfe"/><file name="MigrationError.php" hash="1c91373b020d639ae3fb8acfa099eea0"/><file name="Observer.php" hash="22a8e380ac895894f218e7239560b2e2"/><file name="PreConditionsValidator.php" hash="c6090d025c65b38595101b21dd9673fe"/><dir name="Resource"><dir name="Cms"><dir name="Synchronisation"><file name="Collection.php" hash="117085bb56d3f0db8f3d52f136a8b84b"/></dir></dir><dir name="Media"><dir name="Collection"><file name="Interface.php" hash="1ab399cf089a2d6b0dbaf3f48d35abd7"/></dir><file name="Collection.php" hash="f54d914a6f79c7b3ab51f822bf64de39"/></dir><file name="Migration.php" hash="69a545d0627016afc03ea097641aa749"/><dir name="MigrationError"><file name="Collection.php" hash="3c5ef530b18b4cd7763a610b84cd3d41"/></dir><file name="MigrationError.php" hash="e6de24a80cb0daed6ead44c699dce535"/><dir name="Synchronisation"><file name="Collection.php" hash="8abfc042f7c84f424015e8bc34dab0dc"/></dir><file name="Synchronisation.php" hash="5b721d854d8f89bc3310e46081be7153"/></dir><file name="SyncedImages.php" hash="b76c320d8523450a50d9d05526cf8ca8"/><file name="Synchronisation.php" hash="34e5a1746a91b8f6676a18772c433047"/><file name="SynchronisedMediaUnifier.php" hash="dd47a04cc2eaa2a81b6dce27f22301f2"/><dir name="System"><dir name="Config"><dir name="Source"><dir name="Dropdown"><file name="Dpr.php" hash="2b9bfd5f836dbdb5d7224d298264f540"/><file name="Gravity.php" hash="c241498e2093640892170673cd7550cd"/><file name="Quality.php" hash="e0c5902f5c36c96fb8a8ba7cc741ce1f"/></dir></dir></dir></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="CloudinaryController.php" hash="4e23d18f841d78abf8443de71a951ed6"/></dir></dir><dir name="data"><dir name="cloudinary_setup"><file name="data-upgrade-0.1.0-0.1.1.php" hash="4c6ce6cd9ab0d94654afb4a398fb3d6c"/><file name="data-upgrade-1.1.2-1.1.3.php" hash="fe2026874346017303a8f41a9d0d6c0d"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="46e365e2f4b1d543aad248dfcfb99c50"/><file name="config.xml" hash="172e4f86b89fdf5b348640c09efeed41"/><file name="system.xml" hash="a4ae810df3e6587625d395985b79ee83"/></dir><dir name="sql"><dir name="cloudinary_setup"><file name="install-0.1.0.php" hash="55d93b3dab573c2a932edbb5a2fa4865"/><file name="upgrade-0.1.0-0.1.1.php" hash="6c8d430fbf7b9714586b67db3d455008"/><file name="upgrade-1.1.3-1.1.4.php" hash="d6314fc1843b2061d0d04ae60c4d8091"/><file name="upgrade-1.1.4-1.1.5.php" hash="5b035e4b600cbbc743e9ff6a7b505230"/><file name="upgrade-1.1.5-1.1.6.php" hash="323c5e50635018be420cf524072f6a92"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Cloudinary_Cloudinary.xml" hash="9337962a4ccf8a43164d5d71dfd2d756"/></dir></target><target name="magelib"><dir name="CloudinaryExtension"><file name="Cloud.php" hash="59b0debf9ae297e4e824e39ba819b1d1"/><file name="CloudinaryImageProvider.php" hash="e75c77d6a169fec4c96c875de477fc75"/><file name="Configuration.php" hash="b4ece3c87e2aa2af83b8362311ad7850"/><file name="Credentials.php" hash="ccd2da6450df39f3218cfd64ac47103f"/><dir name="Exception"><file name="InvalidCredentials.php" hash="abecc635a25f6c9896c605ad16e1f7d7"/><file name="MigrationError.php" hash="1f37d28be668edb805e46fd207c72fd9"/></dir><file name="FolderTranslator.php" hash="19a335acf751d67bd7efe46829602490"/><dir name="Image"><file name="Synchronizable.php" hash="38a6b9db4cfc3fde3e94db5b35a92bf8"/><dir name="Transformation"><file name="Crop.php" hash="24fd938f0dc3adf30a7e101b72015bed"/><file name="Dimensions.php" hash="a628855c5bec847a290294e3e3308c7d"/><file name="Dpr.php" hash="f78cd1bfabaf3088ca8d4af972bfd453"/><file name="FetchFormat.php" hash="b81c62dd756dee4ad085ee6f0a83356a"/><file name="Format.php" hash="ab8ea9b6a8c813a24f23b079ea6236da"/><file name="Gravity.php" hash="c1c2adf4dbbeaa6b06d67d2014300559"/><file name="Quality.php" hash="23a857f3910aecf6e45645194ff7f54e"/></dir><file name="Transformation.php" hash="55c5cc1e41047b63c5f7845dab49cb5e"/></dir><file name="Image.php" hash="ad13d525919cde14c163a72f0c348ab7"/><file name="ImageProvider.php" hash="f4eb49d5e1e4c1728a5dde29b6b5a3fa"/><dir name="Migration"><file name="BatchUploader.php" hash="6aae7adc0f4f99dab7836976d0a06d75"/><file name="Logger.php" hash="648b47bb065de0c81b386ac300b4f9a3"/><file name="Queue.php" hash="add92864192b0950c29c91ffe5e5a3ee"/><file name="SynchronizedMediaRepository.php" hash="9e7e1dae66b40ce991b0e86ecdff4c24"/><file name="Task.php" hash="ac11d06c531d48b38cf88f6e8f2bdc19"/></dir><dir name="Security"><file name="ApiSignature.php" hash="049c7db2684ec2a6cf5bb4efcd064951"/><file name="CloudinaryEnvironmentVariable.php" hash="3263d4ae4a497e10f301f065017df8a6"/><file name="ConsoleUrl.php" hash="4e748cfe0f5a0aeab2307c623179c6f9"/><file name="EnvironmentVariable.php" hash="297fa60b819ffc028b9a32dae6eef63d"/><file name="Key.php" hash="ac3a50b59f2a7db1edcf30386759c7ec"/><file name="Secret.php" hash="b1010679976575d57752dbb07f1b94ed"/><file name="SignedConsoleUrl.php" hash="791e1f1080be23423c2ad87f431f6221"/></dir><file name="ValidateRemoteUrlRequest.php" hash="c2e2eb712e5293ad508a23610dfbbd6d"/></dir><dir name="Cloudinary"><file name="Api.php" hash="f046d7b1db05efb0997a458fb610a09f"/><file name="Cloudinary.php" hash="debd99bcb8076cf250f59a8925f8ba1b"/><file name="CloudinaryField.php" hash="411714580d21b58115ab07737367173a"/><file name="Helpers.php" hash="63035ebeaa237bd69dcad2d756a00b44"/><file name="PreloadedFile.php" hash="73cc9e276f96553814f05eae592d11ee"/><file name="Uploader.php" hash="2fbea92cd3e409ec62e61ecbf36e6f2c"/><file name="cacert.pem" hash="c4290b9deb70d0bef2f88b67fc68c8ec"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="cloudinary"><file name="cloudinary.xml" hash="8cf333ec4b49c684ea6a209061f5128b"/></dir></dir><dir name="template"><dir name="cloudinary"><file name="manage.phtml" hash="080ea639f961da33a5a3d2429da13edc"/><dir name="system"><dir name="config"><file name="signup.phtml" hash="2a0e06990eb542f22531ac2ebb5996f5"/></dir></dir></dir></dir></dir></dir></dir></target></contents>
20
  <compatible/>
21
  <dependencies><required><php><min>5.4.0</min><max>7.0.0</max></php></required></dependencies>
22
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Cloudinary_Cloudinary</name>
4
+ <version>2.0.0</version>
5
  <stability>stable</stability>
6
  <license>MIT License (MITL)</license>
7
  <channel>community</channel>
14
  &#xD;
15
  * Foldered migration</notes>
16
  <authors><author><name>Cloudinary</name><user>cloudinary</user><email>accounts+magento@cloudinary.com</email></author></authors>
17
+ <date>2017-04-04</date>
18
+ <time>11:15:39</time>
19
+ <contents><target name="magecommunity"><dir name="Cloudinary"><dir name="Cloudinary"><dir name="Block"><dir name="Adminhtml"><dir name="Manage"><file name="Grid.php" hash="b6a05f6ba08c5ba0d08846a7b0a06776"/></dir><file name="Manage.php" hash="7163a4f6e66444182b777d1d18210a5d"/><dir name="Page"><file name="Menu.php" hash="891d6a4c075ba03c9a20658076c86ad0"/></dir><dir name="System"><dir name="Config"><file name="Signup.php" hash="ed6accbe7a4ce16bb0679eaf0c2dbb22"/></dir></dir></dir></dir><dir name="Helper"><file name="Autoloader.php" hash="393b3e2fc25e63ca28157152d2542b18"/><file name="Console.php" hash="7c909e3226c51c05d6da1f6ff9cbbfc9"/><file name="Data.php" hash="42c9d44f1bbe530e30cf5379846dea65"/><file name="Image.php" hash="af1c1d734793d6b08feaa7e1abd591d0"/></dir><dir name="Model"><dir name="Catalog"><dir name="Product"><file name="Image.php" hash="b5d14bcb836158890152c9fed191f8bc"/><dir name="Media"><file name="Config.php" hash="c2dbac447d4a22c920c19b0d4eb2672e"/></dir><file name="Media.php" hash="05726616a07d7d08933e9654e6107283"/></dir></dir><dir name="Cms"><dir name="Adminhtml"><dir name="Template"><file name="Filter.php" hash="792893f6b4e884a8e42847d457a6068c"/></dir></dir><file name="Synchronisation.php" hash="8d830a18f169a0ff5f7d865e3afcb710"/><dir name="Template"><file name="Filter.php" hash="c3fe64f98128043de13e92156a26ab02"/></dir><file name="Uploader.php" hash="021195c01a7e6fd9e72c5a30ebd11554"/><dir name="Wysiwyg"><dir name="Images"><file name="Storage.php" hash="0d23e557d6db06308886d9307fe92665"/></dir></dir></dir><file name="CollectionCounter.php" hash="e69953aee5d966a3ec13d33533f017e0"/><file name="Configuration.php" hash="e230cbc40b45c5b15114e4985720e7ab"/><file name="Cron.php" hash="4d9ef170bc0398c9f6fa33ff8fc583d6"/><dir name="Exception"><file name="BadFilePathException.php" hash="68135da8dfe2f0589a531b4bd36e3330"/></dir><file name="Image.php" hash="5a833696668a94286a8d2f38b1869531"/><file name="Logger.php" hash="ab5e45b0769c9dbee33025a202a87fee"/><file name="MagentoFolderTranslator.php" hash="37219fc1804d6ad8d1686af8509e1963"/><file name="Migration.php" hash="e923053b36d2ab469362b3590935ecfe"/><file name="MigrationError.php" hash="1c91373b020d639ae3fb8acfa099eea0"/><file name="Observer.php" hash="e9934939e56d87b732aa7a4f89c318f7"/><dir name="Resource"><dir name="Cms"><dir name="Synchronisation"><file name="Collection.php" hash="0ca88440351d209d99972f9e44794bdc"/></dir></dir><dir name="Media"><file name="Collection.php" hash="f54d914a6f79c7b3ab51f822bf64de39"/></dir><file name="Migration.php" hash="69a545d0627016afc03ea097641aa749"/><dir name="MigrationError"><file name="Collection.php" hash="3c5ef530b18b4cd7763a610b84cd3d41"/></dir><file name="MigrationError.php" hash="e6de24a80cb0daed6ead44c699dce535"/><dir name="Synchronisation"><file name="Collection.php" hash="1868b1d1a33a7343162c56f86235623b"/></dir><file name="Synchronisation.php" hash="5b721d854d8f89bc3310e46081be7153"/></dir><file name="Synchronisation.php" hash="05414c5959efc7656b8e101617005d9a"/><file name="SynchronisedMediaUnifier.php" hash="dd47a04cc2eaa2a81b6dce27f22301f2"/><file name="SynchronizationChecker.php" hash="fd2a1fd763b7f5b895a392b48e209f00"/><dir name="System"><dir name="Config"><dir name="Source"><dir name="Dropdown"><file name="Dpr.php" hash="2b9bfd5f836dbdb5d7224d298264f540"/><file name="Gravity.php" hash="c241498e2093640892170673cd7550cd"/><file name="Quality.php" hash="e0c5902f5c36c96fb8a8ba7cc741ce1f"/></dir></dir></dir></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="CloudinaryController.php" hash="3d3a47e88dd9368278277be797bf100c"/></dir></dir><dir name="data"><dir name="cloudinary_setup"><file name="data-upgrade-0.1.0-0.1.1.php" hash="4c6ce6cd9ab0d94654afb4a398fb3d6c"/><file name="data-upgrade-1.1.2-1.1.3.php" hash="fe2026874346017303a8f41a9d0d6c0d"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="46e365e2f4b1d543aad248dfcfb99c50"/><file name="config.xml" hash="3c6e8fb194fefb55b4245ff36b3f8cea"/><file name="system.xml" hash="a4ae810df3e6587625d395985b79ee83"/></dir><dir name="sql"><dir name="cloudinary_setup"><file name="install-0.1.0.php" hash="55d93b3dab573c2a932edbb5a2fa4865"/><file name="upgrade-0.1.0-0.1.1.php" hash="6c8d430fbf7b9714586b67db3d455008"/><file name="upgrade-1.1.3-1.1.4.php" hash="d6314fc1843b2061d0d04ae60c4d8091"/><file name="upgrade-1.1.4-1.1.5.php" hash="5b035e4b600cbbc743e9ff6a7b505230"/><file name="upgrade-1.1.5-1.1.6.php" hash="323c5e50635018be420cf524072f6a92"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Cloudinary_Cloudinary.xml" hash="9337962a4ccf8a43164d5d71dfd2d756"/></dir></target><target name="magelib"><dir name="CloudinaryExtension"><file name="Cloud.php" hash="59b0debf9ae297e4e824e39ba819b1d1"/><file name="CloudinaryImageManager.php" hash="a5037ca0a15c864ffb5809e5be0fa507"/><file name="CloudinaryImageProvider.php" hash="53fc1d86819319c451198a753c0dfd38"/><file name="ConfigurationBuilder.php" hash="c8832b207d9228ef14b3c74100ee74c4"/><file name="ConfigurationInterface.php" hash="4129b1e282fb0000b78946f07baa740c"/><file name="CredentialValidator.php" hash="965b0bd024f668aabcc9f30ef2e3c240"/><file name="Credentials.php" hash="71054eb4af7b6496608ffd14912bdbe4"/><dir name="Exception"><file name="InvalidCredentials.php" hash="abecc635a25f6c9896c605ad16e1f7d7"/><file name="MigrationError.php" hash="1f37d28be668edb805e46fd207c72fd9"/></dir><file name="FolderTranslator.php" hash="19a335acf751d67bd7efe46829602490"/><dir name="Image"><file name="ImageFactory.php" hash="0a2e066331584d33a9c4ec03787fd6e5"/><file name="LocalImage.php" hash="ab9b814b1a006baf05b9904af3ebce74"/><file name="Synchronizable.php" hash="b842f71ed25718838233207b7748f1bf"/><file name="SynchronizationChecker.php" hash="f2c45545766a81fede68138cf84dd1af"/><dir name="Transformation"><file name="Crop.php" hash="84e57281780a57326c938ac776641e8b"/><file name="Dimensions.php" hash="86a36c564aa41a08da2cf383d611c060"/><file name="Dpr.php" hash="f78cd1bfabaf3088ca8d4af972bfd453"/><file name="FetchFormat.php" hash="b81c62dd756dee4ad085ee6f0a83356a"/><file name="Format.php" hash="ab8ea9b6a8c813a24f23b079ea6236da"/><file name="Gravity.php" hash="c1c2adf4dbbeaa6b06d67d2014300559"/><file name="Quality.php" hash="23a857f3910aecf6e45645194ff7f54e"/></dir><file name="Transformation.php" hash="a6281e9e7535ddc01729585ffc8bb036"/></dir><file name="Image.php" hash="896d409ebebf8af12dfe0225b21f4c83"/><file name="ImageInterface.php" hash="4a7c7e39d7fda0b0fa99affcac78ec8c"/><file name="ImageProvider.php" hash="a615c472cdc8a6ad7d887133db35c262"/><dir name="Migration"><file name="BatchUploader.php" hash="6aae7adc0f4f99dab7836976d0a06d75"/><file name="Logger.php" hash="648b47bb065de0c81b386ac300b4f9a3"/><file name="Queue.php" hash="add92864192b0950c29c91ffe5e5a3ee"/><file name="SynchronizedMediaRepository.php" hash="9e7e1dae66b40ce991b0e86ecdff4c24"/><file name="Task.php" hash="ac11d06c531d48b38cf88f6e8f2bdc19"/></dir><dir name="Security"><file name="ApiSignature.php" hash="049c7db2684ec2a6cf5bb4efcd064951"/><file name="CloudinaryEnvironmentVariable.php" hash="418af61bdbcfef955df29ac47c54415b"/><file name="ConsoleUrl.php" hash="4e748cfe0f5a0aeab2307c623179c6f9"/><file name="EnvironmentVariable.php" hash="297fa60b819ffc028b9a32dae6eef63d"/><file name="Key.php" hash="ac3a50b59f2a7db1edcf30386759c7ec"/><file name="Secret.php" hash="b1010679976575d57752dbb07f1b94ed"/><file name="SignedConsoleUrl.php" hash="791e1f1080be23423c2ad87f431f6221"/></dir><file name="SynchroniseAssetsRepositoryInterface.php" hash="e0d8e270ae2c74214e82e53e04e3dc0f"/><file name="UploadConfig.php" hash="a68f1ea7b84574ec36a8d2fac9bd6054"/><file name="UploadResponseValidator.php" hash="9fc81798b1c1319b04c71079d8de2bd5"/><file name="UrlGenerator.php" hash="4f4f40a76cbea2efa239084bdf0f8a65"/><file name="ValidateRemoteUrlRequest.php" hash="c2e2eb712e5293ad508a23610dfbbd6d"/></dir><dir name="Cloudinary"><file name="Api.php" hash="d71322346c3625db7c3563cdad191e8c"/><file name="AuthToken.php" hash="bec8b856baf85d89a249c932c3eba39f"/><file name="Cloudinary.php" hash="f2ec7b7bc8fc7c978f7773c3d4ccc5dd"/><file name="CloudinaryField.php" hash="411714580d21b58115ab07737367173a"/><file name="Helpers.php" hash="4db8371fc84d34be49c8ea04eee7d6eb"/><file name="PreloadedFile.php" hash="73cc9e276f96553814f05eae592d11ee"/><file name="Uploader.php" hash="eae92a330d19654028a8d16410616421"/><file name="cacert.pem" hash="c4290b9deb70d0bef2f88b67fc68c8ec"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="cloudinary"><file name="cloudinary.xml" hash="8cf333ec4b49c684ea6a209061f5128b"/></dir></dir><dir name="template"><dir name="cloudinary"><file name="manage.phtml" hash="080ea639f961da33a5a3d2429da13edc"/><dir name="system"><dir name="config"><file name="signup.phtml" hash="2a0e06990eb542f22531ac2ebb5996f5"/></dir></dir></dir></dir></dir></dir></dir></target></contents>
20
  <compatible/>
21
  <dependencies><required><php><min>5.4.0</min><max>7.0.0</max></php></required></dependencies>
22
  </package>