30 | __('Make your store faster by optimizing your JPEG and PNG product images. This extension automatically optimizes the images by integrating with the popular image compression services TinyJPG and TinyPNG.'); ?>
31 |
32 |
33 |
34 |
35 |
36 | __('In case you need help or assistance with the extension or your account please contact'); ?> support@tinypng.com.
37 |
38 |
39 |
40 |
41 | __('For general questions and help with your account please contact') ?> support@tinypng.com.
42 |
43 |
44 |
45 |
46 | __('This extension is developed by TIG. Please contact them for technical support.', $_tigHomepage, $_kbTicketUrl); ?>
47 |
__('Images are optimized each time Magento generates new product images in the image cache folder')?>
25 |
26 |
__(
28 | 'If you have never used the extension before you should flush the product images cache. Magento will
29 | generate new product images that will be automatically optimized. You will only need to flush the cache
30 | once, any new products will be automatically optimized in future.'
31 | );
32 | ?>
33 |
34 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 0) :?>
46 |
47 |
48 | __(
49 | 'Total of %s optimized product images. Original size : %s. New size : %s. Average savings %s%%.',
50 | $_data['totalCompressions'],
51 | $_data['bytesBefore'],
52 | $_data['bytesAfter'],
53 | $_data['percentageSaved']
54 | ); ?>
55 |
114 | __('There are no saved compressions available yet.'); ?>
115 |
116 |
117 |
118 |
119 |
120 |
121 | getCleanImagesUrl()."/>".$this->__('flush the Magento image cache').""; ?>
122 | __('If an image does not appear you may need to %s and visit the product page
123 | again so images will be regenerated and optimized.', $url
124 | ); ?>
125 |
126 | __('For stores with large number of product images it may be wise to delete the images manually in batches from the product cache folder and not all at once.'); ?>
127 |
128 |
129 |
130 |
131 |
167 |
--------------------------------------------------------------------------------
/lib/TinyCompress/lib/Tinify/Client.php:
--------------------------------------------------------------------------------
1 | options = array(
37 | CURLOPT_BINARYTRANSFER => true,
38 | CURLOPT_RETURNTRANSFER => true,
39 | CURLOPT_HEADER => true,
40 | CURLOPT_USERPWD => $key ? ("api:" . $key) : NULL,
41 | CURLOPT_CAINFO => self::caBundle(),
42 | CURLOPT_SSL_VERIFYPEER => true,
43 | CURLOPT_USERAGENT => $userAgent,
44 | );
45 |
46 | if ($proxy) {
47 | $parts = parse_url($proxy);
48 | if (isset($parts["host"])) {
49 | $this->options[CURLOPT_PROXYTYPE] = CURLPROXY_HTTP;
50 | $this->options[CURLOPT_PROXY] = $parts["host"];
51 | } else {
52 | throw new ConnectionException("Invalid proxy");
53 | }
54 |
55 | if (isset($parts["port"])) {
56 | $this->options[CURLOPT_PROXYPORT] = $parts["port"];
57 | }
58 |
59 | $creds = "";
60 | if (isset($parts["user"])) $creds .= $parts["user"];
61 | if (isset($parts["pass"])) $creds .= ":" . $parts["pass"];
62 |
63 | if ($creds) {
64 | $this->options[CURLOPT_PROXYAUTH] = CURLAUTH_ANY;
65 | $this->options[CURLOPT_PROXYUSERPWD] = $creds;
66 | }
67 | }
68 | }
69 |
70 | function request($method, $url, $body = NULL) {
71 | $header = array();
72 | if (is_array($body)) {
73 | if (!empty($body)) {
74 | $body = json_encode($body);
75 | array_push($header, "Content-Type: application/json");
76 | } else {
77 | $body = NULL;
78 | }
79 | }
80 |
81 | for ($retries = self::RETRY_COUNT; $retries >= 0; $retries--) {
82 | if ($retries < self::RETRY_COUNT) {
83 | usleep(self::RETRY_DELAY * 1000);
84 | }
85 |
86 | $request = curl_init();
87 | if ($request === false || $request === null) {
88 | throw new ConnectionException(
89 | "Error while connecting: curl extension is not functional or disabled."
90 | );
91 | }
92 |
93 | curl_setopt_array($request, $this->options);
94 |
95 | $url = strtolower(substr($url, 0, 6)) == "https:" ? $url : self::API_ENDPOINT . $url;
96 | curl_setopt($request, CURLOPT_URL, $url);
97 | curl_setopt($request, CURLOPT_CUSTOMREQUEST, strtoupper($method));
98 |
99 | if (count($header) > 0) {
100 | curl_setopt($request, CURLOPT_HTTPHEADER, $header);
101 | }
102 |
103 | if ($body) {
104 | curl_setopt($request, CURLOPT_POSTFIELDS, $body);
105 | }
106 |
107 | $response = curl_exec($request);
108 |
109 | if (is_string($response)) {
110 | $status = curl_getinfo($request, CURLINFO_HTTP_CODE);
111 | $headerSize = curl_getinfo($request, CURLINFO_HEADER_SIZE);
112 | curl_close($request);
113 |
114 | $headers = self::parseHeaders(substr($response, 0, $headerSize));
115 | $body = substr($response, $headerSize);
116 |
117 | if (isset($headers["compression-count"])) {
118 | Tinify::setCompressionCount(intval($headers["compression-count"]));
119 | }
120 |
121 | if ( isset( $headers["compression-count-remaining"] ) ) {
122 | Tinify::setRemainingCredits( intval( $headers["compression-count-remaining"] ) );
123 | }
124 |
125 | if ( isset( $headers["paying-state"] ) ) {
126 | Tinify::setPayingState( $headers["paying-state"] );
127 | }
128 |
129 | if ( isset( $headers["email-address"] ) ) {
130 | Tinify::setEmailAddress( $headers["email-address"] );
131 | }
132 |
133 | $isJson = false;
134 | if (isset($headers["content-type"])) {
135 | /* Parse JSON response bodies. */
136 | list($contentType) = explode(";", $headers["content-type"], 2);
137 | if (strtolower(trim($contentType)) == "application/json") {
138 | $isJson = true;
139 | }
140 | }
141 |
142 | /* 1xx and 3xx are unexpected and will be treated as error. */
143 | $isError = $status <= 199 || $status >= 300;
144 |
145 | if ($isJson || $isError) {
146 | /* Parse JSON bodies, always interpret errors as JSON. */
147 | $body = json_decode($body);
148 | if (!$body) {
149 | $message = sprintf("Error while parsing response: %s (#%d)",
150 | PHP_VERSION_ID >= 50500 ? json_last_error_msg() : "Error",
151 | json_last_error());
152 | if ($retries > 0 && $status >= 500) continue;
153 | throw Exception::create($message, "ParseError", $status);
154 | }
155 | }
156 |
157 | if ($isError) {
158 | if ($retries > 0 && $status >= 500) continue;
159 | /* When the key doesn't exist a 404 response is given. */
160 | if ($status == 404) {
161 | throw Exception::create(null, null, $status);
162 | } else {
163 | throw Exception::create($body->message, $body->error, $status);
164 | }
165 | }
166 |
167 | return (object) array("body" => $body, "headers" => $headers);
168 | } else {
169 | $message = sprintf("%s (#%d)", curl_error($request), curl_errno($request));
170 | curl_close($request);
171 | if ($retries > 0) continue;
172 | throw new ConnectionException("Error while connecting: " . $message);
173 | }
174 | }
175 | }
176 |
177 | protected static function parseHeaders($headers) {
178 | if (!is_array($headers)) {
179 | $headers = explode("\r\n", $headers);
180 | }
181 |
182 | $result = array();
183 | foreach ($headers as $header) {
184 | if (empty($header)) continue;
185 | $split = explode(":", $header, 2);
186 | if (count($split) === 2) {
187 | $result[strtolower($split[0])] = trim($split[1]);
188 | }
189 | }
190 | return $result;
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/app/code/community/Tiny/CompressImages/sql/tiny_compressimages_setup/install-1.0.0.php:
--------------------------------------------------------------------------------
1 | startSetup();
8 |
9 | /** @var Varien_Db_Adapter_Interface $connection */
10 | $connection = $installer->getConnection();
11 |
12 | $tableName = $installer->getTable('tiny_compressimages/image');
13 | if (!$connection->isTableExists($tableName)) {
14 | $table = $connection
15 | ->newTable($tableName)
16 | ->addColumn(
17 | 'image_id',
18 | Varien_Db_Ddl_Table::TYPE_INTEGER,
19 | null,
20 | array(
21 | 'identity' => true,
22 | 'unsigned' => true,
23 | 'nullable' => false,
24 | 'primary' => true,
25 | ),
26 | 'ID'
27 | )
28 | ->addColumn(
29 | 'path',
30 | Varien_Db_Ddl_Table::TYPE_VARCHAR,
31 | 255,
32 | array(
33 | 'nullable' => false,
34 | ),
35 | 'The path of this image'
36 | )
37 | ->addColumn(
38 | 'path_optimized',
39 | Varien_Db_Ddl_Table::TYPE_VARCHAR,
40 | 255,
41 | array(
42 | 'nullable' => false,
43 | ),
44 | 'The optimized path of this image'
45 | )
46 | ->addColumn(
47 | 'image_type',
48 | Varien_Db_Ddl_Table::TYPE_VARCHAR,
49 | 255,
50 | array(
51 | 'nullable' => false,
52 | ),
53 | 'The type of this image'
54 | )
55 | ->addColumn(
56 | 'hash_before',
57 | Varien_Db_Ddl_Table::TYPE_VARCHAR,
58 | 255,
59 | array(
60 | 'nullable' => false,
61 | ),
62 | 'The hash of the image before processing'
63 | )
64 | ->addColumn(
65 | 'hash_after',
66 | Varien_Db_Ddl_Table::TYPE_VARCHAR,
67 | 255,
68 | array(
69 | 'nullable' => false,
70 | ),
71 | 'The hash of the image after processing'
72 | )
73 | ->addColumn(
74 | 'bytes_before',
75 | Varien_Db_Ddl_Table::TYPE_INTEGER,
76 | null,
77 | array(
78 | 'nullable' => false,
79 | ),
80 | 'The number of bytes before processing'
81 | )
82 | ->addColumn(
83 | 'bytes_after',
84 | Varien_Db_Ddl_Table::TYPE_INTEGER,
85 | null,
86 | array(
87 | 'nullable' => false,
88 | ),
89 | 'The number of bytes after processing'
90 | )
91 | ->addColumn(
92 | 'used_as_source',
93 | Varien_Db_Ddl_Table::TYPE_INTEGER,
94 | null,
95 | array(
96 | 'nullable' => false,
97 | ),
98 | 'How many times is this file used as source'
99 | )
100 | ->addColumn(
101 | 'is_test',
102 | Varien_Db_Ddl_Table::TYPE_INTEGER,
103 | null,
104 | array(
105 | 'nullable' => false,
106 | ),
107 | 'Is this image processed in test mode?'
108 | )
109 | ->addColumn(
110 | 'parent_id',
111 | Varien_Db_Ddl_Table::TYPE_INTEGER,
112 | null,
113 | array(),
114 | 'Has parent image and therefore its copied'
115 | )
116 | ->addColumn(
117 | 'compressed_before',
118 | Varien_Db_Ddl_Table::TYPE_INTEGER,
119 | null,
120 | array(),
121 | 'Is copressed before and therefore not compressed again'
122 | )
123 | ->addColumn(
124 | 'processed_at',
125 | Varien_Db_Ddl_Table::TYPE_DATETIME,
126 | null,
127 | array(
128 | 'nullable' => false,
129 | ),
130 | 'Processed At'
131 | );
132 |
133 | $connection->createTable($table);
134 | }
135 |
136 | $tableName = $installer->getTable('tiny_compressimages/totals');
137 | if (!$connection->isTableExists($tableName)) {
138 | $table = $connection
139 | ->newTable($tableName)
140 | ->addColumn(
141 | 'entity_id',
142 | Varien_Db_Ddl_Table::TYPE_INTEGER,
143 | null,
144 | array(
145 | 'identity' => true,
146 | 'unsigned' => true,
147 | 'nullable' => false,
148 | 'primary' => true,
149 | ),
150 | 'ID'
151 | )
152 | ->addColumn(
153 | 'total_bytes_before',
154 | Varien_Db_Ddl_Table::TYPE_INTEGER,
155 | null,
156 | array(
157 | 'nullable' => false,
158 | ),
159 | 'The number of bytes before processing'
160 | )
161 | ->addColumn(
162 | 'total_bytes_after',
163 | Varien_Db_Ddl_Table::TYPE_INTEGER,
164 | null,
165 | array(
166 | 'nullable' => false,
167 | ),
168 | 'The number of bytes after processing'
169 | )
170 | ->addColumn(
171 | 'total_compressions',
172 | Varien_Db_Ddl_Table::TYPE_INTEGER,
173 | null,
174 | array(
175 | 'nullable' => false,
176 | ),
177 | 'The number of compressions'
178 | )
179 | ->addColumn(
180 | 'date_from',
181 | Varien_Db_Ddl_Table::TYPE_DATETIME,
182 | null,
183 | array(
184 | 'nullable' => false,
185 | ),
186 | 'First day of the month'
187 | )
188 | ->addColumn(
189 | 'date_to',
190 | Varien_Db_Ddl_Table::TYPE_DATETIME,
191 | null,
192 | array(
193 | 'nullable' => false,
194 | ),
195 | 'Last day of the month'
196 | )
197 | ->addColumn(
198 | 'updated_at',
199 | Varien_Db_Ddl_Table::TYPE_DATETIME,
200 | null,
201 | array(
202 | 'nullable' => false,
203 | ),
204 | 'Processed At'
205 | );
206 |
207 | $connection->createTable($table);
208 | }
209 |
210 | /**
211 | * Move the api_key setting to it's new location
212 | */
213 | $installer->moveConfigSettingInDb('compress_images/settings/api_key', 'tiny_compressimages/settings/api_key');
214 |
215 | /**
216 | * In the old module the different image types are 4 different options, in the new module this is only 1 option
217 | */
218 | $images = array(
219 | 'compress_image' => 'image',
220 | 'compress_small_image' => 'small_image',
221 | 'compress_thumbnail' => 'thumbnail',
222 | 'compress_other_images' => 'media',
223 | );
224 |
225 | $allowedTypes = array();
226 | foreach ($images as $from => $to) {
227 | $value = Mage::getConfig('compress_images/image_sizes/' . $from);
228 |
229 | if ($value) {
230 | $allowedTypes[] = $to;
231 | }
232 | }
233 |
234 | if (count($allowedTypes)) {
235 | try {
236 | $connection->insert(
237 | $installer->getTable('core/config_data'),
238 | array(
239 | 'scope' => 'default',
240 | 'scope_id' => '0',
241 | 'value' => implode(',', $allowedTypes),
242 | 'path' => 'tiny_compressimages/settings/product_compression',
243 | )
244 | );
245 | } catch (Exception $e) {
246 | Mage::helper('tiny_compressimages')->log($e);
247 | }
248 | }
249 |
250 | $installer->endSetup();
251 |
--------------------------------------------------------------------------------
/app/code/community/Tiny/CompressImages/Test/Framework/Tiny/Test/TestCase.php:
--------------------------------------------------------------------------------
1 | 'Tiny_CompressImages_Test_Framework_Tiny_Test_Config'
22 | )
23 | )->setResponse(new Tiny_CompressImages_Test_Framework_Tiny_Test_Http_Response());
24 |
25 | $handler = set_error_handler(
26 | function () {
27 | }
28 | );
29 |
30 | set_error_handler(
31 | function ($errno, $errstr, $errfile, $errline) use ($handler) {
32 | if (E_WARNING === $errno
33 | && 0 === strpos($errstr, 'include(')
34 | && substr($errfile, -19) == 'Varien/Autoload.php'
35 | ) {
36 | return null;
37 | }
38 |
39 | return call_user_func(
40 | $handler, $errno, $errstr, $errfile, $errline
41 | );
42 | }
43 | );
44 | }
45 |
46 | public function prepareFrontendDispatch()
47 | {
48 | $store = Mage::app()->getDefaultStoreView();
49 | $store->setConfig('web/url/redirect_to_base', false);
50 | $store->setConfig('web/url/use_store', false);
51 | $store->setConfig('advanced/modules_disable_output/Enterprise_Banner', true);
52 |
53 | Mage::app()->setCurrentStore($store->getCode());
54 |
55 | $this->registerMockSessions();
56 | }
57 |
58 | public function registerMockSessions($modules = null)
59 | {
60 | if (!is_array($modules)) {
61 | $modules = array('core', 'customer', 'checkout', 'catalog', 'reports');
62 | }
63 |
64 | foreach ($modules as $module) {
65 | $class = "$module/session";
66 | $sessionMock = $this->getMockBuilder(
67 | Mage::getConfig()->getModelClassName($class)
68 | )->disableOriginalConstructor()
69 | ->getMock();
70 | $sessionMock->expects($this->any())
71 | ->method('start')
72 | ->will($this->returnSelf());
73 | $sessionMock->expects($this->any())
74 | ->method('init')
75 | ->will($this->returnSelf());
76 | $sessionMock->expects($this->any())
77 | ->method('getMessages')
78 | ->will(
79 | $this->returnValue(
80 | Mage::getModel('core/message_collection')
81 | )
82 | );
83 | $sessionMock->expects($this->any())
84 | ->method('getSessionIdQueryParam')
85 | ->will(
86 | $this->returnValue(
87 | Mage_Core_Model_Session_Abstract::SESSION_ID_QUERY_PARAM
88 | )
89 | );
90 | $sessionMock->expects($this->any())
91 | ->method('getCookieShouldBeReceived')
92 | ->will($this->returnValue(false));
93 | $this->setSingletonMock($class, $sessionMock);
94 | $this->setModelMock($class, $sessionMock);
95 | }
96 |
97 | $cookieMock = $this->getMock('Mage_Core_Model_Cookie');
98 | $cookieMock->expects($this->any())
99 | ->method('get')
100 | ->will($this->returnValue(serialize('dummy')));
101 | Mage::unregister('_singleton/core/cookie');
102 | Mage::register('_singleton/core/cookie', $cookieMock);
103 |
104 | // mock visitor log observer
105 | $logVisitorMock = $this->getMock('Mage_Log_Model_Visitor');
106 | $this->setModelMock('log/visitor', $logVisitorMock);
107 |
108 | /**
109 | * Fix enterprise catalog permissions issue
110 | */
111 | $factoryName = 'enterprise_catalogpermissions/permission_index';
112 | $className = Mage::getConfig()->getModelClassName($factoryName);
113 | if (class_exists($className)) {
114 | $mockPermissions = $this->getMock($className);
115 | $mockPermissions->expects($this->any())
116 | ->method('getIndexForCategory')
117 | ->withAnyParameters()
118 | ->will($this->returnValue(array()));
119 |
120 | $this->setSingletonMock($factoryName, $mockPermissions);
121 | }
122 | }
123 |
124 | /**
125 | * @param string $modelClass
126 | * @param object $mock
127 | *
128 | * @return $this
129 | */
130 | public function setModelMock($modelClass, $mock)
131 | {
132 | $this->getConfig()->setModelMock($modelClass, $mock);
133 |
134 | return $this;
135 | }
136 | /**
137 | * @param string $modelClass
138 | * @param object $mock
139 | *
140 | * @return $this
141 | */
142 | public function setResourceModelMock($modelClass, $mock)
143 | {
144 | $this->getConfig()->setResourceModelMock($modelClass, $mock);
145 |
146 | return $this;
147 | }
148 |
149 | /**
150 | * @param string $modelClass
151 | * @param object $mock
152 | *
153 | * @return $this
154 | */
155 | public function setSingletonMock($modelClass, $mock)
156 | {
157 | $registryKey = '_singleton/' . $modelClass;
158 |
159 | Mage::unregister($registryKey);
160 | Mage::register($registryKey, $mock);
161 |
162 | return $this;
163 | }
164 |
165 | /**
166 | * @param $modelClass
167 | *
168 | * @return mixed
169 | */
170 | public function getSingletonMock($modelClass)
171 | {
172 | $registryKey = '_singleton/' . $modelClass;
173 |
174 | return Mage::registry($registryKey);
175 | }
176 |
177 | /**
178 | * @param string $resourceModelClass
179 | * @param object $mock
180 | *
181 | * @return $this
182 | */
183 | public function setResourceSingletonMock($resourceModelClass, $mock)
184 | {
185 | $registryKey = '_resource_singleton/' . $resourceModelClass;
186 |
187 | Mage::unregister($registryKey);
188 | Mage::register($registryKey, $mock);
189 |
190 | return $this;
191 | }
192 |
193 | /**
194 | * @param string $helperClass
195 | * @param object $mock
196 | *
197 | * @return $this
198 | */
199 | public function setHelperMock($helperClass, $mock)
200 | {
201 | $registryKey = '_helper/' . $helperClass;
202 |
203 | Mage::unregister($registryKey);
204 | Mage::register($registryKey, $mock);
205 |
206 | return $this;
207 | }
208 |
209 | /**
210 | * @return Tiny_CompressImages_Test_Framework_Tiny_Test_Config
211 | */
212 | public function getConfig()
213 | {
214 | return Mage::getConfig();
215 | }
216 |
217 | /**
218 | * Create the models with the provided data.
219 | *
220 | * @param $modelName
221 | * @param $data
222 | */
223 | public function createModels($modelName, $data)
224 | {
225 | foreach ($data as $row) {
226 | $model = Mage::getModel($modelName);
227 | $model->setData($row);
228 | $model->save();
229 | }
230 | }
231 |
232 | /**
233 | * Sets a protected property to the provided value.
234 | *
235 | * @param $property
236 | * @param $value
237 | * @param null $instance
238 | *
239 | * @return $this
240 | */
241 | public function setProperty($property, $value, $instance = null)
242 | {
243 | if ($instance === null) {
244 | $instance = $this->_instance;
245 | }
246 |
247 | $reflection = new ReflectionObject($instance);
248 | $property = $reflection->getProperty($property);
249 | $property->setAccessible(true);
250 | $property->setValue($instance, $value);
251 |
252 | return $this;
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/app/code/community/Tiny/CompressImages/Test/Model/ImageTest.php:
--------------------------------------------------------------------------------
1 | deleteAll();
15 |
16 | $this->_instance = Mage::getModel('tiny_compressimages/image');
17 | }
18 |
19 | /**
20 | * Test data.
21 | *
22 | * @return array
23 | */
24 | public function testGetStatisticsDataProvider()
25 | {
26 | $now = Zend_Date::now();
27 |
28 | return array(
29 | array(
30 | array(
31 | array(
32 | 'path' => '/media/catalog/product/cache/1/image/c96a280f94e22e3ee3823dd0a1a87606/a/c/acj003_2.jpg',
33 | 'hash_before' => md5(uniqid()),
34 | 'hash_after' => md5(uniqid()),
35 | 'bytes_before' => 100,
36 | 'bytes_after' => 50,
37 | 'used_as_source' => 3,
38 | 'processed_at' => $now->subMonth(1),
39 | ),
40 | array(
41 | 'path' => '/media/catalog/product/cache/1/image/c96a280f94e22e3ee3823dd0a1a87606/a/c/acj003_2.jpg',
42 | 'hash_before' => md5(uniqid()),
43 | 'hash_after' => md5(uniqid()),
44 | 'bytes_before' => 100,
45 | 'bytes_after' => 75,
46 | 'used_as_source' => 3,
47 | 'processed_at' => Varien_Date::now(),
48 | ),
49 | ),
50 | array(
51 | 'percentage' => '25 %',
52 | 'percentage_all_year' => '38 %',
53 | 'greatest_saving' => '25',
54 | 'greatest_saving_all_year' => '50',
55 | 'bytes_saved' => 25,
56 | 'bytes_saved_all_year' => 75,
57 | 'images_count' => 1,
58 | 'images_count_all_year' => 2,
59 | )
60 | ),
61 | array(
62 | array(
63 | array(
64 | 'path' => '/media/catalog/product/cache/1/image/c96a280f94e22e3ee3823dd0a1a87606/d/e/dec001_1.jpg',
65 | 'hash_before' => md5(uniqid()),
66 | 'hash_after' => md5(uniqid()),
67 | 'bytes_before' => 500,
68 | 'bytes_after' => 400,
69 | 'used_as_source' => 1,
70 | 'processed_at' => Varien_Date::now(),
71 | ),
72 | ),
73 | array(
74 | 'percentage' => '20 %',
75 | 'percentage_all_year' => '20 %',
76 | 'greatest_saving' => '20',
77 | 'greatest_saving_all_year' => '20',
78 | 'bytes_saved' => 100,
79 | 'bytes_saved_all_year' => 100,
80 | 'images_count' => 1,
81 | 'images_count_all_year' => 1,
82 | )
83 | ),
84 | array(
85 | array(
86 | array(
87 | 'path' => '/media/catalog/product/cache/1/image/c96a280f94e22e3ee3823dd0a1a87606/t/r/troe008.jpg',
88 | 'hash_before' => md5(uniqid()),
89 | 'hash_after' => md5(uniqid()),
90 | 'bytes_before' => 200,
91 | 'bytes_after' => 150,
92 | 'used_as_source' => 1,
93 | 'processed_at' => Varien_Date::now(),
94 | ),
95 | ),
96 | array(
97 | 'percentage' => '25 %',
98 | 'percentage_all_year' => '25 %',
99 | 'greatest_saving' => '25',
100 | 'greatest_saving_all_year' => '25',
101 | 'bytes_saved' => 50,
102 | 'bytes_saved_all_year' => 50,
103 | 'images_count' => 1,
104 | 'images_count_all_year' => 1,
105 | )
106 | ),
107 | );
108 | }
109 |
110 | /**
111 | * Test the results provided by the getStatistics method.
112 | *
113 | * @dataProvider testGetStatisticsDataProvider
114 | */
115 | public function testGetStatistics($models, $results)
116 | {
117 | $this->createModels('tiny_compressimages/image', $models);
118 |
119 | $stats = $this->_instance->getStatistics();
120 | $this->assertEquals($results['percentage'], $stats->getPercentageSaved());
121 | $this->assertEquals($results['bytes_saved'], $stats->getBytesSaved());
122 | $this->assertEquals($results['images_count'], $stats->getImagesCount());
123 | $this->assertEquals($results['greatest_saving'], $stats->getGreatestSaving());
124 |
125 | $stats = $this->_instance->getStatistics(array('current_month' => false));
126 | $this->assertEquals($results['percentage_all_year'], $stats->getPercentageSaved());
127 | $this->assertEquals($results['bytes_saved_all_year'], $stats->getBytesSaved());
128 | $this->assertEquals($results['images_count_all_year'], $stats->getImagesCount());
129 | $this->assertEquals($results['greatest_saving_all_year'], $stats->getGreatestSaving());
130 | }
131 |
132 | /**
133 | * Test the fetching of a model by a hash.
134 | */
135 | public function testGetByHash()
136 | {
137 | $this->createModels(
138 | 'tiny_compressimages/image', array(
139 | array(
140 | 'path' => '1.jpg',
141 | 'hash_before' => '54eab696c5c868b669076216ede66a9f',
142 | 'hash_after' => '636734773941b236c82c9285b11d7f6c',
143 | 'processed_at' => Varien_Date::now(),
144 | ),
145 | array(
146 | 'path' => '2.jpg',
147 | 'hash_before' => 'e79f197d28bebd30ba1bc571c2cc075a',
148 | 'hash_after' => 'df72ed8dbbdca9c440ff6d3182fb0070',
149 | 'processed_at' => Varien_Date::now(),
150 | ),
151 | array(
152 | 'path' => '3.jpg',
153 | 'hash_before' => '59c50fe6455c85fe00b169bde7cb1962',
154 | 'hash_after' => '8e160a4f7fa67e9126269bedcc81c640',
155 | 'processed_at' => Varien_Date::now(),
156 | ),
157 | )
158 | );
159 |
160 | $model = $this->_instance->getByHash('54eab696c5c868b669076216ede66a9f');
161 | $this->assertEquals('1.jpg', $model->getPath());
162 |
163 | $model = $this->_instance->getByHash('df72ed8dbbdca9c440ff6d3182fb0070');
164 | $this->assertEquals('2.jpg', $model->getPath());
165 |
166 | $model = $this->_instance->getByHash('foobar');
167 | $this->assertNull($model);
168 | }
169 |
170 | /**
171 | * Test if the addUsedAsSource works properly.
172 | */
173 | public function testAddUsedAsSource()
174 | {
175 | for ($i = 1; $i <= 3; $i++) {
176 | $this->_instance->addUsedAsSource();
177 | $this->assertEquals($i, $this->_instance->getUsedAsSource());
178 | }
179 | }
180 |
181 | /**
182 | * Test the getBytesSaved method.
183 | */
184 | public function testGetBytesSaved()
185 | {
186 | $this->_instance->setBytesBefore(500);
187 | $this->_instance->setBytesAfter(300);
188 |
189 | $this->assertEquals(200, $this->_instance->getBytesSaved());
190 | }
191 |
192 | /**
193 | * Test the getPercentageSaved method.
194 | */
195 | public function testGetPercentageSaved()
196 | {
197 | $this->_instance->setBytesBefore(500);
198 | $this->_instance->setBytesAfter(400);
199 |
200 | $this->assertEquals('20 %', $this->_instance->getPercentageSaved());
201 |
202 | $this->_instance->setBytesBefore(500);
203 | $this->_instance->setBytesAfter(500);
204 |
205 | $this->assertEquals('0 %', $this->_instance->getPercentageSaved());
206 | }
207 |
208 | /**
209 | * Test the deleteTest method.
210 | */
211 | public function testDeleteAll()
212 | {
213 | $models = array();
214 | for ($i = 0; $i < 10; $i++) {
215 | $models[] = array(
216 | 'hash_before' => md5(uniqid()),
217 | 'hash_after' => md5(uniqid()),
218 | );
219 | }
220 |
221 | $this->createModels('tiny_compressimages/image', $models);
222 |
223 | $this->assertEquals(10, $this->_instance->getCollection()->getSize());
224 |
225 | $this->_instance->deleteAll();
226 |
227 | $this->assertEquals(0, $this->_instance->getCollection()->getSize());
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/app/code/community/Tiny/CompressImages/Model/Image.php:
--------------------------------------------------------------------------------
1 | _helper = Mage::helper('tiny_compressimages');
41 | }
42 |
43 | /**
44 | * Class constructor.
45 | */
46 | public function _construct()
47 | {
48 | $this->_init('tiny_compressimages/image');
49 | }
50 |
51 | /**
52 | * Set the path and also calculate the optimized path.
53 | *
54 | * @param $path
55 | *
56 | * @return $this
57 | */
58 | public function setPath($path)
59 | {
60 | $this->setData('path', $path);
61 |
62 | $filename = basename($path);
63 | $newPath = str_replace($filename, '', $path);
64 | $hash = md5($newPath);
65 |
66 | $optimizedPath = Tiny_CompressImages_Helper_Tinify::TINY_COMPRESSIMAGES_MEDIA_DIRECTORY;
67 | $optimizedPath .= '/' . $hash[0] . '/' . $hash[1] . '/' . $hash . '/' . $filename;
68 |
69 | $this->setPathOptimized($optimizedPath);
70 |
71 | return $this;
72 | }
73 |
74 | /**
75 | * Creates the path where the image will be saved.
76 | *
77 | * @return $this
78 | */
79 | public function createPath()
80 | {
81 | $path = dirname($this->getPathOptimized());
82 | $fullpath = Mage::getBaseDir('media') . str_replace('/media', '', $path);
83 |
84 | if (!is_dir($fullpath)) {
85 | mkdir($fullpath, 0777, true);
86 | }
87 |
88 | return $this;
89 | }
90 |
91 | /**
92 | * The full path where the image will be saved.
93 | *
94 | * @return string
95 | */
96 | public function getFilepathOptimized()
97 | {
98 | $path = $this->getPathOptimized();
99 | return Mage::getBaseDir('media') . str_replace('/media', '', $path);
100 | }
101 |
102 | /**
103 | * Retrieve the url to the image.
104 | *
105 | * @return string
106 | */
107 | public function getUrl()
108 | {
109 | return Mage::getBaseUrl('media') . str_replace('/media/', '', $this->getPathOptimized());
110 | }
111 |
112 | /**
113 | * Get the statistics for the CompressImages module.
114 | *
115 | * $options:
116 | * - current_month: defaults to true
117 | *
118 | * @param array $options
119 | *
120 | * @return Varien_Object
121 | */
122 | public function getStatistics($options = array())
123 | {
124 | $collection = $this->getCollection();
125 |
126 | /**
127 | * Filter by the current month.
128 | */
129 | if (!isset($options['current_month']) ||
130 | (
131 | isset($options['current_month']) &&
132 | $options['current_month']
133 | )
134 | ) {
135 | $dateFrom = Mage::getModel('core/date')->date('Y-m-01');
136 | $dateTo = Mage::getModel('core/date')->date('Y-m-t 23:59:59');
137 |
138 | $collection->addFieldToFilter(
139 | 'processed_at',
140 | array(
141 | 'from' => $dateFrom,
142 | 'to' => $dateTo,
143 | 'date' => true
144 | )
145 | );
146 | }
147 |
148 | $collection
149 | ->getSelect()
150 | ->reset(Zend_Db_Select::COLUMNS)
151 | ->columns('count(image_id) as images_count')
152 | ->columns('sum(bytes_before) as bytes_before')
153 | ->columns('sum(bytes_after) as bytes_after')
154 | ->columns('max(((bytes_before - bytes_after) / bytes_before) * 100) as greatest_saving');
155 |
156 | $data = $collection->getFirstItem();
157 |
158 | if ($data->images_count > 0) {
159 | $data->setData('percentage_saved', (($data->bytes_before - $data->bytes_after) / $data->bytes_before) * 100);
160 | } else {
161 | $data->setData('percentage_saved', 0);
162 | }
163 |
164 | return $data;
165 | }
166 |
167 | /**
168 | * Retrieve a model by the hash.
169 | *
170 | * @param $hash
171 | *
172 | * @return Tiny_CompressImages_Model_Image|null
173 | */
174 | public function getByHash($hash)
175 | {
176 | /** @var Tiny_CompressImages_Model_Resource_Image_Collection $model */
177 | $model = $this->getCollection();
178 | $model->addFieldToFilter(
179 | array(
180 | 'hash_before',
181 | 'hash_after',
182 | ),
183 | array(
184 | array('eq' => $hash),
185 | array('eq' => $hash),
186 | )
187 | );
188 |
189 | $item = $model->getFirstItem();
190 | if ($item->getId() !== null) {
191 | return $item;
192 | } else {
193 | return null;
194 | }
195 | }
196 |
197 | /**
198 | * Simple function to add 1 to the used_as_source column.
199 | *
200 | * @return $this
201 | */
202 | public function addUsedAsSource()
203 | {
204 | $this->setUsedAsSource($this->getUsedAsSource() + 1);
205 |
206 | return $this;
207 | }
208 |
209 | /**
210 | * Get the url for this image.
211 | *
212 | * @return string
213 | */
214 | public function getImageUrl()
215 | {
216 | // If it is a duplicate image, then there will be a link to his parent.
217 | if ($this->getParentId()) {
218 | /** @var Tiny_CompressImages_Model_Image $parent */
219 | $parent = Mage::getModel('tiny_compressimages/image')->load($this->getParentId());
220 |
221 | return $parent->getUrl();
222 | }
223 |
224 | return $this->getUrl();
225 | }
226 |
227 | /**
228 | * Calculate the bytes saved.
229 | *
230 | * @return null|string
231 | */
232 | public function getBytesSaved()
233 | {
234 | return $this->getBytesBefore() - $this->getBytesAfter();
235 | }
236 |
237 | /**
238 | * Show the correct image type.
239 | *
240 | * @return string
241 | */
242 | public function getImageType()
243 | {
244 | $data = $this->getData('image_type');
245 |
246 | switch($data) {
247 | case 'image':
248 | $data = 'Base image';
249 | break;
250 |
251 | case 'small_image':
252 | $data = 'Small image';
253 | break;
254 |
255 | case 'thumbnail':
256 | $data = 'Thumbnail';
257 | break;
258 |
259 | case 'media_image':
260 | $data = 'Swatches';
261 | break;
262 | }
263 |
264 | return $data;
265 | }
266 |
267 | /**
268 | * Calculate the percentage saved.
269 | *
270 | * @return null|string
271 | */
272 | public function getPercentageSaved()
273 | {
274 | $bytesSaved = $this->getBytesSaved();
275 |
276 | if ($bytesSaved == 0) {
277 | return '0 %';
278 | } else {
279 | return round(($bytesSaved / $this->getBytesBefore()) * 100) . ' %';
280 | }
281 | }
282 |
283 | /**
284 | * Shows the time ago in human readable format when this image was processed.
285 | *
286 | * @return string
287 | */
288 | public function getTimeAgo()
289 | {
290 | return $this->_helper->timeAgo($this->getProcessedAt());
291 | }
292 |
293 | /**
294 | * Delete all models
295 | *
296 | * @return $this
297 | */
298 | public function deleteAll()
299 | {
300 | $collection = $this->getCollection();
301 |
302 | Mage::getSingleton('core/resource_iterator')->walk(
303 | $collection->getSelect(), array( function ($args) {
304 | Mage::getModel('tiny_compressimages/image')->load($args['row']['image_id'])->delete();
305 | })
306 | );
307 |
308 | return $this;
309 | }
310 |
311 | /**
312 | * Delete all models
313 | *
314 | * @return $this
315 | */
316 | public function deleteTest()
317 | {
318 | $collection = $this->getCollection();
319 | $collection->addFieldToFilter('is_test', '1');
320 |
321 | Mage::getSingleton('core/resource_iterator')->walk(
322 | $collection->getSelect(), array( function ($args) {
323 | Mage::getModel('tiny_compressimages/image')->load($args['row']['image_id'])->delete();
324 | })
325 | );
326 |
327 | return $this;
328 | }
329 | }
330 |
--------------------------------------------------------------------------------
/app/locale/en_US/Tiny_CompressImages.csv:
--------------------------------------------------------------------------------
1 | "Tiny_CompressImages::TinyPNG","TinyPNG"
2 | "Tiny_CompressImages::Version & Support","Version & Support"
3 | "Tiny_CompressImages::Enable TinyPNG","Enable TinyPNG"
4 | "Tiny_CompressImages::Logging disabled","Logging disabled"
5 | "Tiny_CompressImages::Exceptions only","Exceptions only"
6 | "Tiny_CompressImages::Errors and Exceptions","Errors and Exceptions"
7 | "Tiny_CompressImages::All logging information","All logging information"
8 | "Tiny_CompressImages::A long time ago","A long time ago"
9 | "Tiny_CompressImages::%s month ago","%s month ago"
10 | "Tiny_CompressImages::%s months ago","%s months ago"
11 | "Tiny_CompressImages::%s year ago","%s year ago"
12 | "Tiny_CompressImages::%s years ago","%s years ago"
13 | "Tiny_CompressImages::%s day ago","%s days ago"
14 | "Tiny_CompressImages::%s week ago","%s weeks ago"
15 | "Tiny_CompressImages::%s hour ago","%s hours ago"
16 | "Tiny_CompressImages::%s minute ago","%s minutes ago"
17 | "Tiny_CompressImages::%s second ago","%s seconds ago"
18 | "Tiny_CompressImages::...and %d more images","...and %d more images"
19 | "Tiny_CompressImages::There are no saved compressions available yet.","There are no saved compressions available yet."
20 | "Tiny_CompressImages::Warning: Flushing the image cache will cause the TinyPNG module to recompress all images. When having a big catalog this can lead to high costs.\n\nAre you sure you want to continue?","Warning: Flushing the image cache will cause the TinyPNG module to recompress all images. When having a big catalog this can lead to high costs.\n\nAre you sure you want to continue?"
21 | "Tiny_CompressImages::TIG Tinypng %s","TIG Tinypng %s"
22 | "Tiny_CompressImages::Information about the TinyPNG extension","Information about the TinyPNG extension"
23 | "Tiny_CompressImages::Magento & 3rd party version compatibility","Magento & 3rd party version compatibility"
24 | "Tiny_CompressImages::Magento Community Edition version","Magento Community Edition version"
25 | "Tiny_CompressImages::Magento Enterprise Edition version","Magento Enterprise Edition version"
26 | "Tiny_CompressImages::Support","Support"
27 | "Tiny_CompressImages::The extension is developed by TIG.","The extension is developed by TIG."
28 | "Tiny_CompressImages::Website:","Website:"
29 | "Tiny_CompressImages::Support website:","Support website:"
30 | "Tiny_CompressImages::Extension support and configuration questions","Extension support and configuration questions"
31 | "Tiny_CompressImages::For questions about installing and configuring the extension please consult the relevant documentation:","For questions about installing and configuring the extension please consult the relevant documentation:"
32 | "Tiny_CompressImages::Frequently asked questions","Frequently asked questions"
33 | "Tiny_CompressImages::FAQ","FAQ"
34 | "Tiny_CompressImages::Installation Manual","Installation Manual"
35 | "Tiny_CompressImages::Do you have any additional questions?","Do you have any additional questions?"
36 | "Tiny_CompressImages::Please contact the TIG servicedesk for additional questions.","Please contact the TIG servicedesk for additional questions."
37 | "Tiny_CompressImages::Ask your question","Ask your question"
38 | "Tiny_CompressImages::TIG.nl","TIG.nl"
39 | "Tiny_CompressImages::TIG Knowledgebase","TIG Knowledgebase"
40 | "Tiny_CompressImages::Operational","Operational"
41 | "Tiny_CompressImages::Non-operational","Non-operational"
42 | "Tiny_CompressImages::Click the button to check the API status.","Click the button to check the API status."
43 | "Tiny_CompressImages::Check status","Check status"
44 | "Tiny_CompressImages::TinyPNG API Status","TinyPNG API Status"
45 | "Tiny_CompressImages::We saved %s%% this month! The greatest image compression was %s%%.","We saved %s%% this month! The greatest image compression was %s%%."
46 | "Tiny_CompressImages::There are %s compressions done this month.","There are %s compressions done this month."
47 | "Tiny_CompressImages::TinyPNG Settings","TinyPNG Settings"
48 | "Tiny_CompressImages::On","On"
49 | "Tiny_CompressImages::Off","Off"
50 | "Tiny_CompressImages::Test","Test"
51 | "Tiny_CompressImages::Live","Live"
52 | "Tiny_CompressImages::If this is set to Off, TinyPNG will no longer compress images","If this is set to Off, TinyPNG will no longer compress images"
53 | "Tiny_CompressImages::If test mode is activated, the extension will do real compressions. The result won't be saved.","If test mode is activated, the extension will do real compressions. The result won't be saved."
54 | "Tiny_CompressImages::API Key","API Key"
55 | "Tiny_CompressImages::You can fetch the API Key from the TinyPNG website.","You can fetch the API Key from the TinyPNG website."
56 | "Tiny_CompressImages::Product images to be compressed","Product images to be compressed"
57 | "Tiny_CompressImages::Messages that should be logged","Messages that should be logged"
58 | "Tiny_CompressImages::TinyPNG Status","TinyPNG Status"
59 | "Tiny_CompressImages::Status","Status"
60 | "Tiny_CompressImages::Saved","Saved"
61 | "Tiny_CompressImages::The Api Key is invalid","The Api Key is invalid"
62 | "Tiny_CompressImages::Category images","Category images"
63 | "Tiny_CompressImages::Block images","Block images"
64 | "Tiny_CompressImages::CMS page images","CMS page images"
65 | "Tiny_CompressImages::Thumbnail","Thumbnail"
66 | "Tiny_CompressImages::Small Image","Small Image"
67 | "Tiny_CompressImages::Base Image","Base Image"
68 | "Tiny_CompressImages::Media Image","Media Image"
69 | "Tiny_CompressImages::File","File"
70 | "Tiny_CompressImages::File size","File size"
71 | "Tiny_CompressImages::Bytes Saved","Bytes Saved"
72 | "Tiny_CompressImages::Percentage saved","Percentage saved"
73 | "Tiny_CompressImages::Time","Time"
74 | "Tiny_CompressImages::Show more","Show more"
75 | "Tiny_CompressImages::Show less","Show less"
76 | "Tiny_CompressImages::Saved %s%% over a total of %s compressions", "Saved %s%% over a total of %s compressions"
77 | "Tiny_CompressImages::When set to all, the log file can become huge. Recommended setting for production servers is Errors and Exceptions.","When set to all, the log file can become huge. Recommended setting for production servers is Errors and Exceptions."
78 | "Tiny_CompressImages::Account type","Account type"
79 | "Tiny_CompressImages::TinyPNG Log","TinyPNG Log"
80 | "Tiny_CompressImages::Note: When flushing the image cache, the TinyPNG extension will re-optimize all product images that are shown on the store view(s).","Note: When flushing the image cache, the TinyPNG extension will re-optimize all product images that are shown on the store view(s)."
81 | "Tiny_CompressImages::File","File"
82 | "Tiny_CompressImages::Original","Original"
83 | "Tiny_CompressImages::Now","Now"
84 | "Tiny_CompressImages::Saved","Saved"
85 | "Tiny_CompressImages::Time","Time"
86 | "Tiny_CompressImages::Download log file","Download log file"
87 | "Tiny_CompressImages::Download TinyPNG log file","Download TinyPNG log file"
88 | "Tiny_CompressImages::Information about the TinyPNG extension","Information about the TinyPNG extension"
89 | "Tiny_CompressImages::What does TinyPNG do?","What does TinyPNG do?"
90 | "Tiny_CompressImages::TinyPNG uses smart lossy compression techniques to reduce the file size of your PNG files. By selectively decreasing the number of colors in the image, fewer bytes are required to store the data. The effect is nearly invisible but it makes a very large difference in file size!","TinyPNG uses smart lossy compression techniques to reduce the file size of your PNG files. By selectively decreasing the number of colors in the image, fewer bytes are required to store the data. The effect is nearly invisible but it makes a very large difference in file size!"
91 | "Tiny_CompressImages::Why should I use TinyPNG?","Why should I use TinyPNG?"
92 | "Tiny_CompressImages::PNG is useful because it's the only widely supported format that can store partially transparent images. The format uses compression, but the files can still be large. Use TinyPNG to shrink images for your apps and sites. It will use less bandwidth and load faster.","PNG is useful because it's the only widely supported format that can store partially transparent images. The format uses compression, but the files can still be large. Use TinyPNG to shrink images for your apps and sites. It will use less bandwidth and load faster."
93 | "Tiny_CompressImages::How does the extension work?","How does the extension work?"
94 | "Tiny_CompressImages::When a users visits the store view, the TinyPNG will automatically compress the images on the fly. This only applies for images that are not already cached. So in order to optimize all images, flush your entire images cache so your whole images database will be optimized. When test mode is enabled, the images will also be compressed on the fly, however, compressed images will not be saved to disk. The result can be found in the status log, which can be downloaded in the Log section tab of this extension.","When a users visits the store view, the TinyPNG will automatically compress the images on the fly. This only applies for images that are not already cached. So in order to optimize all images, flush your entire images cache so your whole images database will be optimized. When test mode is enabled, the images will also be compressed on the fly, however, compressed images will not be saved to disk. The result can be found in the status log, which can be downloaded in the Log section tab of this extension."
95 | "Tiny_CompressImages::General settings and accountdata","General settings and accountdata"
96 | "Tiny_CompressImages::Please contact TinyPNG for assistance in setting up your account data:","Please contact TinyPNG for assistance in setting up your account data:"
97 | "Tiny_CompressImages::Optimized","Optimized"
98 | "Tiny_CompressImages::Please enable the extension to check the compression count.","Please enable the extension to check the compression count."
99 | "Tiny_CompressImages::Please enter your api key to check the compression count.","Please enter your api key to check the compression count."
100 | "Tiny_CompressImages::Please enter your api key to check the amount of compressions left.","Please enter your api key to check the amount of compressions left."
101 | "Tiny_CompressImages::Please enable the extension to check the amount of compressions left.","Please enable the extension to check the amount of compressions left."
102 | "Tiny_CompressImages::Remove all limitations? Visit your TinyPNG dashboard to upgrade your account.","Remove all limitations? Visit your TinyPNG dashboard to upgrade your account."
103 | "Tiny_CompressImages::Upgrade Plan","Upgrade Plan"
104 | "Tiny_CompressImages::For stores with large number of product images it may be wise to delete the images manually in batches from the product cache folder and not all at once.","For stores with large number of product images it may be wise to delete the images manually in batches from the product cache folder and not all at once."
105 |
--------------------------------------------------------------------------------
/app/code/community/Tiny/CompressImages/etc/system.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | general
7 | tiny_compressimages/adminhtml_system_config_form
8 | tiny-compressimages-section
9 | text
10 | 1200
11 | 1
12 | 1
13 | 1
14 |
15 |
16 |
17 | 10
18 | 1
19 | 1
20 | 1
21 |
22 |
23 | 0
24 | tiny_compressimages/adminhtml_system_config_form_field_supportTab
25 | 1
26 | 1
27 | 1
28 |
29 |
30 |
31 |
32 |
33 | 20
34 | 1
35 | 1
36 | 1
37 | active
38 |
39 |
40 |
41 | text
42 | tiny_compressimages/system_config_backend_apiKey
43 | TinyPNG Developer section to get an API key.]]>
44 | 10
45 | 1
46 | 1
47 | 1
48 |
49 |
50 |
51 | 20
52 | compressimages_api
53 | 1
54 | 1
55 | 1
56 |
57 |
58 |
59 | 30
60 | compressimages_status
61 | 1
62 | 0
63 | 0
64 |
65 |
66 |
67 | 40
68 | compressimages_credits
69 | 1
70 | 0
71 | 0
72 |
73 |
74 |
75 |
76 |
77 | Choose which generated image types should be optimized with TinyPNG or TinyJPG. Each selected image size counts as one compression in your compression total.
78 | 25
79 | 1
80 | 1
81 | 1
82 | active
83 |
84 |
85 |
86 | select
87 | Large images on product pages.
88 | adminhtml/system_config_source_yesno
89 | 10
90 | 1
91 | 1
92 | 1
93 |
94 |
95 |
96 | Smaller images on category pages, in list of recently viewed product, new product lists, upsells, wish lists, etc.
97 | select
98 | adminhtml/system_config_source_yesno
99 | 20
100 | 1
101 | 1
102 | 1
103 |
104 |
105 |
106 | Thumbnail-sized images in product image galleries, sidebars, widgets, RSS feeds, etc.
107 | select
108 | adminhtml/system_config_source_yesno
109 | 30
110 | 1
111 | 1
112 | 1
113 |
114 |
115 |
116 | Small images used to select product attributes.
117 | select
118 | adminhtml/system_config_source_yesno
119 | 40
120 | 1
121 | 1
122 | 1
123 |
124 |
125 |
126 |
127 |
128 | 30
129 | 1
130 | 1
131 | 1
132 | active
133 |
134 |
135 | 40
136 | tiny_compressimages/adminhtml_system_config_form_field_logStatus
137 | 1
138 | 1
139 | 1
140 |
141 |
142 |
143 |
144 |
145 | 40
146 | 1
147 | 1
148 | 1
149 |
150 |
151 |
152 | select
153 | 10
154 | tiny_compressimages/system_config_source_testlive
155 |
158 |
159 | Test: New product images will be optimized and saved so you can
160 | test if everything works as expected. However, the optimized
161 | images will not be shown in your store. After testing and switching
162 | to live mode, your store will show the optimized images.
163 |
164 |
165 | Disabled: The extension is switched off and no new product
166 | images will be optimized.
167 |
168 |
169 | ]]>
170 |
171 | If test mode is activated, the extension will do real compressions. The result won't be saved.
172 | 1
173 | 1
174 | 1
175 |
176 |
177 |
178 | 20
179 | tiny_compressimages/system_config_source_log
180 | select
181 |
182 | tiny_compressimages/system_config_backend_comment_loggingMode
183 |
184 | 1
185 | 1
186 | 1
187 |
188 |
189 |
190 | 30
191 | tiny_compressimages/adminhtml_system_config_form_field_logFile
192 | 1
193 | 1
194 | 1
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
--------------------------------------------------------------------------------
/lib/TinyCompress/test/TinifySourceTest.php:
--------------------------------------------------------------------------------
1 | dummyFile = __DIR__ . "/examples/dummy.png";
11 | }
12 |
13 | public function testWithInvalidApiKeyFromFileShouldThrowAccountException() {
14 | Tinify\setKey("invalid");
15 |
16 | CurlMock::register("https://api.tinify.com/shrink", array(
17 | "status" => 401, "body" => '{"error":"Unauthorized","message":"Credentials are invalid"}'
18 | ));
19 |
20 | $this->setExpectedException("Tinify\AccountException");
21 | Tinify\Source::fromFile($this->dummyFile);
22 | }
23 |
24 | public function testWithInvalidApiKeyFromBufferShouldThrowAccountException() {
25 | Tinify\setKey("invalid");
26 |
27 | CurlMock::register("https://api.tinify.com/shrink", array(
28 | "status" => 401, "body" => '{"error":"Unauthorized","message":"Credentials are invalid"}'
29 | ));
30 |
31 | $this->setExpectedException("Tinify\AccountException");
32 | Tinify\Source::fromBuffer("png file");
33 | }
34 |
35 | public function testWithInvalidApiKeyFromUrlShouldThrowAccountException() {
36 | Tinify\setKey("invalid");
37 |
38 | CurlMock::register("https://api.tinify.com/shrink", array(
39 | "status" => 401, "body" => '{"error":"Unauthorized","message":"Credentials are invalid"}'
40 | ));
41 |
42 | $this->setExpectedException("Tinify\AccountException");
43 | Tinify\Source::fromUrl("http://example.com/test.jpg");
44 | }
45 |
46 | public function testWithValidApiKeyFromFileShouldReturnSource() {
47 | Tinify\setKey("valid");
48 |
49 | CurlMock::register("https://api.tinify.com/shrink", array(
50 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
51 | ));
52 |
53 | $this->assertInstanceOf("Tinify\Source", Tinify\Source::fromFile($this->dummyFile));
54 | }
55 |
56 | public function testWithValidApiKeyFromFileShouldReturnSourceWithData() {
57 | Tinify\setKey("valid");
58 |
59 | CurlMock::register("https://api.tinify.com/shrink", array(
60 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
61 | ));
62 |
63 | CurlMock::register("https://api.tinify.com/some/location", array(
64 | "status" => 200, "body" => "compressed file"
65 | ));
66 |
67 | $this->assertSame("compressed file", Tinify\Source::fromFile($this->dummyFile)->toBuffer());
68 | }
69 |
70 | public function testWithValidApiKeyFromBufferShouldReturnSource() {
71 | Tinify\setKey("valid");
72 |
73 | CurlMock::register("https://api.tinify.com/shrink", array(
74 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
75 | ));
76 |
77 | $this->assertInstanceOf("Tinify\Source", Tinify\Source::fromBuffer("png file"));
78 | }
79 |
80 | public function testWithValidApiKeyFromBufferShouldReturnSourceWithData() {
81 | Tinify\setKey("valid");
82 |
83 | CurlMock::register("https://api.tinify.com/shrink", array(
84 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
85 | ));
86 |
87 | CurlMock::register("https://api.tinify.com/some/location", array(
88 | "status" => 200, "body" => "compressed file"
89 | ));
90 |
91 | $this->assertSame("compressed file", Tinify\Source::fromBuffer("png file")->toBuffer());
92 | }
93 |
94 | public function testWithValidApiKeyFromUrlShouldReturnSource() {
95 | Tinify\setKey("valid");
96 |
97 | CurlMock::register("https://api.tinify.com/shrink", array(
98 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
99 | ));
100 |
101 | $this->assertInstanceOf("Tinify\Source", Tinify\Source::fromUrl("http://example.com/testWithValidApiKey.jpg"));
102 | }
103 |
104 | public function testWithValidApiKeyFromUrlShouldReturnSourceWithData() {
105 | Tinify\setKey("valid");
106 |
107 | CurlMock::register("https://api.tinify.com/shrink", array(
108 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
109 | ));
110 |
111 | CurlMock::register("https://api.tinify.com/some/location", array(
112 | "status" => 200, "body" => "compressed file"
113 | ));
114 |
115 | $this->assertSame("compressed file", Tinify\Source::fromUrl("http://example.com/testWithValidApiKey.jpg")->toBuffer());
116 | }
117 |
118 | public function testWithValidApiKeyFromUrlShouldThrowExceptionIfRequestIsNotOK() {
119 | Tinify\setKey("valid");
120 |
121 | CurlMock::register("https://api.tinify.com/shrink", array(
122 | "status" => 400, "body" => '{"error":"Source not found","message":"Cannot parse URL"}'
123 | ));
124 |
125 | $this->setExpectedException("Tinify\ClientException");
126 | Tinify\Source::fromUrl("file://wrong");
127 | }
128 |
129 | public function testWithValidApiKeyResultShouldReturnResult() {
130 | Tinify\setKey("valid");
131 |
132 | CurlMock::register("https://api.tinify.com/shrink", array(
133 | "status" => 201,
134 | "headers" => array("Location" => "https://api.tinify.com/some/location"),
135 | ));
136 |
137 | CurlMock::register("https://api.tinify.com/some/location", array(
138 | "status" => 200, "body" => "compressed file"
139 | ));
140 |
141 | $this->assertInstanceOf("Tinify\Result", Tinify\Source::fromBuffer("png file")->result());
142 | }
143 |
144 | public function testWithValidApiKeyPreserveShouldReturnSource() {
145 | Tinify\setKey("valid");
146 |
147 | CurlMock::register("https://api.tinify.com/shrink", array(
148 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
149 | ));
150 |
151 | CurlMock::register("https://api.tinify.com/some/location", array(
152 | "status" => 200, "body" => "copyrighted file"
153 | ));
154 |
155 | $this->assertInstanceOf("Tinify\Source", Tinify\Source::fromBuffer("png file")->preserve("copyright", "location"));
156 | $this->assertSame("png file", CurlMock::last(CURLOPT_POSTFIELDS));
157 | }
158 |
159 | public function testWithValidApiKeyPreserveShouldReturnSourceWithData() {
160 | Tinify\setKey("valid");
161 |
162 | CurlMock::register("https://api.tinify.com/shrink", array(
163 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
164 | ));
165 |
166 | CurlMock::register("https://api.tinify.com/some/location", array(
167 | "status" => 200, "body" => "copyrighted file"
168 | ));
169 |
170 | $this->assertSame("copyrighted file", Tinify\Source::fromBuffer("png file")->preserve("copyright", "location")->toBuffer());
171 | $this->assertSame("{\"preserve\":[\"copyright\",\"location\"]}", CurlMock::last(CURLOPT_POSTFIELDS));
172 | }
173 |
174 | public function testWithValidApiKeyPreserveShouldReturnSourceWithDataForArray() {
175 | Tinify\setKey("valid");
176 |
177 | CurlMock::register("https://api.tinify.com/shrink", array(
178 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
179 | ));
180 |
181 | CurlMock::register("https://api.tinify.com/some/location", array(
182 | "status" => 200, "body" => "copyrighted file"
183 | ));
184 |
185 | $this->assertSame("copyrighted file", Tinify\Source::fromBuffer("png file")->preserve(array("copyright", "location"))->toBuffer());
186 | $this->assertSame("{\"preserve\":[\"copyright\",\"location\"]}", CurlMock::last(CURLOPT_POSTFIELDS));
187 | }
188 |
189 | public function testWithValidApiKeyPreserveShouldIncludeOtherOptionsIfSet() {
190 | Tinify\setKey("valid");
191 |
192 | CurlMock::register("https://api.tinify.com/shrink", array(
193 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
194 | ));
195 |
196 | CurlMock::register("https://api.tinify.com/some/location", array(
197 | "status" => 200, "body" => "copyrighted resized file"
198 | ));
199 |
200 | $source = Tinify\Source::fromBuffer("png file")->resize(array("width" => 400))->preserve(array("copyright", "location"));
201 |
202 | $this->assertSame("copyrighted resized file", $source->toBuffer());
203 | $this->assertSame("{\"resize\":{\"width\":400},\"preserve\":[\"copyright\",\"location\"]}", CurlMock::last(CURLOPT_POSTFIELDS));
204 | }
205 |
206 | public function testWithValidApiKeyResizeShouldReturnSource() {
207 | Tinify\setKey("valid");
208 |
209 | CurlMock::register("https://api.tinify.com/shrink", array(
210 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
211 | ));
212 |
213 | CurlMock::register("https://api.tinify.com/some/location", array(
214 | "status" => 200, "body" => "small file"
215 | ));
216 |
217 | $this->assertInstanceOf("Tinify\Source", Tinify\Source::fromBuffer("png file")->resize(array("width" => 400)));
218 | $this->assertSame("png file", CurlMock::last(CURLOPT_POSTFIELDS));
219 | }
220 |
221 | public function testWithValidApiKeyResizeShouldReturnSourceWithData() {
222 | Tinify\setKey("valid");
223 |
224 | CurlMock::register("https://api.tinify.com/shrink", array(
225 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
226 | ));
227 |
228 | CurlMock::register("https://api.tinify.com/some/location", array(
229 | "status" => 200, "body" => "small file"
230 | ));
231 |
232 | $this->assertSame("small file", Tinify\Source::fromBuffer("png file")->resize(array("width" => 400))->toBuffer());
233 | $this->assertSame("{\"resize\":{\"width\":400}}", CurlMock::last(CURLOPT_POSTFIELDS));
234 | }
235 |
236 | public function testWithValidApiKeyStoreShouldReturnResultMeta() {
237 | Tinify\setKey("valid");
238 |
239 | CurlMock::register("https://api.tinify.com/shrink", array(
240 | "status" => 201,
241 | "headers" => array("Location" => "https://api.tinify.com/some/location"),
242 | ));
243 |
244 | CurlMock::register("https://api.tinify.com/some/location", array(
245 | "body" => '{"store":{"service":"s3","aws_secret_access_key":"abcde"}}'
246 | ), array("status" => 200));
247 |
248 | $options = array("service" => "s3", "aws_secret_access_key" => "abcde");
249 | $this->assertInstanceOf("Tinify\Result", Tinify\Source::fromBuffer("png file")->store($options));
250 | $this->assertSame("{\"store\":{\"service\":\"s3\",\"aws_secret_access_key\":\"abcde\"}}", CurlMock::last(CURLOPT_POSTFIELDS));
251 | }
252 |
253 | public function testWithValidApiKeyStoreShouldReturnResultMetaWithLocation() {
254 | Tinify\setKey("valid");
255 |
256 | CurlMock::register("https://api.tinify.com/shrink", array(
257 | "status" => 201,
258 | "headers" => array("Location" => "https://api.tinify.com/some/location"),
259 | ));
260 |
261 | CurlMock::register("https://api.tinify.com/some/location", array(
262 | "body" => '{"store":{"service":"s3"}}'
263 | ), array(
264 | "status" => 201,
265 | "headers" => array("Location" => "https://bucket.s3.amazonaws.com/example"),
266 | ));
267 |
268 | $location = Tinify\Source::fromBuffer("png file")->store(array("service" => "s3"))->location();
269 | $this->assertSame("https://bucket.s3.amazonaws.com/example", $location);
270 | $this->assertSame("{\"store\":{\"service\":\"s3\"}}", CurlMock::last(CURLOPT_POSTFIELDS));
271 | }
272 |
273 | public function testWithValidApiKeyStoreShouldIncludeOtherOptionsIfSet() {
274 | Tinify\setKey("valid");
275 |
276 | CurlMock::register("https://api.tinify.com/shrink", array(
277 | "status" => 201,
278 | "headers" => array("Location" => "https://api.tinify.com/some/location"),
279 | ));
280 |
281 | CurlMock::register("https://api.tinify.com/some/location", array(
282 | "body" => '{"resize":{"width":300},"store":{"service":"s3","aws_secret_access_key":"abcde"}}'
283 | ), array("status" => 200));
284 |
285 | $options = array("service" => "s3", "aws_secret_access_key" => "abcde");
286 | $this->assertInstanceOf("Tinify\Result", Tinify\Source::fromBuffer("png file")->resize(array("width" => 300))->store($options));
287 | $this->assertSame("{\"resize\":{\"width\":300},\"store\":{\"service\":\"s3\",\"aws_secret_access_key\":\"abcde\"}}", CurlMock::last(CURLOPT_POSTFIELDS));
288 | }
289 |
290 | public function testWithValidApiKeyToBufferShouldReturnImageData() {
291 | Tinify\setKey("valid");
292 |
293 | CurlMock::register("https://api.tinify.com/shrink", array(
294 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
295 | ));
296 | CurlMock::register("https://api.tinify.com/some/location", array(
297 | "status" => 200, "body" => "compressed file"
298 | ));
299 |
300 | $this->assertSame("compressed file", Tinify\Source::fromBuffer("png file")->toBuffer());
301 | }
302 |
303 | public function testWithValidApiKeyToFileShouldStoreImageData() {
304 | Tinify\setKey("valid");
305 |
306 | CurlMock::register("https://api.tinify.com/shrink", array(
307 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
308 | ));
309 |
310 | CurlMock::register("https://api.tinify.com/some/location", array(
311 | "status" => 200, "body" => "compressed file"
312 | ));
313 |
314 | $path = tempnam(sys_get_temp_dir(), "tinify-php");
315 | Tinify\Source::fromBuffer("png file")->toFile($path);
316 | $this->assertSame("compressed file", file_get_contents($path));
317 | }
318 | }
319 |
--------------------------------------------------------------------------------