Sirv_Magento - Version 1.0.0

Version Notes

Serve your images faster with Sirv. Magento's best CDN for watermarks, text overlays and responsive images with super-fast delivery.

Need to deliver your images fast? Add effects like text, watermarks or frames? Struggling to store and manage loads of images? Need an automated way to resize and optimize images? Want a faster loading website? Sirv is the CDN (content delivery network) you've been looking for.

Sirv stores and processes all your Magento product and category images, serving them fast to your customers all around the world.

Download this release

Release Info

Developer Magic Toolbox
Extension Sirv_Magento
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

app/code/local/MagicToolbox/Sirv/Helper/Cache.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Helper_Cache extends Mage_Core_Helper_Abstract {
4
+
5
+ private static $cacheStorage;
6
+ private static $cacheTTL;
7
+ private static $cacheFile;
8
+ private static $cache = null;
9
+
10
+ public function __construct() {
11
+ self::$cacheStorage = intval(Mage::getStoreConfig('sirv/general/cache_storage'));
12
+ self::$cacheTTL = floatval(Mage::getStoreConfig('sirv/general/cache_ttl')) * 60;//NOTE: in seconds
13
+ self::$cacheFile = Mage::getBaseDir('cache').DS.'sirv.cache';
14
+ if(self::$cacheStorage == 2) {//NOTE: file system cache
15
+ $this->loadFileCache();
16
+ }
17
+ }
18
+
19
+ protected function loadFileCache() {
20
+ if(self::$cache == null) {
21
+ if(file_exists(self::$cacheFile) && $contents = file_get_contents(self::$cacheFile)) {
22
+ self::$cache = unserialize($contents);
23
+ }
24
+ if(!is_array(self::$cache)) {
25
+ self::$cache = array();
26
+ }
27
+ }
28
+ }
29
+
30
+ public function isCached($url) {
31
+ $cacheTTL = rand(intval(self::$cacheTTL * 0.9), intval(self::$cacheTTL * 1.1));
32
+ if(self::$cacheStorage == 1) {//NOTE: database cache
33
+ try {
34
+ $db = Mage::getModel('sirv/cache')->load($url, 'url');
35
+ $lastChecked = $db->getLastChecked();
36
+ if(!$lastChecked) return false;
37
+ $lastChecked = strtotime($lastChecked);
38
+ } catch(Exception $e) {
39
+ return false;
40
+ }
41
+ $maxTime = intval($lastChecked) + ($cacheTTL * 60);
42
+ return (time() < $maxTime);
43
+ } else if(self::$cacheStorage == 2) {//NOTE: file system cache
44
+ if(array_key_exists($url, self::$cache)) {
45
+ $maxTime = self::$cache[$url] + ($cacheTTL * 60);
46
+ return (time() < $maxTime);
47
+ } else {
48
+ return false;
49
+ }
50
+ }
51
+ }
52
+
53
+ public function updateCache($url) {
54
+ if(self::$cacheStorage == 1) {//NOTE: database cache
55
+ try {
56
+ $db = Mage::getModel('sirv/cache')->load($url, 'url');
57
+ $db->setUrl($url);
58
+ $db->setLastChecked(date('Y-m-d H:i:s'));
59
+ $db->save();
60
+ } catch(Exception $e) {
61
+ throw new Exception("Could not access caching database table.");
62
+ return false;
63
+ }
64
+ return true;
65
+ } else if(self::$cacheStorage == 2) {//NOTE: file system cache
66
+ self::loadFileCache();
67
+ self::$cache[$url] = time();
68
+ file_put_contents(self::$cacheFile, serialize(self::$cache));
69
+ return true;
70
+ }
71
+ }
72
+
73
+ public function cleanCache() {
74
+ if(!(int)Mage::getStoreConfig('sirv/general/enabled')) {
75
+ return;
76
+ }
77
+ //NOTE: find and delete all expired entries
78
+ if(self::$cacheStorage == 1) {//NOTE: database cache
79
+ $minTime = time() - (self::$cacheTTL);
80
+ $collection = Mage::getModel('sirv/cache')->getCollection()
81
+ ->addFieldToFilter('last_checked', array('lt' => date('Y-m-d H:i:s', $minTime)))
82
+ ->load();
83
+ if($collection->getSize() > 0) {
84
+ foreach($collection->getIterator() as $record) {
85
+ $record->delete();
86
+ }
87
+ }
88
+ } else if(self::$cacheStorage == 2) {//NOTE: file system cache
89
+ if(count(self::$cache)) {
90
+ foreach(self::$cache as $url => $timeStamp) {
91
+ $maxTime = self::$cache[$url] + (self::$cacheTTL);
92
+ if(time() > $maxTime) {
93
+ unset(self::$cache[$url]);
94
+ }
95
+ }
96
+ file_put_contents(self::$cacheFile, serialize(self::$cache));
97
+ }
98
+ }
99
+ }
100
+
101
+ public function clearCache() {
102
+ //NOTE: file cache
103
+ file_put_contents(self::$cacheFile, '');
104
+ //NOTE: DB cache
105
+ Mage::getModel('sirv/cache')->getCollection()->truncate();
106
+ }
107
+
108
+ }
app/code/local/MagicToolbox/Sirv/Helper/Data.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Helper_Data extends Mage_Core_Helper_Abstract {
4
+
5
+ }
app/code/local/MagicToolbox/Sirv/Helper/Image.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Helper_Image extends Mage_Catalog_Helper_Image {
4
+
5
+ public function __toString() {
6
+ if(!(bool)Mage::getStoreConfig('sirv/general/enabled')) {
7
+ return parent::__toString();
8
+ }
9
+ if(!(bool)Mage::getStoreConfig('sirv/general/sirv_image_processing')) {
10
+ //1
11
+ return parent::__toString();
12
+ //2
13
+ //parent::__toString();
14
+ //return $this->_getModel()->getUrl();
15
+ }
16
+ try {
17
+ $model = $this->_getModel();
18
+
19
+ if($this->getImageFile()) {
20
+ $model->setBaseFile($this->getImageFile());
21
+ } else {
22
+ $model->setBaseFile($this->getProduct()->getData($model->getDestinationSubdir()));
23
+ }
24
+
25
+ if($this->_scheduleRotate) {
26
+ $model->rotate($this->getAngle());
27
+ }
28
+
29
+ if($this->_scheduleResize) {
30
+ $model->resize();
31
+ }
32
+
33
+ if($this->getWatermark()) {
34
+ $model->setWatermark($this->getWatermark());
35
+ }
36
+
37
+ if($model->isCached()) {
38
+ $url = $model->getUrl();
39
+ } else {
40
+
41
+ $url = $model->saveFile()->getUrl();
42
+ }
43
+ } catch (Exception $e) {
44
+ $url = Mage::getDesign()->getSkinUrl($this->getPlaceholder());
45
+ }
46
+ return $url;
47
+ }
48
+
49
+ }
app/code/local/MagicToolbox/Sirv/Model/Adapter/S3.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Adapter_S3 extends Varien_Object {
4
+
5
+ private $sirv = null;
6
+ private $auth = false;
7
+ private $enabled = false;
8
+ private $bucket = '';
9
+ private $base_url = '';
10
+ private $image_folder = '';
11
+
12
+ protected function _construct() {
13
+ if(is_null($this->sirv)) {
14
+ $this->bucket = Mage::getStoreConfig('sirv/s3/bucket');
15
+ $this->base_url = Mage::app()->getStore()->isCurrentlySecure() ? "https://{$this->bucket}.sirv.com" : "http://{$this->bucket}.sirv.com";
16
+ $this->image_folder = '/'.Mage::getStoreConfig('sirv/general/image_folder');
17
+ $this->sirv = Mage::getModel('sirv/adapter_s3_wrapper', array(
18
+ 'host' => 's3.sirv.com',
19
+ 'bucket' => $this->bucket,
20
+ 'key' => Mage::getStoreConfig('sirv/s3/key'),
21
+ 'secret' => Mage::getStoreConfig('sirv/s3/secret'),
22
+ ));
23
+ $bucketsList = $this->sirv->listBuckets();
24
+ if(!empty($bucketsList)) {
25
+ $this->auth = true;
26
+ }
27
+ if(!empty($this->bucket) && in_array($this->bucket, $bucketsList)) {
28
+ $this->enabled = true;
29
+ }
30
+ }
31
+ }
32
+
33
+ public function isAuth() {
34
+ return $this->auth;
35
+ }
36
+
37
+ public function isEnabled() {
38
+ return $this->enabled;
39
+ }
40
+
41
+ public function clearCache() {
42
+
43
+ if(!$this->auth) return;
44
+
45
+ $collection = Mage::getModel('sirv/cache')->getCollection();
46
+ $collectionSize = $collection->getSize();
47
+
48
+ if($collectionSize) {
49
+ $pageNumber = 1;
50
+ $pageSize = 1000;
51
+ $lastPageNumber = ceil($collectionSize/$pageSize);
52
+ do {
53
+ $collection->setCurPage($pageNumber)->setPageSize($pageSize);
54
+ $urls = array();
55
+ foreach($collection->getIterator() as $record) {
56
+ $urls[] = $this->image_folder.$record->getData('url');
57
+ }
58
+ try {
59
+ $this->sirv->deleteMultipleObjects($urls);
60
+ } catch(Exception $e) {
61
+
62
+ }
63
+ $collection->clear();
64
+ $pageNumber++;
65
+ } while($pageNumber <= $lastPageNumber);
66
+ }
67
+
68
+ Mage::Helper('sirv/cache')->clearCache();
69
+
70
+ }
71
+
72
+ public function save($destFileName, $srcFileName) {
73
+ if(!$this->auth) return false;
74
+ $destFileName = $this->getRelative($destFileName);
75
+ try {
76
+ $result = $this->sirv->uploadFile($this->image_folder.$destFileName, $srcFileName, true);
77
+ } catch(Exception $e) {
78
+ $result = false;
79
+ }
80
+ if($result) {
81
+ Mage::Helper('sirv/cache')->updateCache($destFileName);
82
+ }
83
+ return $result;
84
+ }
85
+
86
+ public function remove($fileName) {
87
+ if(!$this->auth) return false;
88
+ $fileName = $this->getRelative($fileName);
89
+ try {
90
+ $result = $this->sirv->deleteObject($this->image_folder.$fileName);
91
+ } catch(Exception $e) {
92
+ $result = false;
93
+ }
94
+ if($result) {
95
+ Mage::Helper('sirv/cache')->updateCache($fileName, true);
96
+ }
97
+ return $result;
98
+ }
99
+
100
+ public function getUrl($fileName) {
101
+ return $this->base_url.$this->image_folder.$this->getRelative($fileName);
102
+ }
103
+
104
+ public function getRelUrl($fileName) {
105
+ return $this->image_folder.$this->getRelative($fileName);
106
+ }
107
+
108
+ public function fileExists($fileName) {
109
+
110
+ $cached = Mage::Helper('sirv/cache')->isCached($fileName);
111
+ if($cached) {
112
+ return true;
113
+ }
114
+
115
+ $url = $this->getUrl($fileName);
116
+ $c = curl_init();
117
+ curl_setopt($c, CURLOPT_URL, $url);
118
+ curl_setopt($c, CURLOPT_HEADER, true);
119
+ curl_setopt($c, CURLOPT_NOBODY, true);
120
+ curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
121
+ curl_setopt($c, CURLOPT_FOLLOWLOCATION, false);
122
+ curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
123
+ curl_exec($c);
124
+ $code = curl_getinfo($c, CURLINFO_HTTP_CODE);
125
+ //NOTE: for test to see if file size greater than zero
126
+ //$size = curl_getinfo($c, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
127
+ curl_close($c);
128
+
129
+ if($code == 200/* && $size*/) {
130
+ Mage::Helper('sirv/cache')->updateCache($fileName);
131
+ return true;
132
+ } else {
133
+ return false;
134
+ }
135
+ }
136
+
137
+ public function getRelative($fileName) {
138
+ $base = str_replace('\\', '/', Mage::getBaseDir('media'));
139
+ $fileName = str_replace('\\', '/', $fileName);
140
+ return str_replace($base, '', $fileName);
141
+ }
142
+
143
+ }
app/code/local/MagicToolbox/Sirv/Model/Adapter/S3/Wrapper.php ADDED
@@ -0,0 +1,400 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Adapter_S3_Wrapper {
4
+
5
+ private $key;
6
+ private $secret;
7
+ private $host;
8
+ private $bucket;
9
+ private $date;
10
+ private $curlInfo;
11
+
12
+ public function __construct($params) {
13
+ $this->key = $params['key'];
14
+ $this->secret = $params['secret'];
15
+ $this->host = isset($params['host']) ? $params['host'] : 's3.sirv.com';
16
+ $this->bucket = $params['bucket'];
17
+ $this->date = gmdate('D, d M Y H:i:s T');
18
+ }
19
+
20
+ public function listBuckets() {
21
+ $buckets = array();
22
+ $request = array('verb' => 'GET', 'resource' => '/');
23
+ $result = $this->sendRequest($request);
24
+ $xml = simplexml_load_string($result);
25
+ if($xml !== false && isset($xml->Buckets->Bucket)) {
26
+ foreach($xml->Buckets->Bucket as $bucket) {
27
+ $buckets[] = (string)$bucket->Name;
28
+ }
29
+ }
30
+ return $buckets;
31
+ }
32
+
33
+ public function uploadFile($sirvPath, $fsPath, $webAccessible = false, $headers = null) {
34
+ $request = array(
35
+ 'verb' => 'PUT',
36
+ 'bucket' => $this->bucket,
37
+ 'resource' => "{$sirvPath}",
38
+ 'content-md5' => $this->base64(md5_file($fsPath))
39
+ );
40
+
41
+ $fh = fopen($fsPath, 'r');
42
+
43
+ $curl_opts = array(
44
+ 'CURLOPT_PUT' => true,
45
+ 'CURLOPT_INFILE' => $fh,
46
+ 'CURLOPT_INFILESIZE' => filesize($fsPath),
47
+ 'CURLOPT_CUSTOMREQUEST' => 'PUT'
48
+ );
49
+
50
+ if(is_null($headers)) {
51
+ $headers = array();
52
+ }
53
+
54
+ $headers['Content-MD5'] = $request['content-md5'];
55
+
56
+ if($webAccessible === true && !isset($headers['x-amz-acl'])) {
57
+ $headers['x-amz-acl'] = 'public-read';
58
+ }
59
+
60
+ if(!isset($headers['Content-Type'])) {
61
+ $ext = pathinfo($fsPath, PATHINFO_EXTENSION);
62
+ $headers['Content-Type'] = isset($this->mimeTypes[$ext]) ? $this->mimeTypes[$ext] : 'application/octet-stream';
63
+ }
64
+ $request['content-type'] = $headers['Content-Type'];
65
+
66
+ $result = $this->sendRequest($request, $headers, $curl_opts);
67
+ fclose($fh);
68
+ return $this->curlInfo['http_code'] == '200';
69
+ }
70
+
71
+ public function deleteObject($sirvPath) {
72
+ $request = array(
73
+ 'verb' => 'DELETE',
74
+ 'bucket' => $this->bucket,
75
+ 'resource' => "{$sirvPath}",
76
+ );
77
+ $result = $this->sendRequest($request);
78
+ return $this->curlInfo['http_code'] == '204';
79
+ }
80
+
81
+ public function deleteMultipleObjects($keys) {
82
+
83
+ $contents = '<'.'?xml version="1.0"?>'."\n".'<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Object><Key>'.implode('</Key></Object><Object><Key>', $keys).'</Key></Object><Quiet>true</Quiet></Delete>'."\n";
84
+
85
+ $contentMd5 = base64_encode(md5($contents, true));
86
+
87
+ $request = array(
88
+ 'verb' => 'POST',
89
+ 'bucket' => $this->bucket,
90
+ 'resource' => "/?delete",
91
+ 'content-md5' => $contentMd5,
92
+ 'content-type' => "application/xml",
93
+ );
94
+
95
+ $filesize = strlen($contents);
96
+ $fh = fopen('php://temp', 'wb+');
97
+ fwrite($fh, $contents);
98
+ rewind($fh);
99
+
100
+ $curl_opts = array(
101
+ 'CURLOPT_CUSTOMREQUEST' => 'POST',
102
+ 'CURLOPT_UPLOAD' => TRUE,
103
+ 'CURLOPT_INFILE' => $fh,
104
+ 'CURLOPT_INFILESIZE' => $filesize,
105
+ );
106
+
107
+ $headers = array(
108
+ 'Content-Type' => 'application/xml',
109
+ 'Content-MD5' => $contentMd5,
110
+ );
111
+
112
+ $result = $this->sendRequest($request, $headers, $curl_opts);
113
+ fclose($fh);
114
+ return $this->curlInfo['http_code'] == '200';
115
+ }
116
+
117
+ private function sendRequest($request, $headers = null, $curl_opts = null) {
118
+ if(is_null($headers)) {
119
+ $headers = array();
120
+ }
121
+
122
+ $headers['Date'] = $this->date;
123
+ $headers['Authorization'] = 'AWS '.$this->key.':'.$this->signature($request, $headers);
124
+ foreach($headers as $k => $v) {
125
+ $headers[$k] = "$k: $v";
126
+ }
127
+
128
+ $host = isset($request['bucket']) ? $request['bucket'].'.'.$this->host : $this->host;
129
+
130
+ $uri = 'http://'.$host.$request['resource'];
131
+ $ch = curl_init();
132
+ curl_setopt($ch, CURLOPT_URL, $uri);
133
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request['verb']);
134
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
135
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
136
+
137
+ if(is_array($curl_opts)) {
138
+ foreach($curl_opts as $k => $v) {
139
+ curl_setopt($ch, constant($k), $v);
140
+ }
141
+ }
142
+
143
+ $result = curl_exec($ch);
144
+ $this->curlInfo = curl_getinfo($ch);
145
+
146
+ curl_close($ch);
147
+ return $result;
148
+ }
149
+
150
+ private function signature($request, $headers = null) {
151
+ if(is_null($headers)) {
152
+ $headers = array();
153
+ }
154
+
155
+ $CanonicalizedAmzHeadersArr = array();
156
+ $CanonicalizedAmzHeadersStr = '';
157
+ foreach($headers as $k => $v) {
158
+ $k = strtolower($k);
159
+
160
+ if(substr($k, 0, 5) != 'x-amz') continue;
161
+
162
+ if(isset($CanonicalizedAmzHeadersArr[$k])) {
163
+ $CanonicalizedAmzHeadersArr[$k] .= ','.trim($v);
164
+ } else {
165
+ $CanonicalizedAmzHeadersArr[$k] = trim($v);
166
+ }
167
+ }
168
+ ksort($CanonicalizedAmzHeadersArr);
169
+
170
+ foreach($CanonicalizedAmzHeadersArr as $k => $v) {
171
+ $CanonicalizedAmzHeadersStr .= "$k:$v\n";
172
+ }
173
+
174
+ if(isset($request['bucket'])) {
175
+ $request['resource'] = '/'.$request['bucket'].$request['resource'];
176
+ }
177
+
178
+ $str = $request['verb']."\n";
179
+ $str .= isset($request['content-md5']) ? $request['content-md5']."\n" : "\n";
180
+ $str .= isset($request['content-type']) ? $request['content-type']."\n" : "\n";
181
+ $str .= isset($request['date']) ? $request['date']."\n" : $this->date."\n";
182
+ $str .= $CanonicalizedAmzHeadersStr.preg_replace('#\?(?!delete$).*$#is', '', $request['resource']);
183
+
184
+ $sha1 = $this->hasher($str);
185
+ return $this->base64($sha1);
186
+ }
187
+
188
+ //NOTE: Algorithm adapted (stolen) from http://pear.php.net/package/Crypt_HMAC/
189
+ private function hasher($data) {
190
+ $key = $this->secret;
191
+ if(strlen($key) > 64) {
192
+ $key = pack('H40', sha1($key));
193
+ }
194
+ if(strlen($key) < 64) {
195
+ $key = str_pad($key, 64, chr(0));
196
+ }
197
+ $ipad = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
198
+ $opad = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
199
+ return sha1($opad.pack('H40', sha1($ipad.$data)));
200
+ }
201
+
202
+ private function base64($str) {
203
+ $ret = '';
204
+ for($i = 0; $i < strlen($str); $i += 2) {
205
+ $ret .= chr(hexdec(substr($str, $i, 2)));
206
+ }
207
+ return base64_encode($ret);
208
+ }
209
+
210
+ private $mimeTypes = array(
211
+ "323" => "text/h323",
212
+ "acx" => "application/internet-property-stream",
213
+ "ai" => "application/postscript",
214
+ "aif" => "audio/x-aiff",
215
+ "aifc" => "audio/x-aiff",
216
+ "aiff" => "audio/x-aiff",
217
+ "asf" => "video/x-ms-asf",
218
+ "asr" => "video/x-ms-asf",
219
+ "asx" => "video/x-ms-asf",
220
+ "au" => "audio/basic",
221
+ "avi" => "video/quicktime",
222
+ "axs" => "application/olescript",
223
+ "bas" => "text/plain",
224
+ "bcpio" => "application/x-bcpio",
225
+ "bin" => "application/octet-stream",
226
+ "bmp" => "image/bmp",
227
+ "c" => "text/plain",
228
+ "cat" => "application/vnd.ms-pkiseccat",
229
+ "cdf" => "application/x-cdf",
230
+ "cer" => "application/x-x509-ca-cert",
231
+ "class" => "application/octet-stream",
232
+ "clp" => "application/x-msclip",
233
+ "cmx" => "image/x-cmx",
234
+ "cod" => "image/cis-cod",
235
+ "cpio" => "application/x-cpio",
236
+ "crd" => "application/x-mscardfile",
237
+ "crl" => "application/pkix-crl",
238
+ "crt" => "application/x-x509-ca-cert",
239
+ "csh" => "application/x-csh",
240
+ "css" => "text/css",
241
+ "dcr" => "application/x-director",
242
+ "der" => "application/x-x509-ca-cert",
243
+ "dir" => "application/x-director",
244
+ "dll" => "application/x-msdownload",
245
+ "dms" => "application/octet-stream",
246
+ "doc" => "application/msword",
247
+ "dot" => "application/msword",
248
+ "dvi" => "application/x-dvi",
249
+ "dxr" => "application/x-director",
250
+ "eps" => "application/postscript",
251
+ "etx" => "text/x-setext",
252
+ "evy" => "application/envoy",
253
+ "exe" => "application/octet-stream",
254
+ "fif" => "application/fractals",
255
+ "flr" => "x-world/x-vrml",
256
+ "gif" => "image/gif",
257
+ "gtar" => "application/x-gtar",
258
+ "gz" => "application/x-gzip",
259
+ "h" => "text/plain",
260
+ "hdf" => "application/x-hdf",
261
+ "hlp" => "application/winhlp",
262
+ "hqx" => "application/mac-binhex40",
263
+ "hta" => "application/hta",
264
+ "htc" => "text/x-component",
265
+ "htm" => "text/html",
266
+ "html" => "text/html",
267
+ "htt" => "text/webviewhtml",
268
+ "ico" => "image/x-icon",
269
+ "ief" => "image/ief",
270
+ "iii" => "application/x-iphone",
271
+ "ins" => "application/x-internet-signup",
272
+ "isp" => "application/x-internet-signup",
273
+ "jfif" => "image/pipeg",
274
+ "jpe" => "image/jpeg",
275
+ "jpeg" => "image/jpeg",
276
+ "jpg" => "image/jpeg",
277
+ "js" => "application/x-javascript",
278
+ "latex" => "application/x-latex",
279
+ "lha" => "application/octet-stream",
280
+ "lsf" => "video/x-la-asf",
281
+ "lsx" => "video/x-la-asf",
282
+ "lzh" => "application/octet-stream",
283
+ "m13" => "application/x-msmediaview",
284
+ "m14" => "application/x-msmediaview",
285
+ "m3u" => "audio/x-mpegurl",
286
+ "man" => "application/x-troff-man",
287
+ "mdb" => "application/x-msaccess",
288
+ "me" => "application/x-troff-me",
289
+ "mht" => "message/rfc822",
290
+ "mhtml" => "message/rfc822",
291
+ "mid" => "audio/mid",
292
+ "mny" => "application/x-msmoney",
293
+ "mov" => "video/quicktime",
294
+ "movie" => "video/x-sgi-movie",
295
+ "mp2" => "video/mpeg",
296
+ "mp3" => "audio/mpeg",
297
+ "mpa" => "video/mpeg",
298
+ "mpe" => "video/mpeg",
299
+ "mpeg" => "video/mpeg",
300
+ "mpg" => "video/mpeg",
301
+ "mpp" => "application/vnd.ms-project",
302
+ "mpv2" => "video/mpeg",
303
+ "ms" => "application/x-troff-ms",
304
+ "mvb" => "application/x-msmediaview",
305
+ "nws" => "message/rfc822",
306
+ "oda" => "application/oda",
307
+ "p10" => "application/pkcs10",
308
+ "p12" => "application/x-pkcs12",
309
+ "p7b" => "application/x-pkcs7-certificates",
310
+ "p7c" => "application/x-pkcs7-mime",
311
+ "p7m" => "application/x-pkcs7-mime",
312
+ "p7r" => "application/x-pkcs7-certreqresp",
313
+ "p7s" => "application/x-pkcs7-signature",
314
+ "pbm" => "image/x-portable-bitmap",
315
+ "pdf" => "application/pdf",
316
+ "pfx" => "application/x-pkcs12",
317
+ "pgm" => "image/x-portable-graymap",
318
+ "pko" => "application/ynd.ms-pkipko",
319
+ "pma" => "application/x-perfmon",
320
+ "pmc" => "application/x-perfmon",
321
+ "pml" => "application/x-perfmon",
322
+ "pmr" => "application/x-perfmon",
323
+ "pmw" => "application/x-perfmon",
324
+ "png" => "image/png",
325
+ "pnm" => "image/x-portable-anymap",
326
+ "pot" => "application/vnd.ms-powerpoint",
327
+ "ppm" => "image/x-portable-pixmap",
328
+ "pps" => "application/vnd.ms-powerpoint",
329
+ "ppt" => "application/vnd.ms-powerpoint",
330
+ "prf" => "application/pics-rules",
331
+ "ps" => "application/postscript",
332
+ "pub" => "application/x-mspublisher",
333
+ "qt" => "video/quicktime",
334
+ "ra" => "audio/x-pn-realaudio",
335
+ "ram" => "audio/x-pn-realaudio",
336
+ "ras" => "image/x-cmu-raster",
337
+ "rgb" => "image/x-rgb",
338
+ "rmi" => "audio/mid",
339
+ "roff" => "application/x-troff",
340
+ "rtf" => "application/rtf",
341
+ "rtx" => "text/richtext",
342
+ "scd" => "application/x-msschedule",
343
+ "sct" => "text/scriptlet",
344
+ "setpay" => "application/set-payment-initiation",
345
+ "setreg" => "application/set-registration-initiation",
346
+ "sh" => "application/x-sh",
347
+ "shar" => "application/x-shar",
348
+ "sit" => "application/x-stuffit",
349
+ "snd" => "audio/basic",
350
+ "spc" => "application/x-pkcs7-certificates",
351
+ "spl" => "application/futuresplash",
352
+ "src" => "application/x-wais-source",
353
+ "sst" => "application/vnd.ms-pkicertstore",
354
+ "stl" => "application/vnd.ms-pkistl",
355
+ "stm" => "text/html",
356
+ "svg" => "image/svg+xml",
357
+ "sv4cpio" => "application/x-sv4cpio",
358
+ "sv4crc" => "application/x-sv4crc",
359
+ "t" => "application/x-troff",
360
+ "tar" => "application/x-tar",
361
+ "tcl" => "application/x-tcl",
362
+ "tex" => "application/x-tex",
363
+ "texi" => "application/x-texinfo",
364
+ "texinfo" => "application/x-texinfo",
365
+ "tgz" => "application/x-compressed",
366
+ "tif" => "image/tiff",
367
+ "tiff" => "image/tiff",
368
+ "tr" => "application/x-troff",
369
+ "trm" => "application/x-msterminal",
370
+ "tsv" => "text/tab-separated-values",
371
+ "txt" => "text/plain",
372
+ "uls" => "text/iuls",
373
+ "ustar" => "application/x-ustar",
374
+ "vcf" => "text/x-vcard",
375
+ "vrml" => "x-world/x-vrml",
376
+ "wav" => "audio/x-wav",
377
+ "wcm" => "application/vnd.ms-works",
378
+ "wdb" => "application/vnd.ms-works",
379
+ "wks" => "application/vnd.ms-works",
380
+ "wmf" => "application/x-msmetafile",
381
+ "wps" => "application/vnd.ms-works",
382
+ "wri" => "application/x-mswrite",
383
+ "wrl" => "x-world/x-vrml",
384
+ "wrz" => "x-world/x-vrml",
385
+ "xaf" => "x-world/x-vrml",
386
+ "xbm" => "image/x-xbitmap",
387
+ "xla" => "application/vnd.ms-excel",
388
+ "xlc" => "application/vnd.ms-excel",
389
+ "xlm" => "application/vnd.ms-excel",
390
+ "xls" => "application/vnd.ms-excel",
391
+ "xlt" => "application/vnd.ms-excel",
392
+ "xlw" => "application/vnd.ms-excel",
393
+ "xof" => "x-world/x-vrml",
394
+ "xpm" => "image/x-xpixmap",
395
+ "xwd" => "image/x-xwindowdump",
396
+ "z" => "application/x-compress",
397
+ "zip" => "application/zip"
398
+ );
399
+
400
+ }
app/code/local/MagicToolbox/Sirv/Model/Cache.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Cache extends Mage_Core_Model_Abstract {
4
+
5
+ protected function _construct() {
6
+ parent::_construct();
7
+ $this->_init('sirv/cache', 'id');
8
+ }
9
+
10
+ }
app/code/local/MagicToolbox/Sirv/Model/Category.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Category extends Mage_Catalog_Model_Category {
4
+
5
+ public function getImageUrl() {
6
+ if((int)Mage::getStoreConfig('sirv/general/enabled') && ($image = $this->getImage())) {
7
+ $relPath = '/catalog/category/'.$image;
8
+ $sirv = Mage::getSingleton('sirv/adapter_s3');
9
+ if(!$sirv->fileExists($relPath)) {
10
+ $sirv->save($relPath, Mage::getBaseDir('media').$relPath);
11
+ }
12
+ $url = $sirv->getUrl($relPath);
13
+ if($url) {
14
+ return $url;
15
+ }
16
+ }
17
+ return parent::getImageUrl();
18
+ }
19
+
20
+ }
app/code/local/MagicToolbox/Sirv/Model/Mysql4/Cache.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Mysql4_Cache extends Mage_Core_Model_Mysql4_Abstract {
4
+
5
+ protected function _construct() {
6
+ $this->_init('sirv/cache', 'id');
7
+ }
8
+
9
+ }
app/code/local/MagicToolbox/Sirv/Model/Mysql4/Cache/Collection.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Mysql4_Cache_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
4
+
5
+ public function _construct() {
6
+ parent::_construct();
7
+ $this->_init('sirv/cache');
8
+ }
9
+
10
+ public function truncate() {
11
+ if(method_exists($this->getConnection(), 'truncate')) {
12
+ $this->getConnection()->truncate($this->getTable('sirv/cache'));
13
+ } else {
14
+ $sql = 'TRUNCATE TABLE '.$this->getConnection()->quoteIdentifier($this->getTable('sirv/cache'));
15
+ $this->getConnection()->raw_query($sql);
16
+ }
17
+ }
18
+
19
+ }
app/code/local/MagicToolbox/Sirv/Model/Observer.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Observer extends Mage_Core_Model_Abstract {
4
+
5
+ public function cleanCache($schedule) {
6
+ Mage::Helper('sirv/cache')->cleanCache();
7
+ }
8
+
9
+ public function onConfigChange($schedule) {
10
+
11
+ $sirv = Mage::getSingleton('sirv/adapter_s3');
12
+
13
+ if(!$sirv->isAuth()) {
14
+ $session = Mage::getSingleton('adminhtml/session');
15
+ $session->addWarning('The access identifiers you provided for Sirv were denied. You must enter proper credentials to use Sirv.');
16
+ return false;
17
+ }
18
+
19
+ if(!$sirv->isEnabled()) {
20
+ $session = Mage::getSingleton('adminhtml/session');
21
+ $session->addWarning('The bucket name you provided ('.Mage::getStoreConfig('sirv/s3/bucket').') is not available. You must enter a proper bucket name to use Sirv.');
22
+ return false;
23
+ }
24
+
25
+ if(!(int)Mage::getStoreConfig('sirv/general/enabled')) {
26
+ return false;
27
+ }
28
+
29
+ //NOTE: clear file and DB cache
30
+ Mage::Helper('sirv/cache')->clearCache();
31
+
32
+ return true;
33
+ }
34
+
35
+ }
app/code/local/MagicToolbox/Sirv/Model/Product/Image.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Product_Image extends Mage_Catalog_Model_Product_Image {
4
+
5
+ protected $_isSirvEnable = false;
6
+ protected $_useSirvImageProcessing = true;
7
+
8
+ protected function _construct() {
9
+ parent::_construct();
10
+ $this->_isSirvEnable = (bool)Mage::getStoreConfig('sirv/general/enabled');
11
+ $this->_useSirvImageProcessing = (bool)Mage::getStoreConfig('sirv/general/sirv_image_processing');
12
+ }
13
+
14
+ public function getImageProcessor() {
15
+ if(!$this->_processor && $this->_isSirvEnable) {
16
+ $this->_processor = Mage::getModel('sirv/varien_image', $this->getBaseFile());
17
+ }
18
+ return parent::getImageProcessor();
19
+ }
20
+
21
+ public function saveFile() {
22
+ if($this->_isSirvEnable && $this->_useSirvImageProcessing) {
23
+ $fileName = $this->getBaseFile();
24
+ $this->getImageProcessor()->save($fileName);
25
+ Mage::helper('core/file_storage_database')->saveFile($fileName);
26
+ return $this;
27
+ }
28
+ return parent::saveFile();
29
+ }
30
+
31
+ public function getUrl() {
32
+ if($this->_isSirvEnable) {
33
+ if($this->_useSirvImageProcessing) {
34
+ $url = Mage::getSingleton('sirv/adapter_s3')->getUrl($this->_baseFile);
35
+ $url .= $this->getImageProcessor()->getImagingOptionsQuery();
36
+ } else {
37
+ $url = Mage::getSingleton('sirv/adapter_s3')->getUrl($this->_newFile);
38
+ }
39
+ return $url;
40
+ }
41
+ return parent::getUrl();
42
+ }
43
+
44
+ public function isCached() {
45
+ if($this->_isSirvEnable) {
46
+ if($this->_useSirvImageProcessing) {
47
+ $sirvFileName = str_replace(Mage::getBaseDir('media'), '', $this->_baseFile);
48
+ return Mage::getSingleton('sirv/adapter_s3')->fileExists($sirvFileName);
49
+ } else {
50
+ return Mage::getSingleton('sirv/adapter_s3')->fileExists($this->_newFile);
51
+ }
52
+ }
53
+ return parent::isCached();
54
+ }
55
+
56
+ public function clearCache() {
57
+ parent::clearCache();
58
+ if($this->_isSirvEnable) {
59
+ Mage::getSingleton('sirv/adapter_s3')->clearCache();
60
+ }
61
+ }
62
+
63
+ }
app/code/local/MagicToolbox/Sirv/Model/Source/Cachestorage.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Source_Cachestorage {
4
+
5
+ public function toOptionArray() {
6
+ return array(
7
+ array('value' => 1, 'label' => 'in database'),
8
+ array('value' => 2, 'label' => 'in filesystem'),
9
+ );
10
+ }
11
+
12
+ }
app/code/local/MagicToolbox/Sirv/Model/Varien/Image.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Varien_Image extends Varien_Image {
4
+
5
+ protected function _getAdapter($adapter = null) {
6
+ if(!isset($this->_adapter)) {
7
+ if((bool)Mage::getStoreConfig('sirv/general/sirv_image_processing')) {
8
+ $this->_adapter = Mage::getModel('sirv/varien_image_adapter_sirv');
9
+ } else {
10
+ $this->_adapter = Mage::getModel('sirv/varien_image_adapter_gd2');
11
+ }
12
+ }
13
+ return $this->_adapter;
14
+ }
15
+
16
+ public function getImagingOptionsQuery() {
17
+ return $this->_getAdapter()->getImagingOptionsQuery();
18
+ }
19
+
20
+ }
app/code/local/MagicToolbox/Sirv/Model/Varien/Image/Adapter/Gd2.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Varien_Image_Adapter_Gd2 extends Varien_Image_Adapter_Gd2 {
4
+
5
+ public function save($destination = null, $newName = null) {
6
+
7
+ if(!(int)Mage::getStoreConfig('sirv/general/enabled')) {
8
+ return parent::save($destination, $newName);
9
+ }
10
+
11
+ $tempFileName = tempnam(sys_get_temp_dir(), 'sirv');
12
+ parent::save($tempFileName);
13
+
14
+ $fileName = !isset($destination) ? $this->_fileName : $destination;
15
+ if(isset($destination) && isset($newName)) {
16
+ $fileName = "{$destination}/{$fileName}";
17
+ } elseif(isset($destination) && !isset($newName)) {
18
+ $info = pathinfo($destination);
19
+ $fileName = $destination;
20
+ $destination = $info['dirname'];
21
+ } elseif(!isset($destination) && isset($newName)) {
22
+ $fileName = "{$this->_fileSrcPath}/{$newName}";
23
+ } else {
24
+ $fileName = $this->_fileSrcPath.$this->_fileSrcName;
25
+ }
26
+
27
+ if(Mage::getSingleton('sirv/adapter_s3')->save($fileName, $tempFileName)) {
28
+ @unlink($tempFileName);
29
+ } else {
30
+ if(!is_writable($destination)) {
31
+ try {
32
+ $io = new Varien_Io_File();
33
+ $io->mkdir($destination);
34
+ } catch (Exception $e) {
35
+ throw new Exception("Unable to write file into directory '{$destinationDir}'. Access forbidden.");
36
+ }
37
+ }
38
+ @rename($tempFileName, $fileName);
39
+ @chmod($fileName, 0644);
40
+ }
41
+
42
+ }
43
+
44
+ }
app/code/local/MagicToolbox/Sirv/Model/Varien/Image/Adapter/Sirv.php ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MagicToolbox_Sirv_Model_Varien_Image_Adapter_Sirv extends Varien_Image_Adapter_Abstract {
4
+
5
+ protected $_requiredExtensions = array('curl');
6
+
7
+ protected $_resized = false;
8
+
9
+ protected $_imaging_options = array();
10
+
11
+ protected $_quality = null;
12
+
13
+ //NOTE: called from the constructor of Varien_Image
14
+ public function open($filename) {
15
+ $this->_fileName = $filename;
16
+ $this->getMimeType();
17
+ $this->_getFileAttributes();
18
+ if($this->_isMemoryLimitReached()) {
19
+ throw new Varien_Exception('Memory limit has been reached.');
20
+ }
21
+ }
22
+
23
+ protected function _isMemoryLimitReached() {
24
+ $limit = $this->_convertToByte(ini_get('memory_limit'));
25
+ return (memory_get_usage(true)) > $limit;
26
+ }
27
+
28
+ protected function _convertToByte($memoryValue) {
29
+ if(stripos($memoryValue, 'M') !== false) {
30
+ return (int)$memoryValue * 1024 * 1024;
31
+ } else if(stripos($memoryValue, 'KB') !== false) {
32
+ return (int)$memoryValue * 1024;
33
+ }
34
+ return (int)$memoryValue;
35
+ }
36
+
37
+ public function save($destination=null, $newName=null) {
38
+
39
+ $fileName = !isset($destination) ? $this->_fileName : $destination;
40
+
41
+ if(isset($destination) && isset($newName)) {
42
+ $fileName = "{$destination}/{$fileName}";
43
+ } elseif(isset($destination) && !isset($newName)) {
44
+ $fileName = $destination;
45
+ } elseif(!isset($destination) && isset($newName)) {
46
+ $fileName = "{$this->_fileSrcPath}/{$newName}";
47
+ } else {
48
+ $fileName = $this->_fileSrcPath.$this->_fileSrcName;
49
+ }
50
+
51
+ $sirvFileName = str_replace(Mage::getBaseDir('media'), '', $fileName);
52
+
53
+ Mage::getSingleton('sirv/adapter_s3')->save($sirvFileName, $fileName);
54
+
55
+ }
56
+
57
+ public function display() {
58
+ throw new Varien_Exception('Direct output not supported yet.');
59
+ }
60
+
61
+ public function quality($quality = null) {
62
+
63
+ if(!is_null($quality)) {
64
+ //NOTE: set quality param for JPG file type
65
+ if($this->_fileType == IMAGETYPE_JPEG) {
66
+ $this->setImagingOptions('quality', (int)$quality);
67
+ } else
68
+ //NOTE: set quality param for PNG file type
69
+ if($this->_fileType == IMAGETYPE_PNG) {
70
+ $quality = round(((int)$quality / 100) * 10);
71
+ if($quality < 1) {
72
+ $quality = 1;
73
+ } else if($quality > 10) {
74
+ $quality = 10;
75
+ }
76
+ $quality = 10 - $quality;
77
+ $this->setImagingOptions('png.compression', $quality);
78
+ }
79
+
80
+ }
81
+ return $quality;
82
+
83
+ }
84
+
85
+ private function _fillBackgroundColor() {
86
+ list($r, $g, $b) = $this->_backgroundColor;
87
+ $this->setImagingOptions('canvas.color', dechex($r).dechex($g).dechex($b));
88
+ }
89
+
90
+ public function resize($frameWidth = null, $frameHeight = null) {
91
+ if(empty($frameWidth) && empty($frameHeight)) {
92
+ throw new Exception('Invalid image dimensions.');
93
+ }
94
+
95
+ //NOTE: calculate lacking dimension
96
+ if(!$this->_keepFrame) {
97
+ if(null === $frameWidth) {
98
+ $frameWidth = round($frameHeight * ($this->_imageSrcWidth / $this->_imageSrcHeight));
99
+ } else if(null === $frameHeight) {
100
+ $frameHeight = round($frameWidth * ($this->_imageSrcHeight / $this->_imageSrcWidth));
101
+ }
102
+ } else {
103
+ if(null === $frameWidth) {
104
+ $frameWidth = $frameHeight;
105
+ } else if(null === $frameHeight) {
106
+ $frameHeight = $frameWidth;
107
+ }
108
+ }
109
+
110
+ //NOTE: define coordinates of image inside new frame
111
+ $srcX = 0;
112
+ $srcY = 0;
113
+ $dstX = 0;
114
+ $dstY = 0;
115
+ $dstWidth = $frameWidth;
116
+ $dstHeight = $frameHeight;
117
+ if($this->_keepAspectRatio) {
118
+ //NOTE: do not make picture bigger, than it is, if required
119
+ if($this->_constrainOnly) {
120
+ if(($frameWidth >= $this->_imageSrcWidth) && ($frameHeight >= $this->_imageSrcHeight)) {
121
+ $dstWidth = $this->_imageSrcWidth;
122
+ $dstHeight = $this->_imageSrcHeight;
123
+ }
124
+ }
125
+ //NOTE: keep aspect ratio
126
+ if($this->_imageSrcWidth / $this->_imageSrcHeight >= $frameWidth / $frameHeight) {
127
+ $dstHeight = round(($dstWidth / $this->_imageSrcWidth) * $this->_imageSrcHeight);
128
+ } else {
129
+ $dstWidth = round(($dstHeight / $this->_imageSrcHeight) * $this->_imageSrcWidth);
130
+ }
131
+ }
132
+ //NOTE: define position in center (TODO: add positions option)
133
+ $dstY = round(($frameHeight - $dstHeight) / 2);
134
+ $dstX = round(($frameWidth - $dstWidth) / 2);
135
+
136
+ //NOTE: get rid of frame (fallback to zero position coordinates)
137
+ if(!$this->_keepFrame) {
138
+ $frameWidth = $dstWidth;
139
+ $frameHeight = $dstHeight;
140
+ $dstY = 0;
141
+ $dstX = 0;
142
+ } else {
143
+ $this->setImagingOptions('canvas.width', $frameWidth);
144
+ $this->setImagingOptions('canvas.height', $frameHeight);
145
+ $this->_fillBackgroundColor();
146
+ }
147
+
148
+ $this->setImagingOptions('scale.width', $dstWidth);
149
+ $this->setImagingOptions('scale.height', $dstHeight);
150
+
151
+ $this->_resized = true;
152
+ }
153
+
154
+ public function rotate($angle) {
155
+ $angle = (int)$angle;
156
+ if($angle <= 0) {
157
+ return;
158
+ }
159
+ if($angle > 360) {
160
+ $angle = $angle - floor($angle/360)*360;
161
+ }
162
+ if($angle <= 180) {
163
+ $angle = -1*$angle;
164
+ } else {
165
+ $angle = 360-$angle;
166
+ }
167
+ $this->setImagingOptions('rotate', $angle);
168
+ $this->_fillBackgroundColor();
169
+ }
170
+
171
+ public function watermark($watermarkImage, $positionX=0, $positionY=0, $watermarkImageOpacity=30, $repeat=false) {
172
+
173
+ $sirvFileName = str_replace(Mage::getBaseDir('media'), '', $watermarkImage);
174
+ $sirv = Mage::getSingleton('sirv/adapter_s3');
175
+ if(!$sirv->fileExists($sirvFileName)) {
176
+ $sirv->save($sirvFileName, $watermarkImage);
177
+ }
178
+
179
+ list($watermarkSrcWidth, $watermarkSrcHeight, $watermarkFileType, ) = getimagesize($watermarkImage);
180
+ $this->_getFileAttributes();
181
+
182
+ $width = $this->getWatermarkWidth();
183
+ if(empty($width)) $width = $watermarkSrcWidth;
184
+ $height = $this->getWatermarkHeigth();
185
+ if(empty($height)) $height = $watermarkSrcHeight;
186
+ $opacity = $this->getWatermarkImageOpacity();
187
+ if(empty($opacity)) $opacity = 50;
188
+
189
+ $this->setImagingOptions('watermark.image', urlencode($sirv->getRelUrl($sirvFileName)));
190
+
191
+ $this->setImagingOptions('watermark.opacity', $opacity);
192
+
193
+ if($this->getWatermarkWidth() && $this->getWatermarkHeigth() && ($this->getWatermarkPosition() != self::POSITION_STRETCH)) {
194
+ $this->setImagingOptions('watermark.scale.width', $width);
195
+ $this->setImagingOptions('watermark.scale.height', $height);
196
+ $this->setImagingOptions('watermark.scale.option', 'ignore');
197
+ }
198
+
199
+ if($this->getWatermarkPosition() == self::POSITION_TILE ) {
200
+ $this->setImagingOptions('watermark.position', 'tile');
201
+ } elseif( $this->getWatermarkPosition() == self::POSITION_STRETCH ) {
202
+ $this->setImagingOptions('watermark.position', 'center');
203
+ $this->setImagingOptions('watermark.scale.width', $this->_imageSrcWidth);
204
+ $this->setImagingOptions('watermark.scale.height', $this->_imageSrcHeight);
205
+ $this->setImagingOptions('watermark.scale.option', 'ignore');
206
+ } elseif( $this->getWatermarkPosition() == self::POSITION_CENTER ) {
207
+ $this->setImagingOptions('watermark.position', 'center');
208
+ } elseif( $this->getWatermarkPosition() == self::POSITION_TOP_RIGHT ) {
209
+ $this->setImagingOptions('watermark.position', 'northeast');
210
+ } elseif( $this->getWatermarkPosition() == self::POSITION_TOP_LEFT ) {
211
+ $this->setImagingOptions('watermark.position', 'northwest');
212
+ } elseif( $this->getWatermarkPosition() == self::POSITION_BOTTOM_RIGHT ) {
213
+ $this->setImagingOptions('watermark.position', 'southeast');
214
+ } elseif( $this->getWatermarkPosition() == self::POSITION_BOTTOM_LEFT ) {
215
+ $this->setImagingOptions('watermark.position', 'southwest');
216
+ }
217
+
218
+ }
219
+
220
+ public function crop($top=0, $left=0, $right=0, $bottom=0) {
221
+
222
+ if($left == 0 && $top == 0 && $right == 0 && $bottom == 0) {
223
+ return;
224
+ }
225
+
226
+ $newWidth = $this->_imageSrcWidth - $left - $right;
227
+ $newHeight = $this->_imageSrcHeight - $top - $bottom;
228
+
229
+ $this->setImagingOptions('crop.x', (int)$top);
230
+ $this->setImagingOptions('crop.y', (int)$left);
231
+ $this->setImagingOptions('crop.width', (int)$newWidth);
232
+ $this->setImagingOptions('crop.height', (int)$newHeight);
233
+
234
+ }
235
+
236
+ public function checkDependencies() {
237
+ foreach($this->_requiredExtensions as $value) {
238
+ if(!extension_loaded($value)) {
239
+ throw new Exception("Required PHP extension '{$value}' was not loaded.");
240
+ }
241
+ }
242
+ }
243
+
244
+ private function setImagingOptions($name, $value) {
245
+ $this->_imaging_options[$name] = $value;
246
+ }
247
+
248
+ public function getImagingOptionsQuery() {
249
+ $query = array();
250
+ foreach($this->_imaging_options as $key => $value) {
251
+ $query[] = "{$key}={$value}";
252
+ }
253
+ return empty($query) ? '' : '?'.implode('&', $query);
254
+ }
255
+
256
+ }
app/code/local/MagicToolbox/Sirv/etc/adminhtml.xml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <all>
6
+ <title>Allow Everything</title>
7
+ </all>
8
+ <admin>
9
+ <children>
10
+ <system>
11
+ <children>
12
+ <config>
13
+ <children>
14
+ <sirv translate="title">
15
+ <title>Sirv for Magento</title>
16
+ <sort_order>100</sort_order>
17
+ </sirv>
18
+ </children>
19
+ </config>
20
+ </children>
21
+ </system>
22
+ </children>
23
+ </admin>
24
+ </resources>
25
+ </acl>
26
+ </config>
app/code/local/MagicToolbox/Sirv/etc/config.xml ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <MagicToolbox_Sirv>
5
+ <version>1.0.0</version>
6
+ </MagicToolbox_Sirv>
7
+ </modules>
8
+ <global>
9
+ <models>
10
+ <sirv>
11
+ <class>MagicToolbox_Sirv_Model</class>
12
+ <resourceModel>sirv_mysql4</resourceModel>
13
+ </sirv>
14
+ <sirv_mysql4>
15
+ <class>MagicToolbox_Sirv_Model_Mysql4</class>
16
+ <entities>
17
+ <cache>
18
+ <table>sirv_cache</table>
19
+ </cache>
20
+ </entities>
21
+ </sirv_mysql4>
22
+ <catalog>
23
+ <rewrite>
24
+ <category>MagicToolbox_Sirv_Model_Category</category>
25
+ <product_image>MagicToolbox_Sirv_Model_Product_Image</product_image>
26
+ </rewrite>
27
+ </catalog>
28
+ </models>
29
+ <resources>
30
+ <sirv_setup>
31
+ <setup>
32
+ <module>MagicToolbox_Sirv</module>
33
+ </setup>
34
+ <connection>
35
+ <use>core_setup</use>
36
+ </connection>
37
+ </sirv_setup>
38
+ <sirv_write>
39
+ <connection>
40
+ <use>core_write</use>
41
+ </connection>
42
+ </sirv_write>
43
+ <sirv_read>
44
+ <connection>
45
+ <use>core_read</use>
46
+ </connection>
47
+ </sirv_read>
48
+ </resources>
49
+ <helpers>
50
+ <sirv>
51
+ <class>MagicToolbox_Sirv_Helper</class>
52
+ </sirv>
53
+ <catalog>
54
+ <rewrite>
55
+ <image>MagicToolbox_Sirv_Helper_Image</image>
56
+ </rewrite>
57
+ </catalog>
58
+ </helpers>
59
+ <events>
60
+ <admin_system_config_changed_section_sirv>
61
+ <observers>
62
+ <sirv>
63
+ <type>singleton</type>
64
+ <class>sirv/observer</class>
65
+ <method>onConfigChange</method>
66
+ </sirv>
67
+ </observers>
68
+ </admin_system_config_changed_section_sirv>
69
+ </events>
70
+ </global>
71
+ <crontab>
72
+ <jobs>
73
+ <sirv>
74
+ <schedule><cron_expr>0 0 1 * *</cron_expr></schedule>
75
+ <run>
76
+ <model>sirv/observer::cleanCache</model>
77
+ </run>
78
+ </sirv>
79
+ </jobs>
80
+ </crontab>
81
+ <adminhtml>
82
+ <acl>
83
+ <resources>
84
+ <all>
85
+ <title>Allow Everything</title>
86
+ </all>
87
+ <admin>
88
+ <children>
89
+ <system>
90
+ <children>
91
+ <config>
92
+ <children>
93
+ <sirv translate="title">
94
+ <title>Sirv for Magento</title>
95
+ <sort_order>100</sort_order>
96
+ </sirv>
97
+ </children>
98
+ </config>
99
+ </children>
100
+ </system>
101
+ </children>
102
+ </admin>
103
+ </resources>
104
+ </acl>
105
+ </adminhtml>
106
+ <default>
107
+ <sirv>
108
+ <general>
109
+ <enabled>0</enabled>
110
+ <sirv_image_processing>1</sirv_image_processing>
111
+ <image_folder>magento</image_folder>
112
+ <cache_storage>1</cache_storage>
113
+ <cache_ttl>1440</cache_ttl>
114
+ </general>
115
+ </sirv>
116
+ </default>
117
+ </config>
app/code/local/MagicToolbox/Sirv/etc/system.xml ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <sirv translate="label" module="sirv">
5
+ <!-- <label>Sirv for Magento</label> -->
6
+ <label><![CDATA[<img height="18" src="//sirv.sirv.com/website/sirv-logo-dark-blue.png?scale.height=18&canvas.height=18&canvas.color=%23ffffff00" alt="Sirv" />&nbsp;]]>for Magento</label>
7
+ <tab>catalog</tab>
8
+ <frontend_type>text</frontend_type>
9
+ <sort_order>1000</sort_order>
10
+ <show_in_default>1</show_in_default>
11
+ <show_in_website>1</show_in_website>
12
+ <show_in_store>1</show_in_store>
13
+ <groups>
14
+ <general translate="label">
15
+ <label>General Settings</label>
16
+ <frontend_type>text</frontend_type>
17
+ <sort_order>0</sort_order>
18
+ <show_in_default>1</show_in_default>
19
+ <show_in_website>1</show_in_website>
20
+ <show_in_store>1</show_in_store>
21
+ <fields>
22
+ <enabled translate="label">
23
+ <label>Use Sirv CDN</label>
24
+ <frontend_type>select</frontend_type>
25
+ <source_model>adminhtml/system_config_source_yesno</source_model>
26
+ <sort_order>1</sort_order>
27
+ <show_in_default>1</show_in_default>
28
+ <show_in_website>1</show_in_website>
29
+ <show_in_store>1</show_in_store>
30
+ </enabled>
31
+ <sirv_image_processing translate="label">
32
+ <label>Use Sirv image processing</label>
33
+ <frontend_type>select</frontend_type>
34
+ <source_model>adminhtml/system_config_source_yesno</source_model>
35
+ <sort_order>2</sort_order>
36
+ <show_in_default>1</show_in_default>
37
+ <show_in_website>1</show_in_website>
38
+ <show_in_store>1</show_in_store>
39
+ </sirv_image_processing>
40
+ <image_folder translate="label">
41
+ <label>Folder for image storage</label>
42
+ <frontend_type>text</frontend_type>
43
+ <sort_order>3</sort_order>
44
+ <show_in_default>1</show_in_default>
45
+ <show_in_website>0</show_in_website>
46
+ <show_in_store>0</show_in_store>
47
+ </image_folder>
48
+ <cache_storage translate="label">
49
+ <label>Cache Storage</label>
50
+ <frontend_type>select</frontend_type>
51
+ <source_model>sirv/source_cachestorage</source_model>
52
+ <sort_order>4</sort_order>
53
+ <show_in_default>1</show_in_default>
54
+ <show_in_website>1</show_in_website>
55
+ <show_in_store>1</show_in_store>
56
+ </cache_storage>
57
+ <cache_ttl translate="label">
58
+ <label>Cache TTL</label>
59
+ <comment>time(in minutes) after which the cache entry expires</comment>
60
+ <frontend_type>text</frontend_type>
61
+ <sort_order>5</sort_order>
62
+ <show_in_default>1</show_in_default>
63
+ <show_in_website>1</show_in_website>
64
+ <show_in_store>1</show_in_store>
65
+ </cache_ttl>
66
+ </fields>
67
+ </general>
68
+ <s3 translate="label">
69
+ <label>Sirv account</label>
70
+ <frontend_type>text</frontend_type>
71
+ <sort_order>10</sort_order>
72
+ <show_in_default>1</show_in_default>
73
+ <show_in_website>1</show_in_website>
74
+ <show_in_store>1</show_in_store>
75
+ <fields>
76
+ <key translate="label">
77
+ <label>S3 Key</label>
78
+ <frontend_type>text</frontend_type>
79
+ <sort_order>1</sort_order>
80
+ <show_in_default>1</show_in_default>
81
+ <show_in_website>1</show_in_website>
82
+ <show_in_store>1</show_in_store>
83
+ </key>
84
+ <secret translate="label">
85
+ <label>S3 Secret</label>
86
+ <frontend_type>text</frontend_type>
87
+ <sort_order>2</sort_order>
88
+ <show_in_default>1</show_in_default>
89
+ <show_in_website>1</show_in_website>
90
+ <show_in_store>1</show_in_store>
91
+ </secret>
92
+ <bucket translate="label">
93
+ <label>S3 Bucket</label>
94
+ <frontend_type>text</frontend_type>
95
+ <sort_order>3</sort_order>
96
+ <show_in_default>1</show_in_default>
97
+ <show_in_website>1</show_in_website>
98
+ <show_in_store>1</show_in_store>
99
+ </bucket>
100
+ </fields>
101
+ </s3>
102
+ </groups>
103
+ </sirv>
104
+ </sections>
105
+ </config>
app/code/local/MagicToolbox/Sirv/sql/sirv_setup/mysql4-install-1.0.0.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ $installer->startSetup();
5
+
6
+ $installer->run("
7
+ DROP TABLE IF EXISTS {$installer->getTable('sirv/cache')};
8
+ CREATE TABLE {$installer->getTable('sirv/cache')} (
9
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
10
+ `url` varchar(255) NOT NULL DEFAULT '',
11
+ `last_checked` datetime DEFAULT NULL,
12
+ PRIMARY KEY (`id`),
13
+ KEY `url` (`url`)
14
+ ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
15
+ ");
16
+
17
+ $connection = $installer->getConnection();
18
+ $result = $connection->query("SELECT * FROM `{$installer->getTable('core/config_data')}` WHERE `scope_id`=0 AND `path`='sirv/general/image_folder'");
19
+ if($result) {
20
+ $rows = $result->fetch(PDO::FETCH_ASSOC);
21
+ if(!$rows) {
22
+ $validCharacters = 'abcdefghijklmnopqrstuxyvwzABCDEFGHIJKLMNOPQRSTUXYVWZ';
23
+ $validCharNumber = strlen($validCharacters);
24
+ $hashLength = 5;
25
+ $randomHash = '';
26
+ for($i = 0; $i < $hashLength; $i++) {
27
+ $charIndex = mt_rand(0, $validCharNumber-1);
28
+ $randomHash .= $validCharacters[$charIndex];
29
+ }
30
+ $installer->setConfigData('sirv/general/image_folder', "magento-{$randomHash}", 'default', 0);
31
+ //$installer->run("INSERT INTO `{$installer->getTable('core/config_data')}` VALUES (NULL, 'default', 0, 'sirv/general/image_folder', 'magento-{$randomHash}');");
32
+ }
33
+ }
34
+
35
+ $installer->endSetup();
package.xml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Sirv_Magento</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/artistic-license-2.0">Artistic License 2.0</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Serve your images faster with Sirv. Magento's best CDN for watermarks, text overlays and responsive images with super-fast delivery.</summary>
10
+ <description>Need to deliver your images fast? Add effects like text, watermarks or frames? Struggling to store and manage loads of images? Need an automated way to resize and optimize images? Want a faster loading website? Sirv is the CDN (content delivery network) you've been looking for.&#xD;
11
+ &#xD;
12
+ Sirv stores and processes all your Magento product and category images, serving them fast to your customers all around the world.&#xD;
13
+ </description>
14
+ <notes>Serve your images faster with Sirv. Magento's best CDN for watermarks, text overlays and responsive images with super-fast delivery.&#xD;
15
+ &#xD;
16
+ Need to deliver your images fast? Add effects like text, watermarks or frames? Struggling to store and manage loads of images? Need an automated way to resize and optimize images? Want a faster loading website? Sirv is the CDN (content delivery network) you've been looking for.&#xD;
17
+ &#xD;
18
+ Sirv stores and processes all your Magento product and category images, serving them fast to your customers all around the world.&#xD;
19
+ </notes>
20
+ <authors><author><name>Magic Toolbox</name><user>MagicToolbox</user><email>talk@magictoolbox.com</email></author></authors>
21
+ <date>2015-02-10</date>
22
+ <time>10:23:01</time>
23
+ <contents><target name="magelocal"><dir name="MagicToolbox"><dir name="Sirv"><dir name="Helper"><file name="Cache.php" hash="a420885d3ac083cadac0f0d98bebb3b2"/><file name="Data.php" hash="c8b4ba55f09f4565a862a221b41fd886"/><file name="Image.php" hash="c4af0b6100ad7edfb6c42d98f5bc7938"/></dir><dir name="Model"><dir name="Adapter"><dir name="S3"><file name="Wrapper.php" hash="40d82e1a52dfcfaaf6cc2abbcb6e0ec3"/></dir><file name="S3.php" hash="98961769f86037ddabb1f5bd3d65e557"/></dir><file name="Cache.php" hash="524a1551687cd3e06846a6eacbbab745"/><file name="Category.php" hash="f6e97e546bc16f7938bc95dee7ce1cda"/><dir name="Mysql4"><dir name="Cache"><file name="Collection.php" hash="5240db3fe28e1187e6a9eb37855996f3"/></dir><file name="Cache.php" hash="b43c518f666637d49b456601d43b62d8"/></dir><file name="Observer.php" hash="fd08f8f4a93a2b1b07856088a5c4e74e"/><dir name="Product"><file name="Image.php" hash="68fcde78528ab15f89a6a74603eb1672"/></dir><dir name="Source"><file name="Cachestorage.php" hash="49439ab36f3f90b4869ac93b71e1eb7c"/></dir><dir name="Varien"><dir name="Image"><dir name="Adapter"><file name="Gd2.php" hash="ef8826cc944b6e418978ec69eea25f92"/><file name="Sirv.php" hash="7708a492555d22c5034ebfc0bd310211"/></dir></dir><file name="Image.php" hash="9ea90ac546224e0eeece85387e574d48"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="6520c10c5578d9a4cbe7fc334cae7517"/><file name="config.xml" hash="93e79c6bdcf441f9ed7610d50fd04e81"/><file name="system.xml" hash="8022a667488e4fdb4d2b4389a997737b"/></dir><dir name="sql"><dir name="sirv_setup"><file name="mysql4-install-1.0.0.php" hash="b68200579ade1c243b7cb80ed60e582b"/></dir></dir></dir></dir></target></contents>
24
+ <compatible/>
25
+ <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
26
+ </package>