__('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 |
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 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
88 |
--------------------------------------------------------------------------------
/app/etc/modules/Tiny_CompressImages.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 | community
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tinify/magento1",
3 | "description": "Speed up your Magento 1 webshop. Optimize your JPEG and PNG images automatically with TinyPNG.",
4 | "keywords": [
5 | "tinify",
6 | "tinypng",
7 | "tinyjpg",
8 | "compress",
9 | "images",
10 | "api",
11 | "magento",
12 | "magento2",
13 | "plugin",
14 | "module"
15 | ],
16 | "type": "magento-module",
17 | "license": "MIT",
18 | "require": {},
19 | "authors": [
20 | {
21 | "name": "TIG",
22 | "email": "servicedesk@tig.nl",
23 | "homepage": "http://tig.nl"
24 | }
25 | ],
26 | "support": {
27 | "email": "servicedesk@tig.nl",
28 | "issues": "http://servicedesk.tig.nl/hc/nl/requests/new"
29 | },
30 | "homepage": "https://tig.nl/image-optimization-magento-extension-community-edition/",
31 | "minimum-stability": "stable"
32 | }
33 |
--------------------------------------------------------------------------------
/lib/TinyCompress/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2013-2018 Tinify
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/TinyCompress/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/tinify/tinify-php)
2 |
3 | # Tinify API client for PHP
4 |
5 | PHP client for the Tinify API, used for [TinyPNG](https://tinypng.com) and [TinyJPG](https://tinyjpg.com). Tinify compresses your images intelligently. Read more at [http://tinify.com](http://tinify.com).
6 |
7 | ## Documentation
8 |
9 | [Go to the documentation for the PHP client](https://tinypng.com/developers/reference/php).
10 |
11 | ## Installation
12 |
13 | Install the API client with Composer. Add this to your `composer.json`:
14 |
15 | ```json
16 | {
17 | "require": {
18 | "tinify/tinify": "*"
19 | }
20 | }
21 | ```
22 |
23 | Then install with:
24 |
25 | ```
26 | composer install
27 | ```
28 |
29 | Use autoloading to make the client available in PHP:
30 |
31 | ```php
32 | require_once("vendor/autoload.php");
33 | ```
34 |
35 | ## Usage
36 |
37 | ```php
38 | Tinify\setKey("YOUR_API_KEY");
39 | Tinify\fromFile("unoptimized.png")->toFile("optimized.png");
40 | ```
41 |
42 | ## Running tests
43 |
44 | ```
45 | composer install
46 | vendor/bin/phpunit
47 | ```
48 |
49 | ### Integration tests
50 |
51 | ```
52 | composer install
53 | TINIFY_KEY=$YOUR_API_KEY vendor/bin/phpunit --no-configuration test/integration.php
54 | ```
55 |
56 | ## License
57 |
58 | This software is licensed under the MIT License. [View the license](LICENSE).
59 |
--------------------------------------------------------------------------------
/lib/TinyCompress/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tinify/tinify",
3 | "description": "PHP client for the Tinify API. Tinify compresses your images intelligently. Read more at https://tinify.com.",
4 | "keywords": [
5 | "tinify",
6 | "tinypng",
7 | "tinyjpg",
8 | "compress",
9 | "images",
10 | "api"
11 | ],
12 |
13 | "homepage": "https://tinify.com/developers",
14 | "license": "MIT",
15 |
16 | "support": {
17 | "email": "support@tinify.com"
18 | },
19 |
20 | "authors": [{
21 | "name": "Rolf Timmermans",
22 | "email": "rolftimmermans@voormedia.com"
23 | }],
24 |
25 | "require": {
26 | "php": ">=5.3.0",
27 | "ext-curl": "*",
28 | "ext-json": "*",
29 | "lib-curl": ">=7.20.0"
30 | },
31 |
32 | "require-dev": {
33 | "symfony/yaml": "~2.0",
34 | "phpunit/phpunit": "~4.0"
35 | },
36 |
37 | "autoload": {
38 | "files": ["lib/Tinify.php", "lib/Tinify/Exception.php"],
39 | "psr-4": {"Tinify\\": "lib/Tinify/"}
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/TinyCompress/lib/Tinify.php:
--------------------------------------------------------------------------------
1 | $email), $options);
33 | $response = self::getClient(self::ANONYMOUS)->request("post", "/keys", $body);
34 | self::setKey($response->body->key);
35 | }
36 |
37 | public static function setAppIdentifier($appIdentifier) {
38 | self::$appIdentifier = $appIdentifier;
39 | self::$client = NULL;
40 | }
41 |
42 | public static function setProxy($proxy) {
43 | self::$proxy = $proxy;
44 | self::$client = NULL;
45 | }
46 |
47 | public static function getCompressionCount() {
48 | return self::$compressionCount;
49 | }
50 |
51 | public static function setCompressionCount($compressionCount) {
52 | self::$compressionCount = $compressionCount;
53 | }
54 |
55 | public static function getRemainingCredits() {
56 | return self::$remainingCredits;
57 | }
58 |
59 | public static function setRemainingCredits($remainingCredits) {
60 | self::$remainingCredits = $remainingCredits;
61 | }
62 |
63 | public static function getPayingState() {
64 | return self::$payingState;
65 | }
66 |
67 | public static function setPayingState($payingState) {
68 | self::$payingState = $payingState;
69 | }
70 |
71 | public static function getEmailAddress() {
72 | return self::$emailAddress;
73 | }
74 |
75 | public static function setEmailAddress($emailAddress) {
76 | self::$emailAddress = $emailAddress;
77 | }
78 |
79 | public static function getClient($mode = self::AUTHENTICATED) {
80 | if ($mode == self::AUTHENTICATED && !self::$key) {
81 | throw new AccountException("Provide an API key with Tinify\setKey(...)");
82 | }
83 |
84 | if (!self::$client) {
85 | self::$client = new Client(self::$key, self::$appIdentifier, self::$proxy);
86 | }
87 |
88 | return self::$client;
89 | }
90 |
91 | public static function setClient($client) {
92 | self::$client = $client;
93 | }
94 | }
95 |
96 | function setKey($key) {
97 | return Tinify::setKey($key);
98 | }
99 |
100 | function getKey() {
101 | return Tinify::getKey();
102 | }
103 |
104 | function createKey($email, $options) {
105 | return Tinify::createKey($email, $options);
106 | }
107 |
108 | function setAppIdentifier($appIdentifier) {
109 | return Tinify::setAppIdentifier($appIdentifier);
110 | }
111 |
112 | function setProxy($proxy) {
113 | return Tinify::setProxy($proxy);
114 | }
115 |
116 | function getCompressionCount() {
117 | return Tinify::getCompressionCount();
118 | }
119 |
120 | function compressionCount() {
121 | return Tinify::getCompressionCount();
122 | }
123 |
124 | function remainingCredits() {
125 | return Tinify::getRemainingCredits();
126 | }
127 |
128 | function payingState() {
129 | return Tinify::getPayingState();
130 | }
131 |
132 | function emailAddress() {
133 | return Tinify::getEmailAddress();
134 | }
135 |
136 | function fromFile($path) {
137 | return Source::fromFile($path);
138 | }
139 |
140 | function fromBuffer($string) {
141 | return Source::fromBuffer($string);
142 | }
143 |
144 | function fromUrl($string) {
145 | return Source::fromUrl($string);
146 | }
147 |
148 | function validate() {
149 | try {
150 | Tinify::getClient()->request("get", "/keys/" . Tinify::getKey());
151 | return true;
152 | } catch (AccountException $err) {
153 | if ($err->status == 429) return true;
154 | throw $err;
155 | } catch (ClientException $err) {
156 | return true;
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/TinyCompress/lib/Tinify/Exception.php:
--------------------------------------------------------------------------------
1 | = 400 && $status <= 499) {
12 | $klass = "Tinify\ClientException";
13 | } else if($status >= 500 && $status <= 599) {
14 | $klass = "Tinify\ServerException";
15 | } else {
16 | $klass = "Tinify\Exception";
17 | }
18 |
19 | if (empty($message)) $message = "No message was provided";
20 | return new $klass($message, $type, $status);
21 | }
22 |
23 | function __construct($message, $type = NULL, $status = NULL) {
24 | $this->status = $status;
25 | if ($status) {
26 | parent::__construct($message . " (HTTP " . $status . "/" . $type . ")");
27 | } else {
28 | parent::__construct($message);
29 | }
30 | }
31 | }
32 |
33 | class AccountException extends Exception {}
34 | class ClientException extends Exception {}
35 | class ServerException extends Exception {}
36 | class ConnectionException extends Exception {}
37 |
--------------------------------------------------------------------------------
/lib/TinyCompress/lib/Tinify/Result.php:
--------------------------------------------------------------------------------
1 | meta = $meta;
10 | $this->data = $data;
11 | }
12 |
13 | public function data() {
14 | return $this->data;
15 | }
16 |
17 | public function toBuffer() {
18 | return $this->data;
19 | }
20 |
21 | public function toFile($path) {
22 | return file_put_contents($path, $this->toBuffer());
23 | }
24 |
25 | public function size() {
26 | return intval($this->meta["content-length"]);
27 | }
28 |
29 | public function mediaType() {
30 | return $this->meta["content-type"];
31 | }
32 |
33 | public function contentType() {
34 | return $this->mediaType();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/TinyCompress/lib/Tinify/ResultMeta.php:
--------------------------------------------------------------------------------
1 | meta = $meta;
10 | }
11 |
12 | public function width() {
13 | return intval($this->meta["image-width"]);
14 | }
15 |
16 | public function height() {
17 | return intval($this->meta["image-height"]);
18 | }
19 |
20 | public function location() {
21 | return isset($this->meta["location"]) ? $this->meta["location"] : null;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/TinyCompress/lib/Tinify/Source.php:
--------------------------------------------------------------------------------
1 | request("post", "/shrink", $string);
14 | return new self($response->headers["location"]);
15 | }
16 |
17 | public static function fromUrl($url) {
18 | $body = array("source" => array("url" => $url));
19 | $response = Tinify::getClient()->request("post", "/shrink", $body);
20 | return new self($response->headers["location"]);
21 | }
22 |
23 | public function __construct($url, $commands = array()) {
24 | $this->url = $url;
25 | $this->commands = $commands;
26 | }
27 |
28 | public function preserve() {
29 | $options = $this->flatten(func_get_args());
30 | $commands = array_merge($this->commands, array("preserve" => $options));
31 | return new self($this->url, $commands);
32 | }
33 |
34 | public function resize($options) {
35 | $commands = array_merge($this->commands, array("resize" => $options));
36 | return new self($this->url, $commands);
37 | }
38 |
39 | public function store($options) {
40 | $response = Tinify::getClient()->request("post", $this->url,
41 | array_merge($this->commands, array("store" => $options)));
42 | return new Result($response->headers, $response->body);
43 | }
44 |
45 | public function result() {
46 | $response = Tinify::getClient()->request("get", $this->url, $this->commands);
47 | return new Result($response->headers, $response->body);
48 | }
49 |
50 | public function toFile($path) {
51 | return $this->result()->toFile($path);
52 | }
53 |
54 | public function toBuffer() {
55 | return $this->result()->toBuffer();
56 | }
57 |
58 | private static function flatten($options) {
59 | $flattened = array();
60 | foreach ($options as $option) {
61 | if (is_array($option)) {
62 | $flattened = array_merge($flattened, $option);
63 | } else {
64 | array_push($flattened, $option);
65 | }
66 | }
67 | return $flattened;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/lib/TinyCompress/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | test
5 |
6 |
7 |
8 |
9 | lib
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/TinyCompress/test/TinifyExceptionTest.php:
--------------------------------------------------------------------------------
1 | assertSame(401, $err->status);
7 | }
8 |
9 | public function testStatusShouldReturnNullIfUnset() {
10 | $err = new Tinify\Exception("Message", "Error");
11 | $this->assertSame(null, $err->status);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/TinyCompress/test/TinifyResultMetaTest.php:
--------------------------------------------------------------------------------
1 | "100"));
8 | $this->assertSame(100, $result->width());
9 | }
10 |
11 | public function testWithMetadataHeightShouldReturnImageHeight() {
12 | $result = new Tinify\ResultMeta(array("image-height" => "60"));
13 | $this->assertSame(60, $result->height());
14 | }
15 |
16 | public function testWithMetadataLocationShouldReturnImageLocation() {
17 | $result = new Tinify\ResultMeta(array("location" => "https://example.com/image.png"));
18 | $this->assertSame("https://example.com/image.png", $result->location());
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/TinyCompress/test/TinifyResultTest.php:
--------------------------------------------------------------------------------
1 | "100"), "image data");
8 | $this->assertSame(100, $result->width());
9 | }
10 |
11 | public function testWithMetaAndDataHeightShouldReturnImageHeight() {
12 | $result = new Tinify\Result(array("image-height" => "60"), "image data");
13 | $this->assertSame(60, $result->height());
14 | }
15 |
16 | public function testWithMetaAndDataLocationShouldReturnNull() {
17 | $result = new Tinify\ResultMeta(array(), "image data");
18 | $this->assertSame(null, $result->location());
19 | }
20 |
21 | public function testWithMetaAndDataSizeShouldReturnContentLength() {
22 | $result = new Tinify\Result(array("content-length" => "450"), "image data");
23 | $this->assertSame(450, $result->size());
24 | }
25 |
26 | public function testWithMetaAndDataContentTypeShouldReturnMimeType() {
27 | $result = new Tinify\Result(array("content-type" => "image/png"), "image data");
28 | $this->assertSame("image/png", $result->contentType());
29 | }
30 |
31 | public function testWithMetaAndDataToBufferShouldReturnImageData() {
32 | $result = new Tinify\Result(array(), "image data");
33 | $this->assertSame("image data", $result->toBuffer());
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/TinyCompress/test/TinifyTest.php:
--------------------------------------------------------------------------------
1 | dummyFile = __DIR__ . "/examples/dummy.png";
11 | }
12 |
13 | public function testGetKeyWithoutKeyShouldReturnNull() {
14 | $this->assertSame(NULL, Tinify\getKey());
15 | }
16 |
17 | public function testGetKeyWithKeyShouldReturnKey() {
18 | Tinify\setKey("abcde");
19 | $this->assertSame("abcde", Tinify\getKey());
20 | }
21 |
22 | public function testCreateKeyWithNewEmailShouldSetKey() {
23 | CurlMock::register("https://api.tinify.com/keys", array(
24 | "status" => 202,
25 | "body" => '{"key":"abcdefg123"}',
26 | "headers" => array("Content-Type" => "application/json"),
27 | ));
28 |
29 | Tinify\createKey("user@example.com", array(
30 | "name" => "John",
31 | "identifier" => "My Tinify plugin",
32 | "link" => "https://mywebsite.example.com/admin/settings",
33 | ));
34 |
35 | $this->assertSame("abcdefg123", Tinify\getKey());
36 | }
37 |
38 | public function testCreateKeyWithDuplicateEmailShouldThrowClientException() {
39 | CurlMock::register("https://api.tinify.com/keys", array(
40 | "status" => 403,
41 | "body" => '{"error":"Duplicate registration","message":"This email address has already been used"}',
42 | ));
43 |
44 | $this->setExpectedException("Tinify\AccountException");
45 | Tinify\createKey("user@example.com", array(
46 | "name" => "John",
47 | "identifier" => "My Tinify plugin",
48 | "link" => "https://mywebsite.example.com/admin/settings",
49 | ));
50 | }
51 |
52 | public function testKeyShouldResetClientWithNewKey() {
53 | CurlMock::register("https://api.tinify.com/", array("status" => 200));
54 | Tinify\setKey("abcde");
55 | Tinify\Tinify::getClient();
56 | Tinify\setKey("fghij");
57 | $client = Tinify\Tinify::getClient();
58 | $client->request("get", "/");
59 |
60 | $this->assertSame("api:fghij", CurlMock::last(CURLOPT_USERPWD));
61 | }
62 |
63 | public function testAppIdentifierShouldResetClientWithNewAppIdentifier() {
64 | CurlMock::register("https://api.tinify.com/", array("status" => 200));
65 | Tinify\setKey("abcde");
66 | Tinify\setAppIdentifier("MyApp/1.0");
67 | Tinify\Tinify::getClient();
68 | Tinify\setAppIdentifier("MyApp/2.0");
69 | $client = Tinify\Tinify::getClient();
70 | $client->request("get", "/");
71 |
72 | $this->assertSame(Tinify\Client::userAgent() . " MyApp/2.0", CurlMock::last(CURLOPT_USERAGENT));
73 | }
74 |
75 | public function testProxyShouldResetClientWithNewProxy() {
76 | CurlMock::register("https://api.tinify.com/", array("status" => 200));
77 | Tinify\setKey("abcde");
78 | Tinify\setProxy("http://localhost");
79 | Tinify\Tinify::getClient();
80 | Tinify\setProxy("http://user:pass@localhost:8080");
81 | $client = Tinify\Tinify::getClient();
82 | $client->request("get", "/");
83 |
84 | $this->assertSame(Tinify\Client::userAgent() . " MyApp/2.0", CurlMock::last(CURLOPT_USERAGENT));
85 | }
86 |
87 | public function testClientWithKeyShouldReturnClient() {
88 | Tinify\setKey("abcde");
89 | $this->assertInstanceOf("Tinify\Client", Tinify\Tinify::getClient());
90 | }
91 |
92 | public function testClientWithoutKeyShouldThrowException() {
93 | $this->setExpectedException("Tinify\AccountException");
94 | Tinify\Tinify::getClient();
95 | }
96 |
97 | public function testClientWithInvalidProxyShouldThrowException() {
98 | $this->setExpectedException("Tinify\ConnectionException");
99 | Tinify\setKey("abcde");
100 | Tinify\setProxy("http-bad-url");
101 | Tinify\Tinify::getClient();
102 | }
103 |
104 | public function testSetClientShouldReplaceClient() {
105 | Tinify\setKey("abcde");
106 | Tinify\Tinify::setClient("foo");
107 | $this->assertSame("foo", Tinify\Tinify::getClient());
108 | }
109 |
110 | public function testValidateWithValidKeyShouldReturnTrue() {
111 | Tinify\setKey("valid");
112 | CurlMock::register("https://api.tinify.com/keys/valid", array(
113 | "status" => 200, "body" => '{}'
114 | ));
115 | $this->assertTrue(Tinify\validate());
116 | }
117 |
118 | public function testValidateWithLimitedKeyShouldReturnTrue() {
119 | Tinify\setKey("limited");
120 | CurlMock::register("https://api.tinify.com/keys/limited", array(
121 | "status" => 200, "body" => '{}'
122 | ));
123 | $this->assertTrue(Tinify\validate());
124 | }
125 |
126 | public function testValidateWithErrorShouldThrowException() {
127 | Tinify\setKey("invalid");
128 | CurlMock::register("https://api.tinify.com/keys/invalid", array(
129 | "status" => 404, "body" => '{}'
130 | ));
131 | $this->setExpectedException("Tinify\AccountException");
132 | Tinify\validate();
133 | }
134 |
135 | public function testFromFileShouldReturnSource() {
136 | CurlMock::register("https://api.tinify.com/shrink", array(
137 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
138 | ));
139 | Tinify\setKey("valid");
140 | $this->assertInstanceOf("Tinify\Source", Tinify\fromFile($this->dummyFile));
141 | }
142 |
143 | public function testFromBufferShouldReturnSource() {
144 | CurlMock::register("https://api.tinify.com/shrink", array(
145 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
146 | ));
147 | Tinify\setKey("valid");
148 | $this->assertInstanceOf("Tinify\Source", Tinify\fromBuffer("png file"));
149 | }
150 |
151 | public function testFromUrlShouldReturnSource() {
152 | CurlMock::register("https://api.tinify.com/shrink", array(
153 | "status" => 201, "headers" => array("Location" => "https://api.tinify.com/some/location")
154 | ));
155 | Tinify\setKey("valid");
156 | $this->assertInstanceOf("Tinify\Source", Tinify\fromUrl("http://example.com/test.jpg"));
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/lib/TinyCompress/test/curl_mock.php:
--------------------------------------------------------------------------------
1 | 471808,
11 | "version" => "7.51.0",
12 | "features" => 951197,
13 | );
14 |
15 | private static $urls = array();
16 | private static $requests = array();
17 | private static $version = array();
18 |
19 | public $options = array();
20 | public $response;
21 | public $closed = false;
22 |
23 | public static function version_info() {
24 | return self::$version;
25 | }
26 |
27 | public static function set_version_info_key($key, $value) {
28 | self::$version[$key] = $value;
29 | }
30 |
31 | public static function register($url, $request, $response = NULL) {
32 | if (!$response) {
33 | $response = $request;
34 | $request = NULL;
35 | }
36 |
37 | if (!isset(self::$urls[$url])) {
38 | self::$urls[$url] = array();
39 | }
40 |
41 | array_push(self::$urls[$url], array($request, $response));
42 | }
43 |
44 | public static function reset() {
45 | self::$requests = array();
46 | self::$urls = array();
47 | self::$version = self::$defaultVersion;
48 | }
49 |
50 | public static function last_has($key) {
51 | $lastReq = self::$requests[count(self::$requests) - 1];
52 | return array_key_exists($key, $lastReq->options);
53 | }
54 |
55 | public static function last($key = null) {
56 | $lastReq = self::$requests[count(self::$requests) - 1];
57 | if ($key) {
58 | return $lastReq->options[$key];
59 | } else {
60 | return $lastReq;
61 | }
62 | }
63 |
64 | public function close() {
65 | $this->closed = true;
66 | }
67 |
68 | public function exec() {
69 | if ($this->closed) {
70 | throw new CurlMockException("Curl already closed");
71 | }
72 | array_push(self::$requests, $this);
73 |
74 | $queue = &self::$urls[$this->options[CURLOPT_URL]];
75 | list($this->request, $this->response) = $queue[0];
76 |
77 | /* Keep last request as fallback. */
78 | if (count($queue) > 1) array_shift($queue);
79 |
80 | if ($this->request) {
81 | if ($this->request["body"]) {
82 | if ($this->options[CURLOPT_POSTFIELDS] != $this->request["body"]) {
83 | throw new Exception("Body '" . $this->options[CURLOPT_POSTFIELDS] .
84 | "' does not match expected '" . $this->request["body"] . "'");
85 | }
86 | }
87 | }
88 |
89 | if (isset($this->response["headers"])) {
90 | $headers = "";
91 | foreach ($this->response["headers"] as $header => $value) {
92 | $headers .= $header . ": " . $value . "\r\n";
93 | }
94 | $this->response["headers"] = $headers . "\r\n";
95 | } else {
96 | $this->response["headers"] = "\r\n";
97 | }
98 |
99 | if (!isset($this->response["body"])) {
100 | $this->response["body"] = "";
101 | }
102 |
103 | if (array_key_exists("return", $this->response)) {
104 | return $this->response["return"];
105 | } else if (isset($this->response["status"])) {
106 | return $this->response["headers"] . $this->response["body"];
107 | } else {
108 | return false;
109 | }
110 | }
111 |
112 | public function setopt_array($array) {
113 | if ($this->closed) {
114 | throw new CurlMockException("Curl already closed");
115 | }
116 | foreach ($array as $key => $value) {
117 | $this->options[$key] = $value;
118 | }
119 | }
120 |
121 | public function setopt($key, $value) {
122 | if ($this->closed) {
123 | throw new CurlMockException("Curl already closed");
124 | }
125 | $this->options[$key] = $value;
126 | }
127 |
128 | public function getinfo($key) {
129 | if ($this->closed) {
130 | throw new CurlMockException("Curl already closed");
131 | }
132 | switch ($key) {
133 | case CURLINFO_HTTP_CODE:
134 | return isset($this->response["status"]) ? $this->response["status"] : 0;
135 | case CURLINFO_HEADER_SIZE:
136 | return strlen($this->response["headers"]);
137 | default:
138 | throw new Exception("Bad key $key");
139 | }
140 | }
141 |
142 | public function error() {
143 | if ($this->closed) {
144 | throw new CurlMockException("Curl already closed");
145 | }
146 | return $this->response["error"];
147 | }
148 |
149 | public function errno() {
150 | if ($this->closed) {
151 | throw new CurlMockException("Curl already closed");
152 | }
153 | return $this->response["errno"];
154 | }
155 | }
156 |
157 | function curl_version() {
158 | return CurlMock::version_info();
159 | }
160 |
161 | function curl_init() {
162 | return new CurlMock();
163 | }
164 |
165 | function curl_exec($mock) {
166 | return $mock->exec();
167 | }
168 |
169 | function curl_close($mock) {
170 | $mock->close();
171 | }
172 |
173 | function curl_setopt_array($mock, $array) {
174 | return $mock->setopt_array($array);
175 | }
176 |
177 | function curl_setopt($mock, $key, $value) {
178 | return $mock->setopt($key, $value);
179 | }
180 |
181 | function curl_getinfo($mock, $key) {
182 | return $mock->getinfo($key);
183 | }
184 |
185 | function curl_error($mock) {
186 | return $mock->error();
187 | }
188 |
189 | function curl_errno($mock) {
190 | return $mock->errno();
191 | }
192 |
--------------------------------------------------------------------------------
/lib/TinyCompress/test/examples/dummy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tinify/magento1-plugin/5ab33d16814ee21bcf251a89bcc87d2f914058a7/lib/TinyCompress/test/examples/dummy.png
--------------------------------------------------------------------------------
/lib/TinyCompress/test/examples/voormedia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tinify/magento1-plugin/5ab33d16814ee21bcf251a89bcc87d2f914058a7/lib/TinyCompress/test/examples/voormedia.png
--------------------------------------------------------------------------------
/lib/TinyCompress/test/helper.php:
--------------------------------------------------------------------------------
1 | toFile($path);
22 |
23 | $size = filesize($path);
24 | $contents = fread(fopen($path, "rb"), $size);
25 |
26 | $this->assertGreaterThan(1000, $size);
27 | $this->assertLessThan(1500, $size);
28 |
29 | /* width == 137 */
30 | $this->assertContains("\0\0\0\x89", $contents);
31 | $this->assertNotContains("Copyright Voormedia", $contents);
32 | }
33 |
34 | public function testShouldCompressFromUrl() {
35 | $path = tempnam(sys_get_temp_dir(), "tinify-php");
36 | $source = \Tinify\fromUrl("https://raw.githubusercontent.com/tinify/tinify-php/master/test/examples/voormedia.png");
37 | $source->toFile($path);
38 |
39 | $size = filesize($path);
40 | $contents = fread(fopen($path, "rb"), $size);
41 |
42 | $this->assertGreaterThan(1000, $size);
43 | $this->assertLessThan(1500, $size);
44 |
45 | /* width == 137 */
46 | $this->assertContains("\0\0\0\x89", $contents);
47 | $this->assertNotContains("Copyright Voormedia", $contents);
48 | }
49 |
50 | public function testShouldResize() {
51 | $path = tempnam(sys_get_temp_dir(), "tinify-php");
52 | self::$optimized->resize(array("method" => "fit", "width" => 50, "height" => 20))->toFile($path);
53 |
54 | $size = filesize($path);
55 | $contents = fread(fopen($path, "rb"), $size);
56 |
57 | $this->assertGreaterThan(500, $size);
58 | $this->assertLessThan(1000, $size);
59 |
60 | /* width == 50 */
61 | $this->assertContains("\0\0\0\x32", $contents);
62 | $this->assertNotContains("Copyright Voormedia", $contents);
63 | }
64 |
65 | public function testShouldPreserveMetadata() {
66 | $path = tempnam(sys_get_temp_dir(), "tinify-php");
67 | self::$optimized->preserve("copyright", "creation")->toFile($path);
68 |
69 | $size = filesize($path);
70 | $contents = fread(fopen($path, "rb"), $size);
71 |
72 | $this->assertGreaterThan(1000, $size);
73 | $this->assertLessThan(2000, $size);
74 |
75 | /* width == 137 */
76 | $this->assertContains("\0\0\0\x89", $contents);
77 | $this->assertContains("Copyright Voormedia", $contents);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/TinyCompress/update-cacert.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | dir=lib/data
3 |
4 | cert=0
5 | curl https://curl.haxx.se/ca/cacert.pem | while read line; do
6 | if [ "-----BEGIN CERTIFICATE-----" == "$line" ]; then
7 | cert=1
8 | echo $line
9 | elif [ "-----END CERTIFICATE-----" == "$line" ]; then
10 | cert=0
11 | echo $line
12 | else
13 | if [ $cert == 1 ]; then
14 | echo $line
15 | fi
16 | fi
17 | done > $dir/cacert.pem
18 |
--------------------------------------------------------------------------------
/modman:
--------------------------------------------------------------------------------
1 | app/code/community/Tiny/CompressImages/
2 | app/design/adminhtml/default/default/layout/Tiny/CompressImages.xml
3 | app/design/adminhtml/default/default/template/Tiny/CompressImages/
4 | app/etc/modules/Tiny_CompressImages.xml
5 | app/locale/en_US/Tiny_CompressImages.csv
6 | lib/TinyCompress/
7 | skin/adminhtml/default/default/css/Tiny/CompressImages/
8 | skin/adminhtml/default/default/images/Tiny/CompressImages/
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 | app/code/community/Tiny/CompressImages/Test
9 |
10 |
11 |
12 |
13 |
14 | app/code/community/Tiny/CompressImages/Block
15 | app/code/community/Tiny/CompressImages/controllers
16 | app/code/community/Tiny/CompressImages/Helper
17 | app/code/community/Tiny/CompressImages/Model
18 | app/code/community/Tiny/CompressImages/Exception.php
19 |
20 |
21 |
--------------------------------------------------------------------------------
/screenshots/magento-config-page.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tinify/magento1-plugin/5ab33d16814ee21bcf251a89bcc87d2f914058a7/screenshots/magento-config-page.jpg
--------------------------------------------------------------------------------
/skin/adminhtml/default/default/css/Tiny/CompressImages/config.css:
--------------------------------------------------------------------------------
1 | /* Default Magento Override */
2 |
3 | .content-header h3 {
4 | color:#253033;
5 | }
6 |
7 | /* Modus radios */
8 | .wrapper-radio { margin-bottom: 3px; }
9 | .wrapper-radio input { margin-right: 5px; }
10 | #row_tiny_compressimages_status_usage .label { vertical-align: middle; }
11 | .radio-container { display: inline-block; }
12 | .radio-container + .field-tooltip { float:right; }
13 |
14 | /* ======== Logo / Identity ======== */
15 |
16 | /* logo in title bar */
17 | #tiny_compressimages_support .content-heading { background: url('../../../images/Tiny/CompressImages/george-menu-icon@2x.png') no-repeat 0 0px !important;
18 | background-size: auto 40px !important;
19 | line-height: 40px;
20 | padding-left: 50px;
21 | padding-bottom: 5px;
22 | }
23 |
24 | /* logo in left column */
25 | .tiny-compressimages-section { position: relative; line-height: 24px; }
26 | .tiny-compressimages-section span { padding-left: 50px !important; }
27 | .tiny-compressimages-section:after { content: '';
28 | display: block;
29 | position: absolute;
30 | top: 3px;
31 | left: 18px;
32 | height: 40px;
33 | width: 54px;
34 | background: url('../../../images/Tiny/CompressImages/george-menu-icon@2x.png') no-repeat 0 0px !important;
35 | background-size: auto 25px !important;
36 | }
37 | /* ======== Support Tab General ====== */
38 |
39 | #tiny_compressimages_support { position :relative;}
40 | #tiny_compressimages_support p,
41 | #tiny_compressimages_support ul,
42 | #tiny_compressimages_support ol { padding: 6px 0 10px 0; }
43 |
44 | #tiny_compressimages_support ol { list-style: decimal inside; }
45 | #tiny_compressimages_support ol ol { list-style: lower-alpha inside; padding: 0 0 10px 12px; }
46 | #tiny_compressimages_support ul ul { list-style:none; padding: 0 0 10px 12px; }
47 | #tiny_compressimages_support h2.content-heading { margin-right: 190px; border-bottom: 4px solid #dfdfdf; }
48 | #tiny_compressimages_support h2.content-heading .content-heading-logo { height: 24px; }
49 |
50 | #tiny_compressimages_support h4,
51 | #tiny_compressimages_support p { margin: 0; }
52 |
53 | /* ======== Support Tab ====== */
54 |
55 | .tiny-logo-bear {
56 | position: absolute;
57 | bottom: 0;
58 | right: 0;
59 | width: auto;
60 | max-width: 20%;
61 | height: auto;
62 | max-height: 180px; }
63 |
64 | fieldset#tiny_compressimages_support { padding-bottom: 0; }
65 |
66 | #tiny_compressimages_support a:hover { text-decoration:none; }
67 | #tiny_compressimages_support img { max-width: 100%; }
68 |
69 | #tiny_compressimages_support .col-set { float:left; width: 100%; }
70 | #tiny_compressimages_support .col-set .col-1 { float:left; width: 75%; box-sizing:border-box; padding-right: 10px; }
71 | #tiny_compressimages_support .col-set .col-2 { float:left; width: 25%; box-sizing:border-box; padding-left: 10px;}
72 |
73 | #tiny_compressimages_support .tig_branding { float:left; width: 100%; }
74 | #tiny_compressimages_support .tig_branding .image { float:left; width: 50px; margin-top: 3px; }
75 | #tiny_compressimages_support .tig_branding .signature{ float:left; width: calc(100% - 50px); }
76 |
77 | /* ======== Connection Settings ======== */
78 |
79 | #compressimages_api_status { color: #009f3e;float: left; padding-bottom: 5px; clear: both; }
80 | .compressimages-api-deactivated { color: #cc0000; }
81 |
82 | #row_tiny_compressimages_settings_credits .value{
83 | position: relative;
84 | }
85 |
86 | .tinypng-upgrade-button {
87 | padding: 8px 15px;
88 | text-align: center;
89 | text-decoration: none;
90 | font-size: 13px;
91 | background-color: #3abbd0;
92 | border: none;
93 | border-bottom: 3px solid #269eb1;
94 | border-radius: 3px;
95 | color: #fff;
96 | font-weight: bold;
97 | cursor: pointer;
98 | font-family: Arial, Helvetica, sans-serif;
99 | position: absolute;
100 | right: -145px;
101 | top: 60px;
102 | letter-spacing: 0.2px;
103 | }
104 |
105 | .tinypng-upgrade-button:hover {
106 | background-color:#359cb1;
107 | border-bottom: 3px solid #348297;
108 | color: #fff;
109 | text-decoration :none;
110 | }
111 |
112 | .compressimages_status_success .indicator,
113 | .compressimages_status_failure .indicator {
114 | vertical-align: top;
115 | display: inline-block;
116 | margin-right: 3px;
117 | }
118 |
119 | .compressimages_status_failure { color: #cc0000; }
120 |
121 | #row_tiny_compressimages_settings_api_key a:hover { text-decoration:none; }
122 | #compressimages_check_status { margin-left: 0 !important; float:left; clear:both; }
123 |
124 | /* ======== Image types to optimize ======== */
125 |
126 |
127 |
128 |
129 | /* ======== Latest Optimizations ======== */
130 |
131 | #tiny_compressimages_status tr td .scalable{ margin: 10px 0; }
132 |
133 | #tiny_compressimages_compression { padding: 9px 0; }
134 | #tiny_compressimages_compression td { padding-top:2px; padding-bottom:2px; margin:0; vertical-align:middle; overflow:hidden; }
135 | #tiny_compressimages_compression tr.tiny_compressimages_log { border-bottom: 1px solid #ececec; width: 100%; }
136 |
137 | #tiny_compressimages_compression .image_path { padding-left: 10px; }
138 |
139 | #tiny_compressimages_compression .image_path img { display: inline-block; max-width: 30px; max-height: 30px; vertical-align: middle; border: 1px solid #d6d6d6; }
140 |
141 | #tiny_compressimages_compression .info_box { padding: 0 0 0 20px; }
142 |
143 | #tiny_compressimages_compression .centered td,
144 | #tiny_compressimages_compression .show_less td,
145 | #tiny_compressimages_compression .show_more td { text-align:center; }
146 |
147 | .tiny_compressimages_width { box-sizing: border-box; min-width: 180px;}
148 | .tiny_compressimages-no-images {padding: 0 0 10px 4px; font-weight: bold;}
149 | .log-details-td {padding-left: 4px;}
150 |
151 | .title-file { width: 45px; }
152 | .title-original { width: 65px; }
153 | .title-now { width: 50px; }
154 |
155 | .tinypng-upgrade-text {
156 | margin-top: 16px;
157 | font-style: italic;
158 |
159 | }
160 |
161 | /* ======== Advanced options ======== */
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
--------------------------------------------------------------------------------
/skin/adminhtml/default/default/images/Tiny/CompressImages/george-magento@2x_opt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tinify/magento1-plugin/5ab33d16814ee21bcf251a89bcc87d2f914058a7/skin/adminhtml/default/default/images/Tiny/CompressImages/george-magento@2x_opt.png
--------------------------------------------------------------------------------
/skin/adminhtml/default/default/images/Tiny/CompressImages/george-menu-icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tinify/magento1-plugin/5ab33d16814ee21bcf251a89bcc87d2f914058a7/skin/adminhtml/default/default/images/Tiny/CompressImages/george-menu-icon@2x.png
--------------------------------------------------------------------------------