MVentory_CDN - Version 1.0.0

Version Notes

Initial release

Download this release

Release Info

Developer Anatoly A. Kazantsev
Extension MVentory_CDN
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

app/code/community/MVentory/CDN/Block/System/Config/Form/Field/Placeholders.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/CDN
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Button for placeholders uploading
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_CDN_Block_System_Config_Form_Field_Placeholders
23
+ extends Mage_Adminhtml_Block_System_Config_Form_Field {
24
+
25
+ protected function _getElementHtml (Varien_Data_Form_Element_Abstract $elem) {
26
+ $website = $this
27
+ ->getRequest()
28
+ ->getParam('website', '');
29
+
30
+ $data = array(
31
+ 'label' => $this->__('Upload placeholders to CDN'),
32
+ 'onclick' => 'setLocation(\''
33
+ . $this->getUrl('cdn/placeholders/upload/',
34
+ array('website' => $website))
35
+ . '\')'
36
+ );
37
+
38
+ return $this
39
+ ->getLayout()
40
+ ->createBlock('adminhtml/widget_button')
41
+ ->setData($data)
42
+ ->toHtml();
43
+ }
44
+ }
app/code/community/MVentory/CDN/Helper/Data.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/CDN
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Data helper
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_CDN_Helper_Data extends Mage_Core_Helper_Abstract {
23
+
24
+ private $_prefix = null;
25
+ private $_productHelper = null;
26
+
27
+ function __construct () {
28
+
29
+ //Load product helper from MVentory_Tm if it's installed and is activated
30
+ //The helper is used to get correct website for the product when MVentory_Tm
31
+ //extension is used
32
+ if ($this->isModuleEnabled('MVentory_Tm'))
33
+ $this->_productHelper = Mage::helper('mventory_tm/product');
34
+ }
35
+
36
+ /**
37
+ * Downloads image from S3 by its absolute path on FS.
38
+ *
39
+ * @param string $path Absolute or relative path to image
40
+ * @param int|string|Mage_Core_Model_Website Website for settings
41
+ * @param string $size Image size ('full' size will be used if null)
42
+ *
43
+ * @return string|bool Return absolute path to downloaded file or false
44
+ * if error occured
45
+ */
46
+ public function download ($path, $website = null, $size = null) {
47
+ $s3 = $this->_getS3($website);
48
+
49
+ $object = $this->_getObject($path, $size);
50
+
51
+ if (!file_exists(dirname($path)))
52
+ mkdir(dirname($path), 0777, true);
53
+
54
+ return $s3->getObjectStream($object, $path) ? $path : false;
55
+ }
56
+
57
+ /**
58
+ * Uploads image to S3. Uses absolute path to image for S3 object name
59
+ *
60
+ * @param string $from Absolute path to source image
61
+ * @param string $path Absolute or relative path to create S3 object name
62
+ * @param int|string|Mage_Core_Model_Website Website for settings
63
+ * @param string $size Image size ('full' size will be used if null)
64
+ *
65
+ * @return bool
66
+ */
67
+ public function upload ($from, $path, $website = null, $size = null) {
68
+ //Prepare meta data for uploading. All uploaded images are public
69
+ $meta = array(Zend_Service_Amazon_S3::S3_ACL_HEADER
70
+ => Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ);
71
+
72
+ return $this
73
+ ->_getS3($website)
74
+ ->putFileStream($from, $this->_getObject($path, $size), $meta);
75
+ }
76
+
77
+ /**
78
+ * Returns configured S3 object.
79
+ * Uses passed website or wesbite which current product is asssigned to
80
+ * or current website to get wesbite's prefix on S3
81
+ *
82
+ * @param int|string|Mage_Core_Model_Website Website for settings
83
+ *
84
+ * @return Zend_Service_Amazon_S3
85
+ */
86
+ protected function _getS3 ($website = null) {
87
+ $store = ($this->_productHelper && $website === null)
88
+ ? $this
89
+ ->_productHelper
90
+ ->getWebsite()
91
+ ->getDefaultStore()
92
+ : Mage::app()->getWebsite($website)->getDefaultStore();
93
+
94
+ $accessKey = $store->getConfig(MVentory_CDN_Model_Config::ACCESS_KEY);
95
+ $secretKey = $store->getConfig(MVentory_CDN_Model_Config::SECRET_KEY);
96
+ $bucket = $store->getConfig(MVentory_CDN_Model_Config::BUCKET);
97
+ $prefix = $store->getConfig(MVentory_CDN_Model_Config::PREFIX);
98
+
99
+ $this->_prefix = $bucket . '/' . $prefix . '/';
100
+
101
+ return new Zend_Service_Amazon_S3($accessKey, $secretKey);
102
+ }
103
+
104
+ /**
105
+ * Build name of S3 object from the absolute path of image
106
+ *
107
+ * @param string $path Absolute or relative path to image. The parameter
108
+ * will be updated with absolute path
109
+ * if relative was given
110
+ * @param string $size Image size ('full' size will be used if null)
111
+ *
112
+ * $return string Name of S3 object
113
+ */
114
+ protected function _getObject (&$path, $size = null) {
115
+ $config = Mage::getSingleton('catalog/product_media_config');
116
+
117
+ $imagePath = str_replace($config->getMediaPath($size), '', $path);
118
+
119
+ if (strpos($imagePath, '/') !== 0)
120
+ $imagePath = '/' . $imagePath;
121
+
122
+ if ($imagePath == $path)
123
+ $path = $config->getMediaPath($size . $path);
124
+
125
+ return $this->_prefix . ($size ? $size : 'full') . $imagePath;
126
+ }
127
+ }
app/code/community/MVentory/CDN/Helper/Mage/Catalog/Image.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/CDN
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Catalog image helper
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_CDN_Helper_Mage_Catalog_Image extends Mage_Catalog_Helper_Image {
23
+
24
+ private $_productHelper = null;
25
+
26
+ function __construct () {
27
+
28
+ //Load product helper from MVentory_Tm if it's installed and is activated
29
+ //The helper is used to get correct website for the product when MVentory_Tm
30
+ //extension is used
31
+ if ($this->isModuleEnabled('MVentory_Tm'))
32
+ $this->_productHelper = Mage::helper('mventory_tm/product');
33
+ }
34
+
35
+ /**
36
+ * Initialize Helper to work with Image
37
+ *
38
+ * @param Mage_Catalog_Model_Product $product
39
+ * @param string $attributeName
40
+ * @param mixed $imageFile
41
+ * @return Mage_Catalog_Helper_Image
42
+ */
43
+ public function init (Mage_Catalog_Model_Product $product, $attributeName,
44
+ $imageFile = null) {
45
+
46
+ $this->_reset();
47
+
48
+ $this->_setModel(new Varien_Object());
49
+
50
+ $this->_getModel()->setDestinationSubdir($attributeName);
51
+ $this->setProduct($product);
52
+
53
+ $path = 'design/watermark/' . $attributeName . '_';
54
+
55
+ $this->watermark(
56
+ Mage::getStoreConfig($path . 'image'),
57
+ Mage::getStoreConfig($path . 'position'),
58
+ Mage::getStoreConfig($path . 'size'),
59
+ Mage::getStoreConfig($path . 'imageOpacity')
60
+ );
61
+
62
+ if ($imageFile)
63
+ $this->setImageFile($imageFile);
64
+
65
+ return $this;
66
+ }
67
+
68
+ /**
69
+ * Retrieve original image height
70
+ *
71
+ * @return int|null
72
+ */
73
+ public function getOriginalHeight () {
74
+ return null;
75
+ }
76
+
77
+ /**
78
+ * Retrieve original image width
79
+ *
80
+ * @return int|null
81
+ */
82
+ public function getOriginalWidth () {
83
+ return null;
84
+ }
85
+
86
+ public function __toString() {
87
+ $model = $this->_getModel();
88
+ $product = $this->getProduct();
89
+
90
+ $destSubdir = $model->getDestinationSubdir();
91
+
92
+ if (!$imageFileName = $this->getImageFile())
93
+ $imageFileName = $product->getData($destSubdir);
94
+
95
+ if ($imageFileName == 'no_selection') {
96
+ if (($bkThumbnail = $product->getData('bk_thumbnail_'))
97
+ && ($destSubdir == 'image' || $destSubdir == 'small_image'))
98
+ return $bkThumbnail . '&zoom=' . ($destSubdir == 'image' ? 1 : 5);
99
+
100
+ $placeholder = Mage::getModel('catalog/product_image')
101
+ ->setDestinationSubdir($destSubdir)
102
+ ->setBaseFile(null)
103
+ ->getBaseFile();
104
+
105
+ $imageFileName = '/' . basename($placeholder);
106
+ }
107
+
108
+ $width = $model->getWidth();
109
+ $height = $model->getHeight();
110
+
111
+ //!!!TODO: remove hack for 75x75 images
112
+ if ($width == $height && $width != 75)
113
+ $height = null;
114
+
115
+ if (($dimensions = $width . 'x' . $height) == 'x')
116
+ $dimensions = 'full';
117
+
118
+ $store = $this->_productHelper
119
+ ? $this->_productHelper->getWebsite($product)->getDefaultStore()
120
+ : Mage::app()->getStore();
121
+
122
+ return $store->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA)
123
+ . $store->getConfig(MVentory_CDN_Model_Config::PREFIX)
124
+ . '/'
125
+ . $dimensions
126
+ . $imageFileName;
127
+ }
128
+ }
app/code/community/MVentory/CDN/Model/Config.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/CDN
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Config data
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ abstract class MVentory_CDN_Model_Config {
23
+
24
+ const ACCESS_KEY = 'cdn/settings/access_key';
25
+ const SECRET_KEY = 'cdn/settings/secret_key';
26
+ const BUCKET = 'cdn/settings/bucket';
27
+ const PREFIX = 'cdn/settings/prefix';
28
+ const DIMENSIONS = 'cdn/settings/resizing_dimensions';
29
+
30
+ }
app/code/community/MVentory/CDN/Model/Mage/Catalog/Product/Image.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/CDN
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Catalog product image model
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_CDN_Model_Mage_Catalog_Product_Image
23
+ extends Mage_Catalog_Model_Product_Image {
24
+
25
+ /**
26
+ * First check this file on FS or DB. If it doesn't then download it from S3
27
+ *
28
+ * @param string $filename
29
+ *
30
+ * @return bool
31
+ */
32
+ protected function _fileExists ($filename) {
33
+ return parent::_fileExists($filename)
34
+ || Mage::helper('cdn')->download($filename);
35
+ }
36
+ }
app/code/community/MVentory/CDN/Model/Observer.php ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/CDN
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Event handlers
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_CDN_Model_Observer {
23
+
24
+ public function upload ($observer) {
25
+ $product = $observer->getEvent()->getProduct();
26
+
27
+ //There's nothing to process because we're using images
28
+ //from original product in duplicate
29
+ if ($product->getIsDuplicate()
30
+ || $product->getData('mventory_update_duplicate'))
31
+ return;
32
+
33
+ $images = $observer->getEvent()->getImages();
34
+
35
+ //Use product helper from MVentory_Tm if it's installed and is activated
36
+ //The helper is used to get correct store for the product when MVentory_Tm
37
+ //extension is used
38
+ //Change current store if product's store is different for correct
39
+ //file name of images
40
+ if (Mage::helper('core')->isModuleEnabled('MVentory_Tm')) {
41
+ $store = Mage::helper('mventory_tm/product')
42
+ ->getWebsite($product)
43
+ ->getDefaultStore();
44
+
45
+ $changeStore = $store->getId() != Mage::app()->getStore()->getId();
46
+ } else {
47
+ $store = Mage::app()->getStore();
48
+ $changeStore = false;
49
+ }
50
+
51
+ //Get settings for S3
52
+ $accessKey = $store->getConfig(MVentory_CDN_Model_Config::ACCESS_KEY);
53
+ $secretKey = $store->getConfig(MVentory_CDN_Model_Config::SECRET_KEY);
54
+ $bucket = $store->getConfig(MVentory_CDN_Model_Config::BUCKET);
55
+ $prefix = $store->getConfig(MVentory_CDN_Model_Config::PREFIX);
56
+ $dimensions = $store->getConfig(MVentory_CDN_Model_Config::DIMENSIONS);
57
+
58
+ //Return if S3 settings are empty
59
+ if (!($accessKey && $secretKey && $bucket && $prefix))
60
+ return;
61
+
62
+ //Build prefix for all files on S3
63
+ $cdnPrefix = $bucket . '/' . $prefix . '/';
64
+
65
+ //Parse dimension. Split string to pairs of width and height
66
+ $dimensions = str_replace(', ', ',', $dimensions);
67
+ $dimensions = explode(',', $dimensions);
68
+
69
+ //Prepare meta data for uploading. All uploaded images are public
70
+ $meta = array(Zend_Service_Amazon_S3::S3_ACL_HEADER
71
+ => Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ);
72
+
73
+ if ($changeStore) {
74
+ $emu = Mage::getModel('core/app_emulation');
75
+ $origEnv = $emu->startEnvironmentEmulation($store);
76
+ }
77
+
78
+ $config = Mage::getSingleton('catalog/product_media_config');
79
+
80
+ $s3 = new Zend_Service_Amazon_S3($accessKey, $secretKey);
81
+
82
+ foreach ($images['images'] as &$image) {
83
+ //Process new images only
84
+ if (isset($image['value_id']))
85
+ continue;
86
+
87
+ //Get name of the image and create its key on S3
88
+ $fileName = $image['file'];
89
+ $cdnPath = $cdnPrefix . 'full' . $fileName;
90
+
91
+ //Full path to uploaded image
92
+ $file = $config->getMediaPath($fileName);
93
+
94
+ //Check if object with the key exists
95
+ if ($s3->isObjectAvailable($cdnPath)) {
96
+ $position = strrpos($fileName, '.');
97
+
98
+ //Split file name and extension
99
+ $name = substr($fileName, 0, $position);
100
+ $ext = substr($fileName, $position);
101
+
102
+ //Search key
103
+ $_key = $prefix .'/full' . $name . '_';
104
+
105
+ //Get all objects which is started with the search key
106
+ $keys = $s3->getObjectsByBucket($bucket, array('prefix' => $_key));
107
+
108
+ $index = 1;
109
+
110
+ //If there're objects which names begin with the search key then...
111
+ if (count($keys)) {
112
+ $extLength = strlen($ext);
113
+
114
+ $_keys = array();
115
+
116
+ //... store object names without extension as indeces of the array
117
+ //for fast searching
118
+ foreach ($keys as $key)
119
+ $_keys[substr($key, 0, -$extLength)] = true;
120
+
121
+ //Find next unused object name
122
+ while(isset($_keys[$_key . $index]))
123
+ ++$index;
124
+
125
+ unset($_keys);
126
+ }
127
+
128
+ //Build new name and path with selected index
129
+ $fileName = $name . '_' . $index . $ext;
130
+ $cdnPath = $cdnPrefix . 'full' . $fileName;
131
+
132
+ //Get new name for uploaded file
133
+ $_file = $config->getMediaPath($fileName);
134
+
135
+ //Rename file uploaded to Magento
136
+ rename($file, $_file);
137
+
138
+ //Update values of media attribute in the product after renaming
139
+ //uploaded image if the image was marked as 'image', 'small_image'
140
+ //or 'thumbnail' in the product
141
+ foreach ($product->getMediaAttributes() as $mediaAttribute) {
142
+ $code = $mediaAttribute->getAttributeCode();
143
+
144
+ if ($product->getData($code) == $image['file'])
145
+ $product->setData($code, $fileName);
146
+ }
147
+
148
+ //Save its new name in Magento
149
+ $image['file'] = $fileName;
150
+ $file = $_file;
151
+
152
+ unset($_file);
153
+ }
154
+
155
+ //Upload original image
156
+ if (!$s3->putFile($file, $cdnPath, $meta)) {
157
+ $msg = 'Can\'t upload original image (' . $file . ') to S3 with '
158
+ . $cdnPath . ' key';
159
+
160
+ if ($changeStore)
161
+ $emu->stopEnvironmentEmulation($origEnv);
162
+
163
+ throw new Mage_Core_Exception($msg);
164
+ }
165
+
166
+ //Go to next newly uploaded image if image dimensions for resizing
167
+ //were not set
168
+ if (!count($dimensions))
169
+ continue;
170
+
171
+ //For every dimension...
172
+ foreach ($dimensions as $dimension) {
173
+ //... resize original image and get path to resized image
174
+ $newFile = Mage::getModel('catalog/product_image')
175
+ ->setDestinationSubdir('image')
176
+ ->setSize($dimension)
177
+ ->setKeepFrame(false)
178
+ ->setConstrainOnly(true)
179
+ ->setBaseFile($fileName)
180
+ ->resize()
181
+ ->saveFile()
182
+ ->getNewFile();
183
+
184
+ //Build S3 path for the resized image
185
+ $newCdnPath = $cdnPrefix . $dimension . $fileName;
186
+
187
+ //Upload resized images
188
+ if (!$s3->putFile($newFile, $newCdnPath, $meta)) {
189
+ $msg = 'Can\'t upload resized (' . $dimension . ') image (' . $file
190
+ . ') to S3 with ' . $cdnPath . ' key';
191
+
192
+ if ($changeStore)
193
+ $emu->stopEnvironmentEmulation($origEnv);
194
+
195
+ throw new Mage_Core_Exception($msg);
196
+ }
197
+ }
198
+ }
199
+
200
+ if ($changeStore)
201
+ $emu->stopEnvironmentEmulation($origEnv);
202
+ }
203
+ }
app/code/community/MVentory/CDN/controllers/PlaceholdersController.php ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * NOTICE OF LICENSE
5
+ *
6
+ * This source file is subject to the Open Software License (OSL 3.0)
7
+ * that is bundled with this package in the file LICENSE-OSL.
8
+ * It is also available through the world-wide-web at this URL:
9
+ * http://opensource.org/licenses/osl-3.0.php
10
+ *
11
+ * @package MVentory/CDN
12
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
13
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
14
+ */
15
+
16
+ /**
17
+ * Controller for placeholders uploading
18
+ *
19
+ * @package MVentory/Productivity
20
+ * @author Anatoly A. Kazantsev <anatoly@mventory.com>
21
+ */
22
+ class MVentory_CDN_PlaceholdersController
23
+ extends Mage_Adminhtml_Controller_Action {
24
+
25
+ const ERROR = Mage_Core_Model_Message::ERROR;
26
+ const WARNING = Mage_Core_Model_Message::WARNING;
27
+ const NOTICE = Mage_Core_Model_Message::NOTICE;
28
+ const SUCCESS = Mage_Core_Model_Message::SUCCESS;
29
+
30
+ protected function _construct() {
31
+ $this->setUsedModuleName('MVentory_CDN');
32
+ }
33
+
34
+ /**
35
+ * Upload placeholders to CDN
36
+ *
37
+ * @return null
38
+ */
39
+ public function uploadAction () {
40
+ $website = $this
41
+ ->getRequest()
42
+ ->getParam('website');
43
+
44
+ $website = Mage::app()->getWebsite($website);
45
+
46
+ if (!$website->getId())
47
+ return $this->_back('No website parameter', self::ERROR, $website);
48
+
49
+ $store = $website->getDefaultStore();
50
+
51
+ $accessKey = $store->getConfig(MVentory_CDN_Model_Config::ACCESS_KEY);
52
+ $secretKey = $store->getConfig(MVentory_CDN_Model_Config::SECRET_KEY);
53
+ $bucket = $store->getConfig(MVentory_CDN_Model_Config::BUCKET);
54
+ $prefix = $store->getConfig(MVentory_CDN_Model_Config::PREFIX);
55
+ $dimensions = $store->getConfig(MVentory_CDN_Model_Config::DIMENSIONS);
56
+
57
+ Mage::log($dimensions);
58
+
59
+ if (!($accessKey && $secretKey && $bucket && $prefix))
60
+ return $this->_back(
61
+ 'CDN settings are not specified',
62
+ self::ERROR,
63
+ $website
64
+ );
65
+
66
+ unset($path);
67
+
68
+ $config = Mage::getSingleton('catalog/product_media_config');
69
+
70
+ $destSubdirs = array('image', 'small_image', 'thumbnail');
71
+
72
+ $placeholders = array();
73
+
74
+ $appEmulation = Mage::getModel('core/app_emulation');
75
+
76
+ $env = $appEmulation->startEnvironmentEmulation($store->getId());
77
+
78
+ foreach ($destSubdirs as $destSubdir) {
79
+ $placeholder = Mage::getModel('catalog/product_image')
80
+ ->setDestinationSubdir($destSubdir)
81
+ ->setBaseFile(null)
82
+ ->getBaseFile();
83
+
84
+ $basename = basename($placeholder);
85
+
86
+ $result = copy($placeholder, $config->getMediaPath($basename));
87
+
88
+ if ($result !== true)
89
+ return $this
90
+ ->_back('Error on copy ' . $placeholder . ' to media folder',
91
+ self::ERROR,
92
+ $website);
93
+
94
+ $placeholders[] = '/' . $basename;
95
+ }
96
+
97
+ $appEmulation->stopEnvironmentEmulation($env);
98
+
99
+ unset($store);
100
+ unset($appEmulation);
101
+
102
+ $s3 = new Zend_Service_Amazon_S3($accessKey, $secretKey);
103
+
104
+ $cdnPrefix = $bucket . '/' . $prefix . '/';
105
+
106
+ $dimensions = str_replace(', ', ',', $dimensions);
107
+ $dimensions = explode(',', $dimensions);
108
+
109
+ $meta = array(Zend_Service_Amazon_S3::S3_ACL_HEADER
110
+ => Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ);
111
+
112
+ foreach ($placeholders as $fileName) {
113
+ $cdnPath = $cdnPrefix . 'full' . $fileName;
114
+
115
+ $file = $config->getMediaPath($fileName);
116
+
117
+ try {
118
+ $s3->putFile($file, $cdnPath, $meta);
119
+ } catch (Exception $e) {
120
+ return $this->_back($e->getMessage(), self::ERROR, $website);
121
+ }
122
+
123
+ if (!count($dimensions))
124
+ continue;
125
+
126
+ foreach ($dimensions as $dimension) {
127
+ $newCdnPath = $cdnPrefix . $dimension . $fileName;
128
+
129
+ $productImage = Mage::getModel('catalog/product_image');
130
+
131
+ $destinationSubdir = '';
132
+
133
+ foreach ($destSubdirs as $destSubdir) {
134
+ $newFile = $productImage
135
+ ->setDestinationSubdir($destSubdir)
136
+ ->setSize($dimension)
137
+ ->setBaseFile($fileName)
138
+ ->getNewFile();
139
+
140
+ if (file_exists($newFile)) {
141
+ $destinationSubdir = $destSubdir;
142
+
143
+ break;
144
+ }
145
+ }
146
+
147
+ if ($destinationSubdir == '')
148
+ try {
149
+ $newFile = $productImage
150
+ ->setDestinationSubdir($destinationSubdir)
151
+ ->setSize($dimension)
152
+ ->setBaseFile($fileName)
153
+ ->resize()
154
+ ->saveFile()
155
+ ->getNewFile();
156
+ } catch (Exception $e) {
157
+ return $this->_back($e->getMessage(), self::ERROR, $website);
158
+ }
159
+
160
+ try {
161
+ $s3->putFile($newFile, $newCdnPath, $meta);
162
+ } catch (Exception $e) {
163
+ return $this->_back($e->getMessage(), self::ERROR, $website);
164
+ }
165
+ }
166
+ }
167
+
168
+ return $this->_back('Successfully uploaded all placeholders',
169
+ self::SUCCESS,
170
+ $website);
171
+ }
172
+
173
+ protected function _back ($msg, $type, $website) {
174
+ $path = 'adminhtml/system_config/edit';
175
+
176
+ $params = array(
177
+ 'section' => 'cdn',
178
+ 'website' => $website->getCode()
179
+ );
180
+
181
+ $msg = $this->__($msg);
182
+
183
+ switch (strtolower($type)) {
184
+ case self::ERROR :
185
+ $message = new Mage_Core_Model_Message_Error($msg);
186
+ break;
187
+ case self::WARNING :
188
+ $message = new Mage_Core_Model_Message_Warning($msg);
189
+ break;
190
+ case self::SUCCESS :
191
+ $message = new Mage_Core_Model_Message_Success($msg);
192
+ break;
193
+ default:
194
+ $message = new Mage_Core_Model_Message_Notice($msg);
195
+ break;
196
+ }
197
+
198
+ Mage::getSingleton('adminhtml/session')->addMessage($message);
199
+
200
+ $this->_redirect($path, $params);
201
+ }
202
+ }
app/code/community/MVentory/CDN/etc/config.xml ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <!--
4
+ /**
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE-OSL.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ *
12
+ * @package MVentory/CDN
13
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
14
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
15
+ */
16
+ -->
17
+
18
+ <config>
19
+ <modules>
20
+ <MVentory_CDN>
21
+ <version>0</version>
22
+ </MVentory_CDN>
23
+ </modules>
24
+
25
+ <global>
26
+ <models>
27
+ <cdn>
28
+ <class>MVentory_CDN_Model</class>
29
+ </cdn>
30
+
31
+ <catalog>
32
+ <rewrite>
33
+ <product_image>MVentory_CDN_Model_Mage_Catalog_Product_Image</product_image>
34
+ </rewrite>
35
+ </catalog>
36
+ </models>
37
+
38
+ <blocks>
39
+ <cdn>
40
+ <class>MVentory_CDN_Block</class>
41
+ </cdn>
42
+ </blocks>
43
+
44
+ <helpers>
45
+ <cdn>
46
+ <class>MVentory_CDN_Helper</class>
47
+ </cdn>
48
+
49
+ <catalog>
50
+ <rewrite>
51
+ <image>MVentory_CDN_Helper_Mage_Catalog_Image</image>
52
+ </rewrite>
53
+ </catalog>
54
+ </helpers>
55
+
56
+ <events>
57
+ <!-- Requires M. >= 1.7 -->
58
+ <catalog_product_media_save_before>
59
+ <observers>
60
+ <cdn_upload>
61
+ <type>singleton</type>
62
+ <class>MVentory_CDN_Model_Observer</class>
63
+ <method>upload</method>
64
+ </cdn_upload>
65
+ </observers>
66
+ </catalog_product_media_save_before>
67
+ </events>
68
+ </global>
69
+
70
+ <admin>
71
+ <routers>
72
+ <cdn_adminhtml>
73
+ <use>admin</use>
74
+ <args>
75
+ <module>MVentory_CDN</module>
76
+ <frontName>cdn</frontName>
77
+ </args>
78
+ </cdn_adminhtml>
79
+ </routers>
80
+ </admin>
81
+
82
+ <adminhtml>
83
+ <acl>
84
+ <resources>
85
+ <admin>
86
+ <children>
87
+ <system>
88
+ <children>
89
+ <config>
90
+ <children>
91
+ <cdn>
92
+ <title>mVentory CDN</title>
93
+ </cdn>
94
+ </children>
95
+ </config>
96
+ </children>
97
+ </system>
98
+ </children>
99
+ </admin>
100
+ </resources>
101
+ </acl>
102
+ </adminhtml>
103
+ </config>
app/code/community/MVentory/CDN/etc/system.xml ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <!--
4
+ /**
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE-OSL.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ *
12
+ * @package MVentory/CDN
13
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
14
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
15
+ */
16
+ -->
17
+
18
+ <config>
19
+ <tabs>
20
+ <mventory translate="label" module="cdn">
21
+ <label>mVentory</label>
22
+ <sort_order>100</sort_order>
23
+ </mventory>
24
+ </tabs>
25
+
26
+ <sections>
27
+ <cdn translate="label" module="cdn">
28
+ <label>CDN</label>
29
+ <tab>mventory</tab>
30
+ <frontend_type>text</frontend_type>
31
+
32
+ <show_in_default>1</show_in_default>
33
+ <show_in_website>1</show_in_website>
34
+ <show_in_store>0</show_in_store>
35
+
36
+ <sort_order>50</sort_order>
37
+
38
+ <groups>
39
+ <settings translate="label">
40
+ <label>Settings</label>
41
+ <frontend_type>text</frontend_type>
42
+
43
+ <show_in_default>1</show_in_default>
44
+ <show_in_website>1</show_in_website>
45
+ <show_in_store>0</show_in_store>
46
+
47
+ <sort_order>5</sort_order>
48
+
49
+ <fields>
50
+ <access_key translate="label">
51
+ <label>Access Key</label>
52
+ <frontend_type>text</frontend_type>
53
+
54
+ <show_in_default>1</show_in_default>
55
+ <show_in_website>0</show_in_website>
56
+ <show_in_store>0</show_in_store>
57
+
58
+ <sort_order>1</sort_order>
59
+ </access_key>
60
+
61
+ <secret_key translate="label">
62
+ <label>Secret Key</label>
63
+ <frontend_type>password</frontend_type>
64
+
65
+ <show_in_default>1</show_in_default>
66
+ <show_in_website>0</show_in_website>
67
+ <show_in_store>0</show_in_store>
68
+
69
+ <sort_order>2</sort_order>
70
+ </secret_key>
71
+
72
+ <bucket translate="label">
73
+ <label>Bucket name</label>
74
+ <frontend_type>text</frontend_type>
75
+
76
+ <show_in_default>1</show_in_default>
77
+ <show_in_website>0</show_in_website>
78
+ <show_in_store>0</show_in_store>
79
+
80
+ <sort_order>3</sort_order>
81
+ </bucket>
82
+
83
+ <prefix translate="label">
84
+ <label>Website Prefix</label>
85
+ <frontend_type>text</frontend_type>
86
+
87
+ <show_in_default>0</show_in_default>
88
+ <show_in_website>1</show_in_website>
89
+ <show_in_store>0</show_in_store>
90
+
91
+ <sort_order>4</sort_order>
92
+ </prefix>
93
+
94
+ <resizing_dimensions translate="label comment">
95
+ <label>Resizing Dimensions</label>
96
+ <comment>Example: 300x300, 256x, x500</comment>
97
+ <frontend_type>text</frontend_type>
98
+
99
+ <show_in_default>1</show_in_default>
100
+ <show_in_website>1</show_in_website>
101
+ <show_in_store>0</show_in_store>
102
+
103
+ <sort_order>5</sort_order>
104
+ </resizing_dimensions>
105
+
106
+ <upload_placeholders>
107
+ <frontend_model>cdn/system_config_form_field_placeholders</frontend_model>
108
+
109
+ <show_in_default>0</show_in_default>
110
+ <show_in_website>1</show_in_website>
111
+ <show_in_store>0</show_in_store>
112
+
113
+ <sort_order>6</sort_order>
114
+ </upload_placeholders>
115
+ </fields>
116
+ </settings>
117
+ </groups>
118
+ </cdn>
119
+ </sections>
120
+ </config>
app/etc/modules/MVentory_CDN.xml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <!--
4
+ /**
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE-OSL.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ *
12
+ * @package MVentory/CDN
13
+ * @copyright Copyright (c) 2014 mVentory Ltd. (http://mventory.com)
14
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
15
+ */
16
+ -->
17
+
18
+ <config>
19
+ <modules>
20
+ <MVentory_CDN>
21
+ <active>true</active>
22
+ <codePool>community</codePool>
23
+ </MVentory_CDN>
24
+ </modules>
25
+ </config>
package.xml ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>MVentory_CDN</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License version 3.0</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Host product images on AWS S3 with automated uploading of new images. Compatible with AWS CloudFront.</summary>
10
+ <description>Product images uploaded to Magento are duplicated in Amazon AWS S3 storage cloud. Product and category pages use S3 or CloudFront URLs to display them.&#xD;
11
+ &#xD;
12
+ The extension has to prepare images of multiple sizes and upload them all to S3 to avoid resizig at runtime. You will need to check your theme for the list of sizes used in it.&#xD;
13
+ &#xD;
14
+ Theme compatibility&#xD;
15
+ &#xD;
16
+ Most well designed themes are compatible with the extension. You may need to make a few minor changes. See documentation on GitHub.&#xD;
17
+ &#xD;
18
+ Source code and documentation&#xD;
19
+ &#xD;
20
+ https://github.com/mVentory/S3CDN&#xD;
21
+ &#xD;
22
+ Support&#xD;
23
+ &#xD;
24
+ Bug reports are welcome at info@mventory.com&#xD;
25
+ &#xD;
26
+ License&#xD;
27
+ &#xD;
28
+ The extension is free open source released under OSL 3 license. Please, contribute your code if you make useful changes.</description>
29
+ <notes>Initial release</notes>
30
+ <authors><author><name>Anatoly A. Kazantsev</name><user>anatoly</user><email>anatoly@mventory.com</email></author></authors>
31
+ <date>2014-06-01</date>
32
+ <time>11:12:21</time>
33
+ <contents><target name="mageetc"><dir name="modules"><file name="MVentory_CDN.xml" hash="28eca62142b4b9a00978904fe0d6d5d2"/></dir></target><target name="magecommunity"><dir name="MVentory"><dir name="CDN"><dir name="Block"><dir name="System"><dir name="Config"><dir name="Form"><dir name="Field"><file name="Placeholders.php" hash="0c893beb510e2102395a247372cfffd9"/></dir></dir></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="3a74eb18a9c0e90d9529740d56d7f10a"/><dir name="Mage"><dir name="Catalog"><file name="Image.php" hash="28d1530c9c8c8810aaaafe6fcd6bcbe1"/></dir></dir></dir><dir name="Model"><file name="Config.php" hash="4d4cbdaf7c72782982c73187d55e55a2"/><dir name="Mage"><dir name="Catalog"><dir name="Product"><file name="Image.php" hash="f62e3eca90e228411f9820c3eee86622"/></dir></dir></dir><file name="Observer.php" hash="ca2028f86e4b115d519091cb9a02a2b6"/></dir><dir name="controllers"><file name="PlaceholdersController.php" hash="7cc18a6f4a0ca224e64dc5dbbd2c7888"/></dir><dir name="etc"><file name="config.xml" hash="1eda6fd8daf7564ce104cd9341f99597"/><file name="system.xml" hash="33e807357b8746420c99bdeaaff51688"/></dir></dir></dir></target></contents>
34
+ <compatible/>
35
+ <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
36
+ </package>