├── LICENSE ├── README.md ├── app ├── code │ └── community │ │ └── Tiny │ │ └── CompressImages │ │ ├── Block │ │ └── Adminhtml │ │ │ ├── ApiNotification.php │ │ │ └── System │ │ │ └── Config │ │ │ ├── Form.php │ │ │ └── Form │ │ │ └── Field │ │ │ ├── AccountType.php │ │ │ ├── Api.php │ │ │ ├── Credits.php │ │ │ ├── LogFile.php │ │ │ ├── LogStatus.php │ │ │ ├── Radios.php │ │ │ ├── Saved.php │ │ │ ├── Status.php │ │ │ └── SupportTab.php │ │ ├── Exception.php │ │ ├── Helper │ │ ├── Config.php │ │ ├── Data.php │ │ └── Tinify.php │ │ ├── Model │ │ ├── Image.php │ │ ├── Observer.php │ │ ├── Product │ │ │ └── Image.php │ │ ├── Resource │ │ │ ├── Image.php │ │ │ ├── Image │ │ │ │ └── Collection.php │ │ │ ├── Setup.php │ │ │ ├── Totals.php │ │ │ └── Totals │ │ │ │ └── Collection.php │ │ ├── System │ │ │ └── Config │ │ │ │ ├── Backend │ │ │ │ ├── ApiKey.php │ │ │ │ └── Comment │ │ │ │ │ └── LoggingMode.php │ │ │ │ └── Source │ │ │ │ ├── Cms │ │ │ │ └── ImageTypes.php │ │ │ │ ├── Log.php │ │ │ │ ├── OnOff.php │ │ │ │ ├── Product │ │ │ │ └── ImageTypes.php │ │ │ │ └── Testlive.php │ │ └── Totals.php │ │ ├── Test │ │ ├── Block │ │ │ └── Adminhtml │ │ │ │ └── System │ │ │ │ └── Config │ │ │ │ └── Form │ │ │ │ └── Field │ │ │ │ └── LogStatusTest.php │ │ ├── Framework │ │ │ └── Tiny │ │ │ │ └── Test │ │ │ │ ├── Config.php │ │ │ │ ├── Http │ │ │ │ └── Response.php │ │ │ │ └── TestCase.php │ │ ├── Helper │ │ │ └── ConfigTest.php │ │ ├── Model │ │ │ ├── ImageTest.php │ │ │ ├── ObserverTest.php │ │ │ ├── Product │ │ │ │ └── ImageTest.php │ │ │ └── System │ │ │ │ └── Config │ │ │ │ └── Backend │ │ │ │ └── Comment │ │ │ │ └── LoggingModeTest.php │ │ └── bootstrap.php │ │ ├── controllers │ │ └── CompressImagesAdminhtml │ │ │ ├── ConfigController.php │ │ │ └── StatusController.php │ │ ├── etc │ │ ├── adminhtml.xml │ │ ├── config.xml │ │ └── system.xml │ │ └── sql │ │ └── tiny_compressimages_setup │ │ └── install-1.0.0.php ├── design │ └── adminhtml │ │ └── default │ │ └── default │ │ ├── layout │ │ └── Tiny │ │ │ └── CompressImages.xml │ │ └── template │ │ └── Tiny │ │ └── CompressImages │ │ ├── Api │ │ └── keyNotification.phtml │ │ └── system │ │ └── config │ │ └── form │ │ └── field │ │ ├── log_file.phtml │ │ ├── log_status.phtml │ │ └── support_tab.phtml ├── etc │ └── modules │ │ └── Tiny_CompressImages.xml └── locale │ └── en_US │ └── Tiny_CompressImages.csv ├── composer.json ├── lib └── TinyCompress │ ├── LICENSE │ ├── README.md │ ├── composer.json │ ├── composer.lock │ ├── lib │ ├── Tinify.php │ ├── Tinify │ │ ├── Client.php │ │ ├── Exception.php │ │ ├── Result.php │ │ ├── ResultMeta.php │ │ └── Source.php │ └── data │ │ └── cacert.pem │ ├── phpunit.xml │ ├── test │ ├── TinifyClientTest.php │ ├── TinifyExceptionTest.php │ ├── TinifyResultMetaTest.php │ ├── TinifyResultTest.php │ ├── TinifySourceTest.php │ ├── TinifyTest.php │ ├── curl_mock.php │ ├── examples │ │ ├── dummy.png │ │ └── voormedia.png │ ├── helper.php │ └── integration.php │ └── update-cacert.sh ├── modman ├── phpunit.xml ├── screenshots └── magento-config-page.jpg └── skin └── adminhtml └── default └── default ├── css └── Tiny │ └── CompressImages │ └── config.css └── images └── Tiny └── CompressImages ├── george-magento@2x_opt.png └── george-menu-icon@2x.png /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2013-2018 Tinify 4 | Copyright (c) 2016-2018 Total Internet Group 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/tinify/magento1-plugin.svg?branch=master)](https://travis-ci.org/tinify/magento1-plugin) 2 | 3 | # Compress JPEG & PNG images for Magento 1 4 | 5 | Make your Magento 1 store faster by compressing your JPEG and PNG images. 6 | 7 | [![Magento configuration page](/screenshots/magento-config-page.jpg?raw=true "Magento configuration page")](#getting-started) 8 | 9 | This extension automatically optimizes your images by integrating with the 10 | popular image compression services TinyJPG and TinyPNG. 11 | 12 | Learn more about TinyJPG and TinyPNG at https://tinypng.com/. 13 | 14 | Do you use Magento 2? Install the Magento 2 extension instead: 15 | https://packagist.org/packages/tinify/magento2 16 | 17 | ## How does it work? 18 | 19 | When you view a product in your webshop, Magento creates different image sizes 20 | in its cache folders. This extension will compress these images for you 21 | automatically. Any image sizes that are exact duplicates of each other will 22 | only be compressed once. 23 | 24 | Your product images are uploaded to the TinyJPG or TinyPNG service and 25 | analyzed to apply the best possible compression. Based on the content of the 26 | image an optimal strategy is chosen. The result is sent back to your Magento 27 | webshop and saved in your public media folder. 28 | 29 | On average JPEG images are compressed by 40-60% and PNG images by 50-80% 30 | without visible loss in quality. Your webshop will load faster for your 31 | visitors, and you’ll save storage space and bandwidth! 32 | 33 | ## Getting started 34 | 35 | Obtain your free API key from https://tinypng.com/developers. The first 500 36 | compressions per month are completely free, no strings attached! As each 37 | product will be shown in different sizes, between 50 and 100 products can be 38 | uploaded to your Magento webshop and compressed for free. You can also change 39 | which of types of image sizes should be compressed. 40 | 41 | If you’re a heavy user, you can compress additional images for a small 42 | additional fee per image by upgrading your account. You can keep track of the 43 | amount of compressions in the Magento 1 configuration section. 44 | 45 | ## Installation 46 | 47 | Copy the extension key from Magento Connect. Login on your webshop backend 48 | and open *System -> Magento Connect -> Magento Connect Manager*. 49 | Paste the extension key to install the extension. 50 | 51 | After installation, go to *System -> Configuration -> Image Optimization*, and 52 | enter your TinyPNG API Key. Flush the images cache to start compressing. 53 | 54 | Regenerating the image cache and optimizing all images may take some time and 55 | can be intensive for the server that your store runs on. If you own a large 56 | store with lots of traffic and many product pages you may not want to flush 57 | the entire cache at once. As an alternative you can manually delete parts of 58 | the existing image cache in smaller batches directly from the filesystem. 59 | Images that are missing from the product cache folder will then be recreated 60 | and optimised on a smaller scale. 61 | 62 | ## Contact us 63 | 64 | Got questions or feedback? Let us know! Contact us at support@tinypng.com. 65 | 66 | ## Information for contributors 67 | 68 | You will need to have the following prerequisites to make changes to the code. 69 | 70 | * PHP 5.4 or newer. 71 | * MySQL 5.4 or newer (integration tests). 72 | * Magento 1.8.0 or newer. 73 | * PHPUnit 4.8 or newer. 74 | 75 | ### Running the unit tests 76 | 77 | From the .modman/Tiny_CompressImages directory: 78 | 79 | phpunit 80 | 81 | ## License 82 | 83 | This software is licensed under the MIT License. [View the license](LICENSE). 84 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/ApiNotification.php: -------------------------------------------------------------------------------- 1 | getApiKey()) { 14 | return true; 15 | } 16 | 17 | return false; 18 | } 19 | 20 | /** 21 | * @return string 22 | */ 23 | public function getBackendUrl() 24 | { 25 | return Mage::helper('adminhtml')->getUrl('adminhtml/system_config/edit/section/tiny_compressimages/'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/System/Config/Form.php: -------------------------------------------------------------------------------- 1 | _elementTypes = parent::_getAdditionalElementTypes(); 17 | 18 | $this 19 | ->_addRadioButtons() 20 | ->_addStatusIndicator() 21 | ->_addCreditsIndicator() 22 | ->_addSavedIndicator() 23 | ->_addApiIndicator() 24 | ->_addAccountType() 25 | ->_addLogButtonType(); 26 | 27 | return $this->_elementTypes; 28 | } 29 | 30 | /** 31 | * Add the Off/Live/Test radio button list. 32 | * 33 | * @return $this 34 | */ 35 | protected function _addRadioButtons() 36 | { 37 | $this->_elementTypes['compressimages_radios'] = Mage::getConfig() 38 | ->getBlockClassName('tiny_compressimages/adminhtml_system_config_form_field_radios'); 39 | 40 | return $this; 41 | } 42 | 43 | /** 44 | * Add a field that shows the status indicator. 45 | * 46 | * @return $this 47 | */ 48 | protected function _addStatusIndicator() 49 | { 50 | $this->_elementTypes['compressimages_status'] = Mage::getConfig() 51 | ->getBlockClassName('tiny_compressimages/adminhtml_system_config_form_field_status'); 52 | 53 | return $this; 54 | } 55 | 56 | /** 57 | * Add a field that shows the credits remaining indicator. 58 | * 59 | * @return $this 60 | */ 61 | protected function _addCreditsIndicator() 62 | { 63 | $this->_elementTypes['compressimages_credits'] = Mage::getConfig() 64 | ->getBlockClassName('tiny_compressimages/adminhtml_system_config_form_field_credits'); 65 | 66 | return $this; 67 | } 68 | 69 | /** 70 | * Add a field that shows how much has been saved. 71 | * 72 | * @return $this 73 | */ 74 | protected function _addSavedIndicator() 75 | { 76 | $this->_elementTypes['compressimages_saved'] = Mage::getConfig() 77 | ->getBlockClassName('tiny_compressimages/adminhtml_system_config_form_field_saved'); 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * Add a field that shows the api status indicator. 84 | * 85 | * @return $this 86 | */ 87 | protected function _addApiIndicator() 88 | { 89 | $this->_elementTypes['compressimages_api'] = Mage::getConfig() 90 | ->getBlockClassName('tiny_compressimages/adminhtml_system_config_form_field_api'); 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * Add a field that shows the api status indicator. 97 | * 98 | * @return $this 99 | */ 100 | protected function _addAccountType() 101 | { 102 | $this->_elementTypes['compressimages_account_type'] = Mage::getConfig() 103 | ->getBlockClassName('tiny_compressimages/adminhtml_system_config_form_field_accountType'); 104 | 105 | return $this; 106 | } 107 | 108 | /** 109 | * Add a field that shows the api status indicator. 110 | * 111 | * @return $this 112 | */ 113 | protected function _addLogButtonType() 114 | { 115 | $this->_elementTypes['compressimages_log_button'] = Mage::getConfig() 116 | ->getBlockClassName('tiny_compressimages/adminhtml_system_config_form_field_logFile'); 117 | 118 | return $this; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/System/Config/Form/Field/AccountType.php: -------------------------------------------------------------------------------- 1 | '; 12 | $button .= Mage::helper('tiny_compressimages')->__('Credits'); 13 | $button .= ''; 14 | 15 | return Mage::helper('tiny_compressimages')->__('Free (A maximum of 500 images per month)
' . $button); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/System/Config/Form/Field/Api.php: -------------------------------------------------------------------------------- 1 | _configHelper === null) { 27 | $this->_configHelper = Mage::helper('tiny_compressimages/config'); 28 | } 29 | 30 | return $this->_configHelper; 31 | } 32 | 33 | /** 34 | * Get the Tinify helper. 35 | * 36 | * @return Tiny_CompressImages_Helper_Tinify 37 | */ 38 | public function getTinifyHelper() 39 | { 40 | if ($this->_tinifyHelper === null) { 41 | $this->_tinifyHelper = Mage::helper('tiny_compressimages/tinify'); 42 | } 43 | 44 | return $this->_tinifyHelper; 45 | } 46 | 47 | /** 48 | * Generate the status for the CompressImages extension. 49 | * 50 | * @return string 51 | */ 52 | public function getElementHtml() 53 | { 54 | $this 55 | ->_getStatusHtml() 56 | ->_getJavascript(); 57 | 58 | return $this->output; 59 | } 60 | 61 | /** 62 | * Generate the status message. 63 | * 64 | * @return $this 65 | */ 66 | protected function _getStatusHtml() 67 | { 68 | $result = $this->getTinifyHelper()->getApiStatus(true); 69 | 70 | $this->output .= $result['message']; 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Generate the javascript to make the Ajax call to check the API. 77 | * 78 | * @return $this 79 | */ 80 | protected function _getJavascript() 81 | { 82 | $js = ''; 83 | 84 | if ($this->getConfigHelper()->isEnabled()) { 85 | $js 86 | .= ''; 104 | } 105 | 106 | $this->output .= $js; 107 | 108 | return $this; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/System/Config/Form/Field/Credits.php: -------------------------------------------------------------------------------- 1 | _helper = Mage::helper('tiny_compressimages'); 30 | $this->_tinifyHelper = Mage::helper('tiny_compressimages/tinify'); 31 | $this->_configHelper = Mage::helper('tiny_compressimages/config'); 32 | 33 | } 34 | 35 | /** 36 | * Generate the amount of credits remaining for the CompressImages extension. 37 | * 38 | * @return string 39 | */ 40 | public function getElementHtml() 41 | { 42 | if (!$this->_configHelper->isConfigured() && !$this->_configHelper->getApiKey()) { 43 | return '' 44 | . $this->_helper->__('Please enter your api key to check the amount of compressions left.') 45 | . ''; 46 | } 47 | 48 | if (!$this->_configHelper->isConfigured() && !$this->_configHelper->isEnabled()) { 49 | return '' 50 | . $this->_helper->__('Please enable the extension to check the amount of compressions left.') 51 | . ''; 52 | } 53 | 54 | return $this->messageGenerator(); 55 | } 56 | 57 | protected function messageGenerator() 58 | { 59 | $payingState = $this->_tinifyHelper->getPayingState(); 60 | $remainingCredits = $this->_tinifyHelper->getRemainingCredits(); 61 | 62 | if (!$remainingCredits && $payingState !== 'free') { 63 | $remainingCredits = 'unlimited'; 64 | } 65 | 66 | $resultString = $this->_helper->__( 67 | 'You are on a %s plan with %s compressions left this month.', $payingState, $remainingCredits 68 | ); 69 | 70 | if ($payingState === 'free') { 71 | $resultString = $this->addUpgradeButton($resultString); 72 | } 73 | 74 | return $resultString; 75 | } 76 | 77 | /** 78 | * @param $resultString 79 | * 80 | * @return string 81 | */ 82 | protected function addUpgradeButton($resultString) 83 | { 84 | $apiEmail = $this->_tinifyHelper->getApiEmail(); 85 | $upgradeUrl = self::TINY_COMPRESSIMAGES_BASE_UPGRADE_URL . $apiEmail; 86 | 87 | $resultString .= '

' 88 | . $this->_helper->__('Remove all limitations? Visit your TinyPNG dashboard to upgrade your account.') 89 | . '

' 90 | . '' 91 | . $this->_helper->__('Upgrade account') 92 | . ''; 93 | 94 | return $resultString; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/System/Config/Form/Field/LogFile.php: -------------------------------------------------------------------------------- 1 | setElement($element); 23 | 24 | return $this->toHtml(); 25 | } 26 | 27 | /** 28 | * Get the URL where we can download the log file. 29 | * 30 | * @return string 31 | */ 32 | public function getDownloadUrl() 33 | { 34 | return Mage::helper('adminhtml')->getUrl('adminhtml/CompressImagesAdminhtml_config/downloadLogs'); 35 | } 36 | 37 | /** 38 | * Helper to check if the log file exists. 39 | * 40 | * @return bool 41 | */ 42 | public function logFileExists() 43 | { 44 | return Mage::helper('tiny_compressimages')->getLogFileExists(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/System/Config/Form/Field/LogStatus.php: -------------------------------------------------------------------------------- 1 | setElement($element); 21 | 22 | return $this->toHtml(); 23 | } 24 | 25 | /** 26 | * @return string 27 | */ 28 | public function getCleanImagesUrl() 29 | { 30 | $url = Mage::helper("adminhtml")->getUrl('adminhtml/CompressImagesAdminhtml_config/clearCache'); 31 | 32 | return $url; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/System/Config/Form/Field/Radios.php: -------------------------------------------------------------------------------- 1 | '; 15 | $html .= 'serialize(array('name', 'class', 'style', 'disabled')); 16 | if (is_array($option)) { 17 | $html.= 'value="' 18 | . $this->_escape($option['value']) 19 | . '" id="' 20 | . $this->getHtmlId() 21 | . $option['value'] 22 | . '"'; 23 | 24 | if ($option['value'] == $selected) { 25 | $html .= ' checked="checked"'; 26 | } 27 | 28 | $html .= ' />'; 29 | 30 | $html .= ''; 36 | } else if ($option instanceof Varien_Object) { 37 | $html .= 'id="' 38 | . $this->getHtmlId() 39 | . $option->getValue() 40 | . '"' 41 | . $option->serialize( 42 | array( 43 | 'label', 44 | 'title', 45 | 'value', 46 | 'class', 47 | 'style', 48 | ) 49 | ); 50 | 51 | if (in_array($option->getValue(), $selected)) { 52 | $html .= ' checked="checked"'; 53 | } 54 | 55 | $html .= ' />'; 56 | 57 | $html .= ''; 63 | } 64 | 65 | $html.= ''; 66 | 67 | return $html; 68 | } 69 | 70 | /** 71 | * Wrap the output in a container div. 72 | * 73 | * @return string 74 | */ 75 | public function getElementHtml() 76 | { 77 | $html = parent::getElementHtml(); 78 | 79 | return '
' . $html . '
'; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/System/Config/Form/Field/Saved.php: -------------------------------------------------------------------------------- 1 | getTotalCompressionInformation(); 12 | 13 | return Mage::helper('tiny_compressimages')->__( 14 | 'Saved %s%% over a total of %s compressions', 15 | $data['percentageSaved'], 16 | $data['totalCompressions'] 17 | ); 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/System/Config/Form/Field/Status.php: -------------------------------------------------------------------------------- 1 | _helper = Mage::helper('tiny_compressimages'); 17 | } 18 | 19 | /** 20 | * Generate the status for the CompressImages extension. 21 | * 22 | * @return string 23 | */ 24 | public function getElementHtml() 25 | { 26 | /** @var Tiny_CompressImages_Helper_Config $configHelper */ 27 | $configHelper = Mage::helper('tiny_compressimages/config'); 28 | 29 | if (!$configHelper->isConfigured()) { 30 | if (!$configHelper->getApiKey()) { 31 | return '' 32 | . $this->_helper->__('Please enter your api key to check the compression count.') 33 | . ''; 34 | } 35 | 36 | if (!$configHelper->isEnabled()) { 37 | return '' 38 | . $this->_helper->__('Please enable the extension to check the compression count.') 39 | . ''; 40 | } 41 | } 42 | 43 | $compressionCount = Mage::helper('tiny_compressimages/tinify')->compressionCount(); 44 | 45 | if ($configHelper->getApiKey() == '') { 46 | return $this->_helper->__('Add your TinyPNG API key to check the status'); 47 | } 48 | 49 | if ($compressionCount == 500) { 50 | $button = ''; 51 | $button .= 'Upgrade'; 52 | 53 | $onhold = $this->_helper->__('Compression on hold. 500 free images compressed this month.') . ' '; 54 | $upgrade = $this->_helper->__('Upgrade your account to compress more images'); 55 | 56 | return '' . $onhold . '' . $upgrade . '
' . $button; 57 | } 58 | 59 | return $this->_helper->__( 60 | 'There are %s compressions done this month.', 61 | $compressionCount 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Block/Adminhtml/System/Config/Form/Field/SupportTab.php: -------------------------------------------------------------------------------- 1 | setElement($element); 23 | 24 | return $this->toHtml(); 25 | } 26 | 27 | /** 28 | * Get the current version of the PostNL extension's code base. 29 | * 30 | * @return string 31 | */ 32 | public function getModuleVersion() 33 | { 34 | $version = (string) Mage::getConfig()->getModuleConfig('Tiny_CompressImages')->version; 35 | 36 | return $version; 37 | } 38 | 39 | /** 40 | * Get the current stability of the PostNL extension's code base. 41 | * 42 | * @return string 43 | */ 44 | public function getModuleStability() 45 | { 46 | $stability = (string) Mage::getConfig()->getModuleConfig('Tiny_CompressImages')->stability; 47 | 48 | return $stability; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Exception.php: -------------------------------------------------------------------------------- 1 | _getEnabled($store); 37 | 38 | if ($enabledValue != 0) { 39 | return true; 40 | } else { 41 | return false; 42 | } 43 | } 44 | 45 | /** 46 | * Is the extension currently in test mode 47 | * 48 | * @param null|int $store 49 | * 50 | * @return bool 51 | */ 52 | public function isTestMode($store = null) 53 | { 54 | $enabledValue = $this->_getEnabled($store); 55 | 56 | if ($enabledValue == 1) { 57 | return true; 58 | } else { 59 | return false; 60 | } 61 | } 62 | 63 | /** 64 | * @param null $store 65 | * 66 | * @return bool 67 | */ 68 | public function isConfigured($store = null) 69 | { 70 | if (!$this->isEnabled($store)) { 71 | return false; 72 | } 73 | 74 | if (!$this->getApiKey($store)) { 75 | return false; 76 | } 77 | 78 | return true; 79 | } 80 | 81 | /** 82 | * Return the API Key 83 | * 84 | * @param null|int $store 85 | * 86 | * @return string 87 | */ 88 | public function getApiKey($store = null) 89 | { 90 | return Mage::getStoreConfig(self::XPATH_API_KEY, $store); 91 | } 92 | 93 | /** 94 | * Lets you know if base images should be compressed. 95 | * 96 | * @param null $store 97 | * 98 | * @return mixed 99 | */ 100 | public function isBaseImageTypeEnabledForCompression($store = null) 101 | { 102 | return Mage::getStoreConfig(self::XPATH_IMAGE_TYPE_BASE, $store); 103 | } 104 | 105 | /** 106 | * Lets you know if small images should be compressed. 107 | * 108 | * @param null $store 109 | * 110 | * @return mixed 111 | */ 112 | public function isSmallImageTypeEnabledForCompression($store = null) 113 | { 114 | return Mage::getStoreConfig(self::XPATH_IMAGE_TYPE_SMALL, $store); 115 | } 116 | 117 | /** 118 | * Lets you know if thumbnail-sized images should be compressed. 119 | * 120 | * @param null $store 121 | * 122 | * @return mixed 123 | */ 124 | public function isThumbnailTypeEnabledForCompression($store = null) 125 | { 126 | return Mage::getStoreConfig(self::XPATH_IMAGE_TYPE_THUMBNAIL, $store); 127 | } 128 | 129 | /** 130 | * Lets you know if swachtes (small images used to select product attributes) images should be compressed. 131 | * 132 | * @param null $store 133 | * 134 | * @return mixed 135 | */ 136 | public function isSwatchTypeEnabledForCompression($store = null) 137 | { 138 | return Mage::getStoreConfig(self::XPATH_IMAGE_TYPE_SWATCHES, $store); 139 | } 140 | 141 | 142 | /** 143 | * Returns an array of image types that can be compressed by CompressImages 144 | * 145 | * @param null $store 146 | * 147 | * @return mixed 148 | */ 149 | public function getProductImageTypesToCompress($store = null) 150 | { 151 | $imageTypes = array(); 152 | 153 | if ($this->isBaseImageTypeEnabledForCompression($store)) { 154 | $imageTypes[] = 'image'; 155 | } 156 | 157 | if ($this->isSmallImageTypeEnabledForCompression($store)) { 158 | $imageTypes[] = 'small_image'; 159 | } 160 | 161 | if ($this->isThumbnailTypeEnabledForCompression($store)) { 162 | $imageTypes[] = 'thumbnail'; 163 | $imageTypes[] = 'media_image'; 164 | } 165 | 166 | if ($this->isSwatchTypeEnabledForCompression($store)) { 167 | $imageTypes[] = 'swatches'; 168 | } 169 | 170 | return $imageTypes; 171 | } 172 | 173 | /** 174 | * Returns the logging mode. 175 | * 176 | * @param null $store 177 | * 178 | * @return mixed 179 | */ 180 | public function getLoggingMode($store = null) 181 | { 182 | return Mage::getStoreConfig(self::XPATH_LOGGING_MODE, $store); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Helper/Data.php: -------------------------------------------------------------------------------- 1 | _configHelper = Mage::helper('tiny_compressimages/config'); 22 | } 23 | 24 | /** 25 | * @param $msg 26 | * @param $type 27 | * 28 | * @return $this; 29 | */ 30 | public function log($msg, $type = null, $store = null) 31 | { 32 | $logginModes = $this->_loggingArray($store); 33 | 34 | /** 35 | * Always log exceptions. $msg should be an instanceof Exception! 36 | */ 37 | if ($msg instanceof Exception) { 38 | $type = 'exception'; 39 | $msg = $msg->__toString(); 40 | } 41 | 42 | if (!in_array($type, $logginModes)) { 43 | return $this; 44 | } 45 | 46 | Mage::log($msg, null, $this->logFile, true); 47 | 48 | return $this; 49 | } 50 | 51 | /** 52 | * @param $store 53 | * 54 | * @return array 55 | */ 56 | protected function _loggingArray($store) 57 | { 58 | switch ($this->_configHelper->getLoggingMode($store)) 59 | { 60 | case 'only_exceptions': 61 | $logginArray = array('exception'); 62 | break; 63 | case 'fail_and_exceptions': 64 | $logginArray = array('error', 'exception'); 65 | break; 66 | case 'all': 67 | $logginArray = array('info', 'error', 'exception'); 68 | break; 69 | case 'off': 70 | default: 71 | $logginArray = array(''); 72 | } 73 | 74 | return $logginArray; 75 | } 76 | 77 | /** 78 | * Returns whether the log file exists or not 79 | * 80 | * @return bool 81 | */ 82 | public function getLogFileExists() 83 | { 84 | $filePath = $this->getLogFilePath(); 85 | 86 | if (!@file_exists($filePath)) { 87 | return false; 88 | } 89 | 90 | return true; 91 | } 92 | 93 | /** 94 | * Returns the file path of the log file 95 | * 96 | * @return string 97 | */ 98 | public function getLogFilePath() 99 | { 100 | $logDir = Mage::getBaseDir('log'); 101 | 102 | $filePath = $logDir . DS . $this->logFile; 103 | 104 | return $filePath; 105 | } 106 | 107 | /** 108 | * Get the name of the logfile. 109 | * 110 | * @return string 111 | */ 112 | public function getLogFilename() 113 | { 114 | return $this->logFile; 115 | } 116 | 117 | /** 118 | * Copied from https://gist.github.com/jblyberg/1572386 119 | * 120 | * @param $datefrom 121 | * @param int $dateto 122 | * 123 | * @return string 124 | */ 125 | public function timeAgo($datefrom, $dateto = -1) 126 | { 127 | $datefrom = strtotime($datefrom); 128 | if ($datefrom <= 0) { 129 | return $this->__('A long time ago'); 130 | } 131 | 132 | if ($dateto == -1) { 133 | $dateto = time(); 134 | } 135 | 136 | $difference = $dateto - $datefrom; 137 | if ($difference < 60) { 138 | $interval = "s"; 139 | } elseif ($difference >= 60 && $difference < 60 * 60) { 140 | $interval = "n"; 141 | } elseif ($difference >= 60 * 60 && $difference < 60 * 60 * 24) { 142 | $interval = "h"; 143 | } elseif ($difference >= 60 * 60 * 24 && $difference < 60 * 60 * 24 * 7) { 144 | $interval = "d"; 145 | } elseif ($difference >= 60 * 60 * 24 * 7 && $difference < 60 * 60 * 24 * 30) { 146 | $interval = "ww"; 147 | } elseif ($difference >= 60 * 60 * 24 * 30 && $difference < 60 * 60 * 24 * 365) { 148 | $interval = "m"; 149 | } elseif ($difference >= 60 * 60 * 24 * 365) { 150 | $interval = "y"; 151 | } 152 | 153 | switch ($interval) { 154 | case "m": 155 | $months_difference = floor($difference / 60 / 60 / 24 / 29); 156 | while (mktime(date("H", $datefrom), date("i", $datefrom), date("s", $datefrom), date("n", $datefrom) + ($months_difference), date("j", $dateto), date("Y", $datefrom)) < $dateto) { 157 | $months_difference++; 158 | } 159 | 160 | $datediff = $months_difference; 161 | if ($datediff == 12) { 162 | $datediff--; 163 | } 164 | 165 | $res = ($datediff == 1) ? $this->__('%s month ago', $datediff) : $this->__('%s months ago', $datediff); 166 | break; 167 | case "y": 168 | $datediff = floor($difference / 60 / 60 / 24 / 365); 169 | $res = ($datediff == 1) ? $this->__('%s year ago', $datediff) : $this->__('%s years ago', $datediff); 170 | break; 171 | case "d": 172 | $datediff = floor($difference / 60 / 60 / 24); 173 | $res = ($datediff == 1) ? $this->__('%s day ago', $datediff) : $this->__('%s days ago', $datediff); 174 | break; 175 | case "ww": 176 | $datediff = floor($difference / 60 / 60 / 24 / 7); 177 | $res = ($datediff == 1) ? $this->__('%s week ago', $datediff) : $this->__('%s weeks ago', $datediff); 178 | break; 179 | case "h": 180 | $datediff = floor($difference / 60 / 60); 181 | $res = ($datediff == 1) ? $this->__('%s hour ago', $datediff) : $this->__('%s hours ago', $datediff); 182 | break; 183 | case "n": 184 | $datediff = floor($difference / 60); 185 | $res = ($datediff == 1) ? $this->__('%s minute ago', $datediff) : $this->__('%s minutes ago', $datediff); 186 | break; 187 | case "s": 188 | $datediff = $difference; 189 | $res = ($datediff == 1) ? $this->__('%s second ago', $datediff) : $this->__('%s seconds ago', $datediff); 190 | break; 191 | } 192 | 193 | return $res; 194 | } 195 | 196 | /** 197 | * Retrieve a human readable file size. 198 | * Copied from http://jeffreysambells.com/2012/10/25/human-readable-filesize-php 199 | * 200 | * @param $bytes 201 | * 202 | * @return string 203 | */ 204 | public function fileSize($bytes) 205 | { 206 | $size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB'); 207 | $factor = floor((strlen($bytes) - 1) / 3); 208 | 209 | return sprintf('%.0f ', $bytes / pow(1024, $factor)) . @$size[$factor]; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /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/code/community/Tiny/CompressImages/Model/Observer.php: -------------------------------------------------------------------------------- 1 | getConfigHelper()->isEnabled()) { 30 | return $this; 31 | } 32 | 33 | try { 34 | $storeId = Mage::app()->getStore()->getStoreId(); 35 | $this->getTinifyHelper()->setProductImage($observer->getObject(), $storeId)->compress(); 36 | } catch (Exception $e) { 37 | $this->getDataHelper()->log($e); 38 | 39 | throw $e; 40 | } 41 | 42 | return $this; 43 | } 44 | 45 | /** 46 | * @return Tiny_CompressImages_Helper_Tinify 47 | */ 48 | protected function getTinifyHelper() 49 | { 50 | if ($this->_tinifyHelper === null) { 51 | $this->_tinifyHelper = Mage::helper('tiny_compressimages/tinify'); 52 | } 53 | 54 | return $this->_tinifyHelper; 55 | } 56 | 57 | /** 58 | * @return Tiny_CompressImages_Helper_Data 59 | */ 60 | protected function getDataHelper() 61 | { 62 | if ($this->_dataHelper === null) { 63 | $this->_dataHelper = Mage::helper('tiny_compressimages'); 64 | } 65 | 66 | return $this->_dataHelper; 67 | } 68 | 69 | /** 70 | * @return Tiny_CompressImages_Helper_Config 71 | */ 72 | protected function getConfigHelper() 73 | { 74 | if ($this->_configHelper === null) { 75 | $this->_configHelper = Mage::helper('tiny_compressimages/config'); 76 | } 77 | 78 | return $this->_configHelper; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/Product/Image.php: -------------------------------------------------------------------------------- 1 | _configHelper === null) { 27 | $this->_configHelper = Mage::helper('tiny_compressimages/config'); 28 | } 29 | 30 | return $this->_configHelper; 31 | } 32 | 33 | /** 34 | * @return Tiny_CompressImages_Helper_Data 35 | */ 36 | public function getDataHelper() 37 | { 38 | if ($this->_dataHelper === null) { 39 | $this->_dataHelper = Mage::helper('tiny_compressimages'); 40 | } 41 | 42 | return $this->_dataHelper; 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getUrl() 49 | { 50 | /** @var Tiny_CompressImages_Helper_Config $helper */ 51 | $helper = $this->getConfigHelper(); 52 | if ($helper->isTestMode(Mage::app()->getStore()->getStoreId())) { 53 | return parent::getUrl(); 54 | } 55 | 56 | /** @var Tiny_Compressimages_Model_Image $model */ 57 | $path = substr($this->_newFile, strlen(Mage::getBaseDir('media')) - 6); 58 | $model = Mage::getModel('tiny_compressimages/image')->load($path, 'path'); 59 | if (!$model->getId() || !file_exists($model->getFilepathOptimized())) { 60 | return parent::getUrl(); 61 | } 62 | 63 | return $model->getUrl(); 64 | } 65 | 66 | /** 67 | * @return Mage_Catalog_Model_Product_Image $this 68 | */ 69 | public function saveFile() 70 | { 71 | parent::saveFile(); 72 | 73 | Mage::dispatchEvent('catalog_product_image_save_after', array($this->_eventObject => $this)); 74 | 75 | return $this; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/Resource/Image.php: -------------------------------------------------------------------------------- 1 | _init('tiny_compressimages/image', 'image_id'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/Resource/Image/Collection.php: -------------------------------------------------------------------------------- 1 | _init('tiny_compressimages/image'); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/Resource/Setup.php: -------------------------------------------------------------------------------- 1 | getConnection(); 16 | 17 | try { 18 | $select = $conn->select() 19 | ->from($this->getTable('core/config_data')) 20 | ->where('path = ?', $fromXpath); 21 | 22 | $result = $conn->fetchAll($select); 23 | foreach ($result as $row) { 24 | try { 25 | /** 26 | * Copy the old setting to the new setting. 27 | * 28 | * @todo Check if the row already exists. 29 | */ 30 | $conn->insert( 31 | $this->getTable('core/config_data'), 32 | array( 33 | 'scope' => $row['scope'], 34 | 'scope_id' => $row['scope_id'], 35 | 'value' => $row['value'], 36 | 'path' => $toXpath 37 | ) 38 | ); 39 | } catch (Exception $e) { 40 | Mage::helper('tiny_compressimages')->log($e); 41 | } 42 | } 43 | } catch (Exception $e) { 44 | Mage::helper('tiny_compressimages')->log($e); 45 | } 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * Check if the specified xpath exists. 52 | * 53 | * @param $xpath 54 | * 55 | * @return bool 56 | */ 57 | public function configExists($xpath) 58 | { 59 | $conn = $this->getConnection(); 60 | 61 | try { 62 | $select = $conn->select() 63 | ->from($this->getTable('core/config_data')) 64 | ->where('path = ?', $xpath); 65 | 66 | $result = $conn->fetchAll($select); 67 | foreach ($result as $row) { 68 | return true; 69 | } 70 | } catch (Exception $e) { 71 | Mage::helper('tiny_compressimages')->log($e); 72 | } 73 | 74 | return false; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/Resource/Totals.php: -------------------------------------------------------------------------------- 1 | _init('tiny_compressimages/totals', 'entity_id'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/Resource/Totals/Collection.php: -------------------------------------------------------------------------------- 1 | _init('tiny_compressimages/totals'); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/System/Config/Backend/ApiKey.php: -------------------------------------------------------------------------------- 1 | getValue(); 13 | $oldApiKeyValue = $this->getOldValue(); 14 | $helper = Mage::helper('tiny_compressimages/tinify'); 15 | 16 | if (strlen($apiKeyValue) > 0) { 17 | $validateResult = $helper->validateApiKey($apiKeyValue); 18 | 19 | if (!$validateResult) { 20 | throw new Mage_Exception($helper->__('The Api Key is invalid')); 21 | } 22 | } 23 | 24 | /** 25 | * Remove the cached items. 26 | */ 27 | if ($apiKeyValue != $oldApiKeyValue) { 28 | Mage::app()->removeCache(Tiny_CompressImages_Helper_Tinify::CACHE_KEY); 29 | } 30 | 31 | return parent::_beforeSave(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/System/Config/Backend/Comment/LoggingMode.php: -------------------------------------------------------------------------------- 1 | __('Any error messages will be saved to %s'); 12 | $downloadUrl = Mage::helper("adminhtml")->getUrl('adminhtml/CompressImagesAdminhtml_config/downloadLogs'); 13 | $logPath = Mage::helper('tiny_compressimages')->getLogFilePath(); 14 | $basePath = Mage::getBaseDir(); 15 | 16 | $nicePath = str_replace($basePath, '', $logPath); 17 | 18 | if (Mage::helper('tiny_compressimages')->getLogFileExists()) { 19 | $text = sprintf($text, '' . $nicePath . ''); 20 | } else { 21 | $text = sprintf($text, $nicePath); 22 | } 23 | 24 | return $text; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/System/Config/Source/Cms/ImageTypes.php: -------------------------------------------------------------------------------- 1 | 'category_images', 16 | 'label' => $helper->__('Category images') 17 | ), 18 | array( 19 | 'value' => 'block_images', 20 | 'label' => $helper->__('Block images') 21 | ), 22 | array( 23 | 'value' => 'cms_images', 24 | 'label' => $helper->__('CMS page images') 25 | ), 26 | ); 27 | 28 | return $array; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/System/Config/Source/Log.php: -------------------------------------------------------------------------------- 1 | 'off', 16 | 'label' => $helper->__('Logging disabled') 17 | ), 18 | array( 19 | 'value' => 'only_exceptions', 20 | 'label' => $helper->__('Exceptions only') 21 | ), 22 | array( 23 | 'value' => 'fail_and_exceptions', 24 | 'label' => $helper->__('Errors and Exceptions') 25 | ), 26 | array( 27 | 'value' => 'all', 28 | 'label' => $helper->__('All logging information') 29 | ), 30 | ); 31 | 32 | return $array; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/System/Config/Source/OnOff.php: -------------------------------------------------------------------------------- 1 | 0, 16 | 'label' => $helper->__('Off') 17 | ), 18 | array( 19 | 'value' => 1, 20 | 'label' => $helper->__('On') 21 | ), 22 | ); 23 | 24 | return $array; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/System/Config/Source/Product/ImageTypes.php: -------------------------------------------------------------------------------- 1 | 'thumbnail', 16 | 'label' => $helper->__('Thumbnail') 17 | ), 18 | array( 19 | 'value' => 'small_image', 20 | 'label' => $helper->__('Small Image') 21 | ), 22 | array( 23 | 'value' => 'image', 24 | 'label' => $helper->__('Base Image') 25 | ), 26 | array( 27 | 'value' => 'media_image', 28 | 'label' => $helper->__('Media Image') 29 | ), 30 | ); 31 | 32 | return $array; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/System/Config/Source/Testlive.php: -------------------------------------------------------------------------------- 1 | '2', 20 | 'label' => $helper->__('Live') 21 | ), 22 | array( 23 | 'value' => '1', 24 | 'label' => $helper->__('Test') 25 | ), 26 | array( 27 | 'value' => '0', 28 | 'label' => $helper->__('Disabled') 29 | ), 30 | ); 31 | return $array; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Model/Totals.php: -------------------------------------------------------------------------------- 1 | _init('tiny_compressimages/totals'); 33 | } 34 | 35 | /** 36 | * Gets the compression information over the total data. 37 | * 38 | * @return array 39 | */ 40 | public function getTotalCompressionInformation() 41 | { 42 | $collection = $this->getCollection(); 43 | 44 | $totalCompressions = 0; 45 | $totalBytesBefore = 0; 46 | $totalBytesAfter = 0; 47 | 48 | /** @var Tiny_CompressImages_Model_Totals $record */ 49 | foreach ($collection as $record) { 50 | $totalCompressions = $record->getTotalCompressions() + $totalCompressions; 51 | $totalBytesBefore = $record->getTotalBytesBefore() + $totalBytesBefore; 52 | $totalBytesAfter = $record->getTotalBytesAfter() + $totalBytesAfter; 53 | } 54 | 55 | $bytesSaved = $totalBytesBefore - $totalBytesAfter; 56 | $percantageSaved = 0; 57 | 58 | if ($bytesSaved !== 0) { 59 | $percantageSaved = round(($bytesSaved / $totalBytesBefore) * 100); 60 | } 61 | 62 | return array( 63 | 'percentageSaved' => $percantageSaved, 64 | 'totalCompressions' => $totalCompressions, 65 | 'bytesBefore' => $this->formatBytes($totalBytesBefore), 66 | 'bytesAfter' => $this->formatBytes($totalBytesAfter), 67 | ); 68 | } 69 | 70 | /** 71 | * @param $bytes 72 | * @param int $precision 73 | * 74 | * @return string 75 | */ 76 | function formatBytes($bytes, $precision = 2) 77 | { 78 | if ($bytes <= 0) { 79 | return '0 KB'; 80 | } 81 | 82 | $units = array('B', 'KB', 'MB', 'GB', 'TB'); 83 | 84 | $i = floor(log($bytes, 1024)); 85 | $sum = round($bytes / pow(1024, $i), $precision); 86 | 87 | return $sum . ' ' . $units[$i]; 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Test/Block/Adminhtml/System/Config/Form/Field/LogStatusTest.php: -------------------------------------------------------------------------------- 1 | _instance = new Tiny_CompressImages_Block_Adminhtml_System_Config_Form_Field_LogStatus; 13 | $this->_instance->setArea('adminhtml'); 14 | 15 | Mage::getModel('tiny_compressimages/image')->deleteAll(); 16 | } 17 | 18 | public function testImagesLimit() 19 | { 20 | $html = $this->_instance->toHtml(); 21 | $this->assertNotContains('id="show_all"', $html); 22 | 23 | for ($i = 0; $i < 11; $i++) { 24 | $model = Mage::getModel('tiny_compressimages/image'); 25 | $model->setProcessedAt(Varien_Date::now()); 26 | $model->save(); 27 | } 28 | 29 | $html = $this->_instance->toHtml(); 30 | $this->assertContains('id="show_all"', $html); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Test/Framework/Tiny/Test/Config.php: -------------------------------------------------------------------------------- 1 | _mockModels[$modelClass] = $mock; 23 | return $this; 24 | } 25 | 26 | /** 27 | * @param string $modelClass 28 | * @param object $mock 29 | * 30 | * @return $this 31 | */ 32 | public function setResourceModelMock($modelClass, $mock) 33 | { 34 | $this->_mockResourceModels[$modelClass] = $mock; 35 | return $this; 36 | } 37 | 38 | /** 39 | * @param string $modelClass 40 | * @param array $constructArguments 41 | * 42 | * @return false|Mage_Core_Model_Abstract 43 | */ 44 | public function getModelInstance($modelClass = '', $constructArguments = array()) 45 | { 46 | $modelClass = (string) $modelClass; 47 | 48 | if (array_key_exists($modelClass, $this->_mockModels)) { 49 | return $this->_mockModels[$modelClass]; 50 | } 51 | 52 | return parent::getModelInstance($modelClass, $constructArguments); 53 | } 54 | 55 | /** 56 | * Get resource model object by alias 57 | * 58 | * @param string $modelClass 59 | * @param array $constructArguments 60 | * @return object 61 | */ 62 | public function getResourceModelInstance($modelClass='', $constructArguments = array()) 63 | { 64 | $modelClass = (string) $modelClass; 65 | 66 | if (array_key_exists($modelClass, $this->_mockResourceModels)) { 67 | return $this->_mockResourceModels[$modelClass]; 68 | } 69 | 70 | return parent::getResourceModelInstance($modelClass, $constructArguments); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Test/Framework/Tiny/Test/Http/Response.php: -------------------------------------------------------------------------------- 1 | _headersSent = $headersSent; 17 | 18 | return $this; 19 | } 20 | 21 | /** 22 | * @return boolean 23 | */ 24 | public function getHeadersSent() 25 | { 26 | return $this->_headersSent; 27 | } 28 | 29 | /** 30 | * @param bool $throw 31 | * 32 | * @return bool 33 | */ 34 | public function canSendHeaders($throw = false) 35 | { 36 | $canSendHeaders = !$this->getHeadersSent(); 37 | return $canSendHeaders; 38 | } 39 | 40 | /** 41 | * @return Mage_Core_Controller_Response_Http 42 | */ 43 | public function sendHeaders() 44 | { 45 | $this->setHeadersSent(true); 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * @return $this 52 | */ 53 | public function sendResponse() 54 | { 55 | return $this; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /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/Helper/ConfigTest.php: -------------------------------------------------------------------------------- 1 | _instance = Mage::helper('tiny_compressimages/config'); 12 | } 13 | 14 | public function getProductImageTypesToCompressDataProvider() 15 | { 16 | return array( 17 | array( 18 | array( 19 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_BASE => 1, 20 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_SMALL => 1, 21 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_THUMBNAIL => 1, 22 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_SWATCHES => 1, 23 | ), 24 | array( 25 | 'image', 26 | 'small_image', 27 | 'thumbnail', 28 | 'media_image', 29 | 'swatches', 30 | ), 31 | ), 32 | array( 33 | array( 34 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_BASE => 0, 35 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_SMALL => 1, 36 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_THUMBNAIL => 1, 37 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_SWATCHES => 1, 38 | ), 39 | array( 40 | 'small_image', 41 | 'thumbnail', 42 | 'media_image', 43 | 'swatches', 44 | ), 45 | ), 46 | array( 47 | array( 48 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_BASE => 0, 49 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_SMALL => 0, 50 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_THUMBNAIL => 1, 51 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_SWATCHES => 1, 52 | ), 53 | array( 54 | 'thumbnail', 55 | 'media_image', 56 | 'swatches', 57 | ), 58 | ), 59 | array( 60 | array( 61 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_BASE => 0, 62 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_SMALL => 0, 63 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_THUMBNAIL => 0, 64 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_SWATCHES => 1, 65 | ), 66 | array( 67 | 'swatches', 68 | ), 69 | ), 70 | array( 71 | array( 72 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_BASE => 0, 73 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_SMALL => 0, 74 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_THUMBNAIL => 0, 75 | Tiny_CompressImages_Helper_Config::XPATH_IMAGE_TYPE_SWATCHES => 0, 76 | ), 77 | array( 78 | ), 79 | ), 80 | ); 81 | } 82 | 83 | /** 84 | * @dataProvider getProductImageTypesToCompressDataProvider 85 | */ 86 | public function testGetProductImageTypesToCompress($config, $shouldHave) 87 | { 88 | foreach ($config as $path => $value) { 89 | Mage::app()->getStore()->setConfig($path, $value); 90 | } 91 | 92 | $result = $this->_instance->getProductImageTypesToCompress(); 93 | 94 | $this->assertEquals($shouldHave, $result); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /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/Test/Model/ObserverTest.php: -------------------------------------------------------------------------------- 1 | getMock('Tiny_CompressImages_Helper_Tinify'); 7 | 8 | $tinifyHelperMock 9 | ->expects($this->never()) 10 | ->method('setProductImage'); 11 | 12 | $configHelperMock = $this->getMock('Tiny_CompressImages_Helper_Config'); 13 | 14 | $configHelperMock->expects($this->once()) 15 | ->method('isEnabled') 16 | ->willReturn(false); 17 | 18 | $instance = new Tiny_CompressImages_Model_Observer; 19 | $this->setProperty('_tinifyHelper', $tinifyHelperMock, $instance); 20 | $this->setProperty('_configHelper', $configHelperMock, $instance); 21 | 22 | $instance->catalogProductImageSaveAfter(null); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Test/Model/Product/ImageTest.php: -------------------------------------------------------------------------------- 1 | _instance = Mage::getModel('catalog/product_image'); 17 | $this->_helper = $this->getMock('Tiny_CompressImages_Helper_Config'); 18 | 19 | $this->setProperty('_configHelper', $this->_helper); 20 | } 21 | 22 | public function testGetUrl() 23 | { 24 | $this->_helper 25 | ->expects($this->once()) 26 | ->method('isTestMode') 27 | ->willReturn('false'); 28 | 29 | $this->setProperty('_newFile', 'test.jpg'); 30 | 31 | $url = Mage::getBaseUrl('web'); 32 | $result = $this->_instance->getUrl(); 33 | 34 | $this->assertEquals('http://magento.local/media/test.jpg', str_replace($url, 'http://magento.local/', $result)); 35 | 36 | Mage::app()->getStore()->setConfig('web/seo/', 1); 37 | 38 | $this->assertEquals('http://magento.local/media/test.jpg', str_replace($url, 'http://magento.local/', $result)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/Test/Model/System/Config/Backend/Comment/LoggingModeTest.php: -------------------------------------------------------------------------------- 1 | _logPath = Mage::helper('tiny_compressimages')->getLogFilePath(); 18 | $this->_instance = new Tiny_CompressImages_Model_System_Config_Backend_Comment_LoggingMode; 19 | 20 | if (file_exists($this->_logPath)) { 21 | unlink($this->_logPath); 22 | } 23 | } 24 | 25 | public function testCommentReturnsLink() 26 | { 27 | $text = $this->_instance->getCommentText(); 28 | $this->assertNotContains('_logPath); 31 | $text = $this->_instance->getCommentText(); 32 | $this->assertContains('isAllowed('admin/compressimages'); 13 | } 14 | 15 | /** 16 | * Download CompressImages log file 17 | * 18 | * @return $this 19 | */ 20 | public function downloadLogsAction() 21 | { 22 | /** @var Tiny_CompressImages_Helper_Data $helper */ 23 | $helper = Mage::helper('tiny_compressimages'); 24 | $filePath = $helper->getLogFilePath(); 25 | 26 | if (!$helper->getLogFileExists()) { 27 | return $this; 28 | } 29 | 30 | $content = array( 31 | 'type' => 'filename', 32 | 'value' => $filePath, 33 | 'rm' => false, 34 | ); 35 | 36 | $this->postDispatch(); 37 | $this->_prepareDownloadResponse($helper->getLogFilename(), $content); 38 | 39 | return $this; 40 | } 41 | 42 | /** 43 | * @return bool 44 | */ 45 | public function clearCacheAction() 46 | { 47 | try { 48 | Mage::getModel('catalog/product_image')->clearCache(); 49 | Mage::dispatchEvent('clean_catalog_images_cache_after'); 50 | $this->_getSession()->addSuccess( 51 | Mage::helper('adminhtml')->__('The image cache was cleaned.') 52 | ); 53 | } 54 | catch (Mage_Core_Exception $e) { 55 | $this->_getSession()->addError($e->getMessage()); 56 | } 57 | catch (Exception $e) { 58 | $this->_getSession()->addException( 59 | $e, 60 | Mage::helper('adminhtml')->__('An error occurred while clearing the image cache.') 61 | ); 62 | } 63 | 64 | $this->_redirect('adminhtml/system_config/edit/section/tiny_compressimages'); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/controllers/CompressImagesAdminhtml/StatusController.php: -------------------------------------------------------------------------------- 1 | isAllowed('admin/compressimages'); 18 | } 19 | 20 | /** 21 | * Get the Tinify helper. 22 | * 23 | * @return Tiny_CompressImages_Helper_Tinify 24 | */ 25 | public function getHelper() 26 | { 27 | if ($this->_helper === null) { 28 | $this->_helper = Mage::helper('tiny_compressimages/tinify'); 29 | } 30 | 31 | return $this->_helper; 32 | } 33 | 34 | public function getApiStatusAction() 35 | { 36 | if (!$this->_validateFormKey()) { 37 | return; 38 | } 39 | 40 | $result = $this->getHelper()->getApiStatus(); 41 | 42 | /** @var Mage_Core_Helper_Data $coreHelper */ 43 | $coreHelper = Mage::helper('core'); 44 | $this->getResponse()->setBody($coreHelper->jsonEncode($result)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/etc/adminhtml.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Allow everything 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Tiny CompressImages 16 | 500 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/code/community/Tiny/CompressImages/etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2.1.0 6 | stable 7 | 8 | 9 | 10 | 11 | 12 | Tiny_CompressImages_Block 13 | 14 | 15 | 16 | 17 | Tiny_CompressImages_Helper 18 | 19 | 20 | 21 | 22 | Tiny_CompressImages_Model 23 | tiny_compressimages_resource 24 | 25 | 26 | Tiny_CompressImages_Model_Resource 27 | 28 | 29 | tiny_compressimages_image
30 | 31 | 32 | tiny_compressimages_totals
33 |
34 |
35 |
36 | 37 | 38 | Tiny_CompressImages_Model_Product_Image 39 | 40 | 41 |
42 | 43 | 44 | 45 | Tiny_CompressImages_Model_Resource_Setup 46 | Tiny_CompressImages 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | singleton 55 | Tiny_CompressImages_Model_Observer 56 | catalogProductImageSaveAfter 57 | 58 | 59 | 60 | 61 |
62 | 63 | 64 | 65 | 66 | 67 | Tiny_CompressImages 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | Tiny/CompressImages.xml 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | Tiny_CompressImages.csv 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 1 95 | 1 96 | 1 97 | 1 98 | 99 | 100 | all 101 | 2 102 | 103 | 104 | 105 |
106 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/design/adminhtml/default/default/layout/Tiny/CompressImages.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | skin_css 12 | css/Tiny/CompressImages/config.css 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/design/adminhtml/default/default/template/Tiny/CompressImages/Api/keyNotification.phtml: -------------------------------------------------------------------------------- 1 | 2 | isApiKeySet()) : ?> 3 |
4 | __('Add your TinyPNG API key to start compressing images.'); ?> 5 | 6 | __('Go to Image Optimization Settings and add your key.'); ?> 7 | 8 |
9 | 10 | -------------------------------------------------------------------------------- /app/design/adminhtml/default/default/template/Tiny/CompressImages/system/config/form/field/log_file.phtml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | logFileExists()): ?> 10 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/design/adminhtml/default/default/template/Tiny/CompressImages/system/config/form/field/log_status.phtml: -------------------------------------------------------------------------------- 1 | getTotalCompressionInformation(); 12 | 13 | /** @var Tiny_CompressImages_Model_Image $image */ 14 | $_images = $_tinify->getCompressionStatus(); 15 | 16 | $_imagesLimit = 10; 17 | ?> 18 | 19 | 20 | 21 | 22 | 23 | 39 | 40 |
24 |
__('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 |
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 | 56 | 57 | 58 | 59 | 60 | count()): ?> 61 | 62 | 63 | 64 | 65 | 66 | 67 | $_imagesLimit): ?> 68 | 69 | 70 | 71 | 72 | 77 | 78 | 81 | 90 | 91 | 92 | 93 | 94 | getSize() > $_images->count()): ?> 95 | 96 | 97 | 98 | 99 | 100 | $_imagesLimit): ?> 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
73 | 74 | 75 | 76 | getImageType(); ?> 79 | getPercentageSaved(); ?> saved (fileSize($image->getBytesBefore()). ' / '. $_helper->fileSize($image->getBytesAfter()); ?>) 80 | 82 | getCompressedBefore()) : ?> 83 | __('Optimized previously'); ?> 84 | getParentId()) :?> 85 | __('Duplicate image'); ?> 86 | 87 | __('Optimized'); ?> 88 | 89 | getTimeAgo(); ?>
__('...and %d more images', $_images->getSize() - $_images->count()); ?>
__('Show more'); ?>
__('Show less'); ?>
109 | 110 | 111 | 112 | 113 | 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 | -------------------------------------------------------------------------------- /app/design/adminhtml/default/default/template/Tiny/CompressImages/system/config/form/field/support_tab.phtml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | getModuleVersion(); ?> 12 | getModuleStability(); ?> 13 | 14 | 15 | 16 |

17 | __('Image Optimization %s', $_version);?> 18 | 19 | - 20 | 21 |

22 | 23 | 24 | 56 | 57 |
25 | 26 |
27 |
28 |
29 |

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 | CompressImages 52 |
53 |
54 |
55 |
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 | [Build Status](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 --------------------------------------------------------------------------------