Commit 413bb3e4 by Krystian Kowalczuk

Initial module

parents
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Block\Adminhtml;
use Magento\Framework\Data\Form\Element\AbstractElement;
class About extends \Magento\Backend\Block\AbstractBlock implements
\Magento\Framework\Data\Form\Element\Renderer\RendererInterface
{
/**
* @var \Apptrian\ImageOptimizer\Helper\Data
*/
public $helper;
/**
* Constructor
*
* @param \Apptrian\ImageOptimizer\Helper\Data $helper
*/
public function __construct(\Apptrian\ImageOptimizer\Helper\Data $helper)
{
$this->helper = $helper;
}
/**
* Retrieve element HTML markup.
*
* @param \Magento\Framework\Data\Form\Element\AbstractElement $element
* @return string
*/
public function render(AbstractElement $element)
{
$element = null;
$version = $this->helper->getExtensionVersion();
$logopath = 'https://www.apptrian.com/media/apptrian.gif';
$html = <<<HTML
<div style="background: url('$logopath') no-repeat scroll 15px 15px #f8f8f8;
border:1px solid #ccc; min-height:100px; margin:5px 0;
padding:15px 15px 15px 140px;">
<p>
<strong>Apptrian Image Optimizer Extension v$version</strong><br /><br />
Optimize your images, reduce their file size without losing image quality and
speed up your site.
</p>
</div>
HTML;
return $html;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Block\Adminhtml\Button;
use Magento\Framework\Data\Form\Element\AbstractElement;
class Clear extends \Magento\Config\Block\System\Config\Form\Field
{
/**
* Retrieve element HTML markup
*
* @param \Magento\Framework\Data\Form\Element\AbstractElement $element
* @return string
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function _getElementHtml(AbstractElement $element)
{
$element = null;
/** @var \Magento\Backend\Block\Widget\Button $buttonBlock */
$buttonBlock = $this->getForm()->getLayout()
->createBlock('Magento\Backend\Block\Widget\Button');
$url = $this->getUrl("apptrian_imageoptimizer/optimizer/clear");
$confirmText = __('Are you sure you want to do this?');
$data = [
'class' => 'apptrian-imageoptimizer-admin-button-clear',
'label' => __('Clear Index'),
'onclick' => "confirmSetLocation('".$confirmText."', '".$url."')",
];
$html = $buttonBlock->setData($data)->toHtml();
return $html;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Block\Adminhtml\Button;
use Magento\Framework\Data\Form\Element\AbstractElement;
class Optimize extends \Magento\Config\Block\System\Config\Form\Field
{
/**
* Retrieve element HTML markup
*
* @param \Magento\Framework\Data\Form\Element\AbstractElement $element
* @return string
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function _getElementHtml(AbstractElement $element)
{
$element = null;
/** @var \Magento\Backend\Block\Widget\Button $buttonBlock */
$buttonBlock = $this->getForm()->getLayout()
->createBlock('Magento\Backend\Block\Widget\Button');
$url = $this->getUrl("apptrian_imageoptimizer/optimizer/optimize");
$data = [
'class' => 'apptrian-imageoptimizer-admin-button-optimize',
'label' => __('Start Optimization Process'),
'onclick' => "setLocation('" . $url . "')",
];
$html = $buttonBlock->setData($data)->toHtml();
return $html;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Block\Adminhtml\Button;
use Magento\Framework\Data\Form\Element\AbstractElement;
class Scan extends \Magento\Config\Block\System\Config\Form\Field
{
/**
* Retrieve element HTML markup
*
* @param \Magento\Framework\Data\Form\Element\AbstractElement $element
* @return string
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function _getElementHtml(AbstractElement $element)
{
$element = null;
/** @var \Magento\Backend\Block\Widget\Button $buttonBlock */
$buttonBlock = $this->getForm()->getLayout()
->createBlock('Magento\Backend\Block\Widget\Button');
$url = $this->getUrl("apptrian_imageoptimizer/optimizer/scan");
$data = [
'class' => 'apptrian-imageoptimizer-admin-button-scan',
'label' => __('Start Scan and Reindex Process'),
'onclick' => "setLocation('" . $url . "')",
];
$html = $buttonBlock->setData($data)->toHtml();
return $html;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Block\Adminhtml;
use Magento\Framework\Data\Form\Element\AbstractElement;
class Stats extends \Magento\Config\Block\System\Config\Form\Field
{
/**
* @var \Apptrian\ImageOptimizer\Helper\Data
*/
public $helper;
/**
* Constructor
*
* @param \Magento\Backend\Block\Template\Context $context
* @param \Apptrian\ImageOptimizer\Helper\Data $helper
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Apptrian\ImageOptimizer\Helper\Data $helper
) {
$this->helper = $helper;
parent::__construct($context);
}
/**
* Retrieve element HTML markup.
*
* @param \Magento\Framework\Data\Form\Element\AbstractElement $element
* @return string
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function _getElementHtml(AbstractElement $element)
{
$element = null;
$r = [];
$indexed = 0;
$optimized = 0;
try {
$r = $this->helper->getFileCount();
$indexed = $r['indexed'];
$optimized = $r['optimized'];
} catch (\Exception $e) {
$message = $e->getMessage();
return '<div class="apptrian-imageoptimizer-bar-wrapper">
<div class="apptrian-imageoptimizer-bar-outer">
<div class="apptrian-imageoptimizer-bar-inner" style="width: 0;">
</div><div class="apptrian-imageoptimizer-bar-text">
<span style="color: #f00;">' . $message . '</span></div>
</div></div>';
}
// Fix for division by zero possibility
if ($indexed == 0) {
$percent = 0;
} else {
$percent = round((100 * $optimized) / $indexed, 2);
}
$html = '<div class="apptrian-imageoptimizer-bar-wrapper">
<div class="apptrian-imageoptimizer-bar-outer">
<div class="apptrian-imageoptimizer-bar-inner" style="width:'
. $percent .'%;"></div>
<div class="apptrian-imageoptimizer-bar-text"><span>' . $percent
. '% ' . sprintf(__('(%s of %s files)'), $optimized, $indexed)
. '</span></div>
</div></div>';
return $html;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Controller\Adminhtml\Optimizer;
class Clear extends \Magento\Backend\App\Action
{
/**
* @var \Apptrian\ImageOptimizer\Helper\Data
*/
public $dataHelper;
/**
* Constructor
*
* @param \Magento\Backend\App\Action\Context $context
* @param \Apptrian\ImageOptimizer\Helper\Data $dataHelper
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Apptrian\ImageOptimizer\Helper\Data $dataHelper
) {
$this->dataHelper = $dataHelper;
parent::__construct($context);
}
/**
* Clear index action.
*
* @return \Magento\Backend\Model\View\Result\Redirect
*/
public function execute()
{
set_time_limit(18000);
try {
$this->dataHelper->clearIndex();
$this->messageManager->addSuccess(
__('Clear index operation completed successfully.')
);
} catch (\Exception $e) {
$message = __('Clear index operation failed.');
$this->messageManager->addError($message);
$this->messageManager->addError($e->getMessage());
}
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath(
'adminhtml/system_config/edit',
['section' => 'apptrian_imageoptimizer']
);
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Controller\Adminhtml\Optimizer;
class Optimize extends \Magento\Backend\App\Action
{
/**
* @var \Apptrian\ImageOptimizer\Helper\Data
*/
public $dataHelper;
/**
* Constructor
*
* @param \Magento\Backend\App\Action\Context $context
* @param \Apptrian\ImageOptimizer\Helper\Data $dataHelper
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Apptrian\ImageOptimizer\Helper\Data $dataHelper
) {
$this->dataHelper = $dataHelper;
parent::__construct($context);
}
/**
* Optimize action.
*
* @return \Magento\Backend\Model\View\Result\Redirect
*/
public function execute()
{
set_time_limit(18000);
if ($this->dataHelper->isExecFunctionEnabled()) {
try {
$this->dataHelper->optimize();
$this->messageManager->addSuccess(
__('Optimization operations completed successfully.')
);
} catch (\Exception $e) {
$message = __('Optimization failed.');
$this->messageManager->addError($message);
$this->messageManager->addError($e->getMessage());
}
} else {
$message = __(
'Optimization failed because PHP exec() function is disabled.'
);
$this->messageManager->addError($message);
}
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath(
'adminhtml/system_config/edit',
['section' => 'apptrian_imageoptimizer']
);
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Controller\Adminhtml\Optimizer;
class Scan extends \Magento\Backend\App\Action
{
/**
* @var \Apptrian\ImageOptimizer\Helper\Data
*/
public $dataHelper;
/**
* Constructor
*
* @param \Magento\Backend\App\Action\Context $context
* @param \Apptrian\ImageOptimizer\Helper\Data $dataHelper
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Apptrian\ImageOptimizer\Helper\Data $dataHelper
) {
$this->dataHelper = $dataHelper;
parent::__construct($context);
}
/**
* Scan and reindex action.
*
* @return \Magento\Backend\Model\View\Result\Redirect
*/
public function execute()
{
set_time_limit(18000);
try {
$this->dataHelper->scanAndReindex();
$message = $this->messageManager->addSuccess(
__('Scan and reindex operations completed successfully.')
);
} catch (\Exception $e) {
$message = __('Scanning and reindexing failed.');
$this->messageManager->addError($message);
$this->messageManager->addError($e->getMessage());
}
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath(
'adminhtml/system_config/edit',
['section' => 'apptrian_imageoptimizer']
);
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Cron;
class Optimize
{
/**
* @var \Apptrian\ImageOptimizer\Helper\Data
*/
public $helper;
/**
* @var \Psr\Log\LoggerInterface
*/
public $logger;
/**
* Constructor
*
* @param \Apptrian\ImageOptimizer\Helper\Data $helper
* @param \Psr\Log\LoggerInterface $logger
*/
public function __construct(
\Apptrian\ImageOptimizer\Helper\Data $helper,
\Psr\Log\LoggerInterface $logger
) {
$this->helper = $helper;
$this->logger = $logger;
}
/**
* Cron method for executing optmization process.
*/
public function execute()
{
$extensionEnabled = (int) $this->helper->getConfig(
'apptrian_imageoptimizer/general/enabled'
);
$cronJobEnabled = (int) $this->helper->getConfig(
'apptrian_imageoptimizer/cron/enabled_optimize'
);
if ($extensionEnabled && $cronJobEnabled) {
$mPrefix = 'Image Optimizer Cron: Optimization process ';
if ($this->helper->isExecFunctionEnabled()) {
try {
$result = $this->helper->optimize();
if ($result !== true) {
$this->logger->debug($mPrefix . 'failed.');
}
} catch (\Exception $e) {
$this->logger->critical($e);
}
} else {
$this->logger->debug(
$mPrefix . 'failed because PHP exec() function is disabled.'
);
}
}
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Cron;
class Scan
{
/**
* @var \Apptrian\ImageOptimizer\Helper\Data
*/
public $helper;
/**
* @var \Psr\Log\LoggerInterface
*/
public $logger;
/**
* Constructor
*
* @param \Apptrian\ImageOptimizer\Helper\Data $helper
* @param \Psr\Log\LoggerInterface $logger
*/
public function __construct(
\Apptrian\ImageOptimizer\Helper\Data $helper,
\Psr\Log\LoggerInterface $logger
) {
$this->helper = $helper;
$this->logger = $logger;
}
/**
* Cron method for executing scan and reindex process.
*/
public function execute()
{
$extensionEnabled = (int) $this->helper->getConfig(
'apptrian_imageoptimizer/general/enabled'
);
$cronJobEnabled = (int) $this->helper->getConfig(
'apptrian_imageoptimizer/cron/enabled_scan'
);
if ($extensionEnabled && $cronJobEnabled) {
try {
$result = $this->helper->scanAndReindex();
if ($result !== true) {
$mPrefix = 'Image Optimizer Cron: Scan and Reindex process';
$this->logger->debug($mPrefix . ' failed.');
}
} catch (\Exception $e) {
$this->logger->critical($e);
}
}
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Helper;
class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
/**
* @var \Magento\Framework\App\Config\ScopeConfigInterface
*/
public $scopeConfig;
/**
* @var \Magento\Framework\Module\ModuleListInterface
*/
public $moduleList;
/**
* @var \Magento\Framework\Filesystem
*/
public $fileSystem;
/**
* @var \Magento\Framework\Component\ComponentRegistrarInterface
*/
public $componentRegistrar;
/**
* @var \Magento\Framework\Shell
*/
public $shell;
/**
* @var \Psr\Log\LoggerInterface
*/
public $logger;
/**
* Magento Root full path.
*
* @var null|string
*/
public $baseDir = null;
/**
* Module Root full path.
*
* @var null|string
*/
public $moduleDir = null;
/**
* Logging flag.
*
* @var null|int
*/
public $logging = null;
/**
* Path to utilities.
*
* @var null|string
*/
public $utilPath = null;
/**
* Extension (for win binaries)
*
* @var null|string
*/
public $utilExt = null;
/**
* Index filename.
*
* @var string $indexFilename
*/
public $indexFilename = 'apptrian_imageoptimizer_index.data';
/**
* Index path.
*
* @var null|string $indexPath
*/
public $indexPath = null;
/**
* Index array.
*
* @var array $index
*/
public $index = [];
/**
* Total count of files in index.
*
* @var integer $indexTotalCount
*/
public $indexTotalCount = 0;
/**
* Count of files that are optimized.
*
* @var integer $indexOptimizedCount
*/
public $indexOptimizedCount = 0;
/**
* Constructor
*
* @param \Magento\Framework\App\Helper\Context $context
* @param \Magento\Framework\Module\ModuleListInterface $moduleList
* @param \Magento\Framework\Filesystem $fileSystem
* @param \Magento\Framework\Component\ComponentRegistrarInterface $compReg
* @param \Magento\Framework\Shell $shell
*/
public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Framework\Module\ModuleListInterface $moduleList,
\Magento\Framework\Filesystem $fileSystem,
\Magento\Framework\Component\ComponentRegistrarInterface $compReg,
\Magento\Framework\Shell $shell
) {
$this->scopeConfig = $context->getScopeConfig();
$this->logger = $context->getLogger();
$this->moduleList = $moduleList;
$this->fileSystem = $fileSystem;
$this->componentRegistrar = $compReg;
$this->shell = $shell;
parent::__construct($context);
}
/**
* Returns extension version.
*
* @return string
*/
public function getExtensionVersion()
{
$moduleCode = 'Apptrian_ImageOptimizer';
$moduleInfo = $this->moduleList->getOne($moduleCode);
return $moduleInfo['setup_version'];
}
/**
* Based on provided configuration path returns configuration value.
*
* @param string $configPath
* @return string
*/
public function getConfig($configPath)
{
return $this->scopeConfig->getValue($configPath);
}
/**
* Returns Magento Root full path.
*
* @return string
*/
public function getBaseDir()
{
if ($this->baseDir === null) {
$dir = $this->fileSystem->getDirectoryRead(
\Magento\Framework\App\Filesystem\DirectoryList::ROOT
);
$this->baseDir = $dir->getAbsolutePath();
}
return $this->baseDir;
}
/**
* Returns Module Root full path.
*
* @return null|string
*/
public function getModuleDir()
{
if ($this->moduleDir === null) {
$moduleName = 'Apptrian_ImageOptimizer';
$this->moduleDir = $this->componentRegistrar->getPath(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
$moduleName
);
}
return $this->moduleDir;
}
/**
* Optimized way of getting logging flag from config.
*
* @return int
*/
public function isLoggingEnabled()
{
if ($this->logging === null) {
$this->logging = (int) $this->getConfig(
'apptrian_imageoptimizer/utility/log_output'
);
}
return $this->logging;
}
/**
* Checks if exec() function is enabled in php and suhosin config.
*
* @return boolean
*/
public function isExecFunctionEnabled()
{
$r = false;
// PHP disabled functions
$phpDisabledFunctions = array_map(
'strtolower',
array_map('trim', explode(',', ini_get('disable_functions')))
);
// Suhosin disabled functions
$suhosinDisabledFunctions = array_map(
'strtolower',
array_map(
'trim',
explode(',', ini_get('suhosin.executor.func.blacklist'))
)
);
$disabledFunctions = array_merge(
$phpDisabledFunctions,
$suhosinDisabledFunctions
);
$disabled = false;
if (in_array('exec', $disabledFunctions)) {
$disabled = true;
}
if (function_exists('exec') === true && $disabled === false) {
$r = true;
}
return $r;
}
/**
* Based on config returns array of all paths that will be scaned
* for images.
*
* @return array
*/
public function getPaths()
{
$paths = [];
$pathsString = trim(
trim(
$this->getConfig('apptrian_imageoptimizer/general/paths'),
';'
)
);
$rawPaths = explode(';', $pathsString);
foreach ($rawPaths as $p) {
$trimmed = trim(trim($p), '/');
$dirs = explode('/', $trimmed);
$paths[] = implode('/', $dirs);
}
return array_unique($paths);
}
/**
* Optimizes single file.
*
* @param string $filePath
* @return boolean
*/
public function optimizeFile($filePath)
{
$info = pathinfo($filePath);
$output = '';
$exitCode = 0;
try {
switch (strtolower($info['extension'])) {
case 'jpg':
case 'jpeg':
$output = $this->shell
->execute($this->getJpgUtil($filePath));
break;
case 'png':
$output = $this->shell
->execute($this->getPngUtil($filePath));
break;
case 'gif':
$output = $this->shell
->execute($this->getGifUtil($filePath));
break;
}
} catch (\Magento\Framework\Exception\LocalizedException $e) {
$this->logger->debug($e->getMessage());
$this->logger->debug($e->getPrevious()->getMessage());
$exitCode = $e->getPrevious()->getCode();
$this->logger->debug('Exit code: ' . $exitCode);
if ($exitCode == 126) {
$error = 'Image optimization utility is not executable.';
$this->logger->debug($error);
}
return false;
}
if ($this->isLoggingEnabled()) {
$this->logger->debug($filePath);
$this->logger->debug($output);
}
$permissions = (string) $this->getConfig(
'apptrian_imageoptimizer/utility/permissions'
);
if ($permissions) {
chmod($filePath, octdec($permissions));
}
return true;
}
/**
* Optimization process.
*
* @return boolean
*/
public function optimize()
{
$this->loadIndex();
// Get Batch Size
$batchSize = (int) $this->getConfig(
'apptrian_imageoptimizer/general/batch_size'
);
// Get array of files for optimization limited by batch size
$files = $this->getFiles($batchSize);
$id = '';
$item = [];
$toUpdate = [];
$encodedPath = '';
$decodedPath = '';
$filePath = '';
// Optimize batch of files
foreach ($files as $id => $item) {
$encodedPath = $item['f'];
$decodedPath = utf8_decode($encodedPath);
$filePath = realpath($decodedPath);
// If image exists, optimize else remove it from database
if (file_exists($filePath)) {
if ($this->optimizeFile($filePath)) {
$toUpdate[$id]['f'] = $encodedPath;
}
} else {
// Remove files that do not exist anymore from the index
unset($this->index[$id]);
}
}
$i = '';
$f = [];
// Itereate over $toUpdate array and set modified time
// (mtime) takes a split second to update
foreach ($toUpdate as $i => $f) {
$encodedPath = $f['f'];
$decodedPath = utf8_decode($encodedPath);
$filePath = realpath($decodedPath);
if (file_exists($filePath)) {
// Update optimized file information in index
$this->index[$i]['t'] = filemtime($filePath);
}
// Free Memory
unset($toUpdate[$i]);
}
return $this->saveIndex();
}
/**
* Scan and reindex process.
*
* @return boolean
*/
public function scanAndReindex()
{
$this->loadIndex();
$id = '';
$item = [];
$encodedPath = '';
$decodedPath = '';
$filePath = '';
// Check index for files that need to be updated and/or removed
foreach ($this->index as $id => $item) {
$encodedPath = $item['f'];
$decodedPath = utf8_decode($encodedPath);
$filePath = realpath($decodedPath);
if (file_exists($filePath)) {
if ($item['t'] != 0 && filemtime($filePath) != $item['t']) {
// Update time to 0 in index so it can be optimized again
$this->index[$id]['t'] = 0;
}
} else {
// Remove files that do not exist anymore from the index
unset($this->index[$id]);
}
}
$paths = $this->getPaths();
$path = '';
// Scan for new files and add them to the index
foreach ($paths as $path) {
$this->scanAndReindexPath($path);
}
return $this->saveIndex();
}
/**
* Scans provided path for images and adds them to index.
*
* @param string $path
*/
public function scanAndReindexPath($path)
{
$id = '';
$encodedPath = '';
$filePath = '';
$file = null;
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator(
$this->getBaseDir() . $path,
\RecursiveDirectoryIterator::FOLLOW_SYMLINKS
)
);
foreach ($iterator as $file) {
if ($file->isFile()
&& preg_match(
'/^.+\.(jpe?g|gif|png)$/i',
$file->getFilename()
)
) {
$filePath = $file->getRealPath();
if (!is_writable($filePath)) {
continue;
}
$encodedPath = utf8_encode($filePath);
$id = hash('md5', $encodedPath);
// Add only if file is not already in the index
if (!isset($this->index[$id])) {
$this->index[$id] = ['f' => $encodedPath, 't' => 0];
}
}
// Free Memory
$file = null;
}
// Free Memory
$iterator = null;
}
/**
* Checks if server OS is Windows
*
* @return bool
*/
public function isWindows()
{
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
return true;
} else {
return false;
}
}
/**
* Alias for getUtil() and .gif
*
* @param string $filePath
* @return string
*/
public function getGifUtil($filePath)
{
return $this->getUtil('gif', $filePath);
}
/**
* Alias for getUtil() and .jpg
*
* @param string $filePath
* @return string
*/
public function getJpgUtil($filePath)
{
return $this->getUtil('jpg', $filePath);
}
/**
* Alias for getUtil() and .png
*
* @param string $filePath
* @return string
*/
public function getPngUtil($filePath)
{
return $this->getUtil('png', $filePath);
}
/**
* Formats and returns the shell command string for an image optimization
* utility.
*
* @param string $type - This is image type. Valid values gif|jpg|png
* @param string $filePath - Path to the image to be optimized
* @return string
*/
public function getUtil($type, $filePath)
{
$exactPath = $this->getConfig(
'apptrian_imageoptimizer/utility/' . $type . '_path'
);
// If utility exact path is set use it
if ($exactPath != '') {
$cmd = $exactPath;
// Use path to extension's local utilities
} else {
$cmd = $this->getUtilPath()
. '/'
. $this->getConfig('apptrian_imageoptimizer/utility/' . $type)
. $this->getUtilExt();
}
$cmd .= ' ' . $this->getConfig(
'apptrian_imageoptimizer/utility/' . $type . '_options'
);
return str_replace('%filepath%', $filePath, $cmd);
}
/**
* Gets and stores utility extension.
* Checks server OS and determine utility extension.
*
* @return string
*/
public function getUtilExt()
{
if ($this->utilExt === null) {
$this->utilExt = $this->isWindows() ? '.exe' : '';
}
return $this->utilExt;
}
/**
* Gets and stores path to utilities. Checks server OS and config to
* determine the path where image optimization utilities are.
*
* @return string
*/
public function getUtilPath()
{
if ($this->utilPath === null) {
$useSixtyFourBit = (int) $this->getConfig(
'apptrian_imageoptimizer/utility/use64bit'
);
if ($useSixtyFourBit) {
$bit = '64';
} else {
$bit = '32';
}
$os = $this->isWindows() ? 'win' . $bit : 'elf' . $bit;
$pathString = trim(
trim($this->getConfig('apptrian_imageoptimizer/utility/path')),
'/'
);
$dirs = explode('/', $pathString);
$path = implode('/', $dirs);
$this->utilPath = $this->getModuleDir() . '/' . $path . '/' . $os;
}
return $this->utilPath;
}
/**
* Returns index path.
*
* @return string
*/
public function getIndexPath()
{
if ($this->indexPath === null) {
$dir = $this->fileSystem->getDirectoryRead(
\Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR
);
$this->indexPath = $dir->getAbsolutePath() . $this->indexFilename;
}
return $this->indexPath;
}
/**
* Returns array of files for optimization limited by $batchSize.
*
* @param int $batchSize
*/
public function getFiles($batchSize)
{
$files = [];
$counter = 0;
foreach ($this->index as $id => $f) {
if ($counter == $batchSize) {
break;
}
if ($f['t'] == 0) {
$files[$id] = $f;
$counter++;
}
}
return $files;
}
/**
* Returns count of indexed and optmized files.
*
* @return array
*/
public function getFileCount()
{
$this->loadIndex();
$r['indexed'] = $this->indexTotalCount;
$r['optimized'] = $this->indexOptimizedCount;
// Free memory
$this->index = null;
return $r;
}
/**
* Clear index (Empty index file).
*
* @return boolean
*/
public function clearIndex()
{
$r = file_put_contents($this->getIndexPath(), '', LOCK_EX);
if ($r === false) {
$this->logger->debug('Clear index operation failed.');
} else {
$r = true;
}
return $r;
}
/**
* Load index from a file.
*/
public function loadIndex()
{
$filePath = $this->getIndexPath();
if (file_exists($filePath)) {
$line = '';
$l = [];
$id = '';
$file = [];
$str = file_get_contents($filePath);
if ($str != '') {
$data = explode("\n", $str);
// Free Memory
unset($str);
$this->indexTotalCount = count($data);
$i = 0;
for ($i = 0; $i < $this->indexTotalCount; $i++) {
$line = $data[$i];
$l = explode('|', $line);
if (!array_key_exists(0, $l)
|| !array_key_exists(1, $l)
|| !array_key_exists(2, $l)
) {
$i++;
$message = sprintf(
__('Your image index is corrupted at line %s.'),
$i
);
$this->logger->critical($message);
throw new \Exception($message);
}
$id = (string) $l[0];
$file['f'] = (string) $l[1];
$file['t'] = (int) $l[2];
$this->index[$id] = $file;
if ($file['t'] > 0) {
$this->indexOptimizedCount++;
}
// Free Memory
unset($data[$i]);
}
// Free Memory
$data = null;
}
if (!$this->index) {
$this->index = [];
}
}
}
/**
* Save index to a file.
*
* @return boolean
*/
public function saveIndex()
{
$id = '';
$f = '';
$data = [];
$c = 0;
$b = 0;
$r = false;
// Truncate existing index file
$this->clearIndex();
foreach ($this->index as $id => $f) {
// str_replace() removes | from filename because | is delimiter
$data[] = sprintf(
'%s|%s|%d',
$id,
str_replace('|', '', $f['f']),
$f['t']
);
// Free memory
unset($this->index[$id]);
if ($c == 100000) {
// Save part of the file
$r = $this->saveToFile($data, $b);
// Free memory
$data = [];
// Increment batch
$b++;
// Reset count
$c = 0;
} else {
// Increment count
$c++;
}
}
// Save last part of the file
$r = $this->saveToFile($data, $b);
// Free memory
$this->index = null;
if ($r === false) {
$this->logger->debug('Writting index to a file failed.');
} else {
$r = true;
}
return $r;
}
/**
* Saves batch of data to a file.
*
* @param array $data
* @param int $b
*/
public function saveToFile($data, $b)
{
$r = true;
if (!empty($data)) {
$fh = fopen($this->getIndexPath(), 'a');
if ($b != 0) {
fwrite($fh, "\n");
}
$r = fwrite($fh, implode("\n", $data));
fclose($fh);
}
return $r;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Model\Config;
use Magento\Framework\Exception\LocalizedException;
class BatchSize extends \Magento\Framework\App\Config\Value
{
/**
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeSave()
{
$value = $this->getValue();
$validator = \Zend_Validate::is(
$value,
'Regex',
['pattern' => '/^[0-9]+$/']
);
if (!$validator || $value < 1) {
$message = __(
'Please correct Batch Size: "%1".',
$value
);
throw new LocalizedException($message);
}
return $this;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Model\Config;
use Magento\Framework\Exception\LocalizedException;
class Cron extends \Magento\Framework\App\Config\Value
{
/**
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeSave()
{
$value = $this->getValue();
$validator = \Zend_Validate::is(
$value,
'Regex',
['pattern' => '/^[0-9,\-\?\/\*\ ]+$/']
);
if (!$validator) {
$message = __(
'Please correct Cron Expression: "%1".',
$value
);
throw new LocalizedException($message);
}
return $this;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Model\Config;
use Magento\Framework\Exception\LocalizedException;
class ExactPath extends \Magento\Framework\App\Config\Value
{
/**
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeSave()
{
$value = $this->getValue();
$validator = \Zend_Validate::is(
$value,
'Regex',
['pattern' => '/^[\p{L}\p{N}_=,;:!&#\+\*\$\?\|\'\.\-\ \/\\\\]*$/iu']
);
if (!$validator) {
$message = __(
'Please correct Utilities Path: "%1".',
$value
);
throw new LocalizedException($message);
}
return $this;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Model\Config;
use Magento\Framework\Exception\LocalizedException;
class Options extends \Magento\Framework\App\Config\Value
{
/**
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeSave()
{
$value = $this->getValue();
$validator = \Zend_Validate::is(
$value,
'Regex',
['pattern'=>'/^[\p{L}\p{N}_,;:!%&#\<\>\=\+\*\$\?\|\'\.\-\ \/]+$/iu']
);
if (!$validator) {
$message = __(
'Please correct Utility Options: "%1".',
$value
);
throw new LocalizedException($message);
}
return $this;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Model\Config;
use Magento\Framework\Exception\LocalizedException;
class Path extends \Magento\Framework\App\Config\Value
{
/**
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeSave()
{
$value = $this->getValue();
$validator = \Zend_Validate::is(
$value,
'Regex',
['pattern' => '/^[\p{L}\p{N}_,;:!&#\+\*\$\?\|\'\.\-\ \/]+$/iu']
);
if (!$validator) {
$message = __(
'Please correct Utilities Path: "%1".',
$value
);
throw new LocalizedException($message);
}
return $this;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Model\Config;
use Magento\Framework\Exception\LocalizedException;
class Paths extends \Magento\Framework\App\Config\Value
{
/**
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeSave()
{
$value = $this->getValue();
$validator = \Zend_Validate::is(
$value,
'Regex',
['pattern' => '/^[\p{L}\p{N}_,;:!&#\+\*\$\?\|\'\.\-\ \/]+$/iu']
);
if (!$validator) {
$message = __(
'Please correct Paths: "%1".',
$value
);
throw new LocalizedException($message);
}
return $this;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Model\Config;
use Magento\Framework\Exception\LocalizedException;
class Permissions extends \Magento\Framework\App\Config\Value
{
/**
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeSave()
{
$value = $this->getValue();
$validator = \Zend_Validate::is(
$value,
'Regex',
['pattern' => '/^[0-7]*$/']
);
if (!$validator) {
$message = __(
'Please correct Force File Permissions: "%1".',
$value
);
throw new LocalizedException($message);
}
return $this;
}
}
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
namespace Apptrian\ImageOptimizer\Model\Config;
use Magento\Framework\Exception\LocalizedException;
class Utility extends \Magento\Framework\App\Config\Value
{
/**
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeSave()
{
$value = $this->getValue();
$validator = \Zend_Validate::is(
$value,
'Regex',
['pattern' => '/^[\p{L}\p{N}_,;:!&#\+\*\$\?\|\'\.\-\ \/]+$/iu']
);
if (!$validator) {
$message = __(
'Please correct Utility field: "%1".',
$value
);
throw new LocalizedException($message);
}
return $this;
}
}
{
"name": "apptrian/image-optimizer",
"description": "Optimize your images, reduce their file size without losing image quality and speed up your site.",
"type": "magento2-module",
"version": "2.2.3",
"license": [
"OSL-3.0"
],
"require": {
"php": "~5.5|~7.0"
},
"autoload": {
"files": [ "registration.php" ],
"psr-4": {
"Apptrian\\ImageOptimizer\\": ""
}
}
}
<?xml version="1.0"?>
<!--
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="Magento_Backend::stores">
<resource id="Magento_Backend::stores_settings">
<resource id="Magento_Config::config">
<resource id="Apptrian_ImageOptimizer::config_system" title="Apptrian Image Optimizer" sortOrder="1" />
</resource>
</resource>
</resource>
</resource>
</resources>
</acl>
</config>
<?xml version="1.0"?>
<!--
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="admin">
<route id="apptrian_imageoptimizer" frontName="apptrian_imageoptimizer">
<module name="Apptrian_ImageOptimizer" after="Magento_Backend" />
</route>
</router>
</config>
<?xml version="1.0"?>
<!--
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="apptrian_extensions" translate="label" sortOrder="1000000">
<label>Apptrian Extensions</label>
</tab>
<section id="apptrian_imageoptimizer" translate="label" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Image Optimizer</label>
<tab>apptrian_extensions</tab>
<resource>Apptrian_ImageOptimizer::config_system</resource>
<group id="about" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
<label>About</label>
<frontend_model>Apptrian\ImageOptimizer\Block\Adminhtml\About</frontend_model>
</group>
<group id="general" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0">
<label>General</label>
<field id="enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Enabled</label>
<comment>Enables or disables extension.</comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="batch_size" translate="label comment" type="text" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Batch Size</label>
<comment>Number of images to be optimized per request.</comment>
<validate>validate-no-html-tags validate-digits validate-greater-than-zero</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\BatchSize</backend_model>
</field>
<field id="paths" translate="label comment tooltip" type="textarea" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Paths</label>
<comment>Paths to be scanned for images.</comment>
<tooltip><![CDATA[Paths are relative to your Magento root directory.<br />Use "/" as directory separator.<br />Use ";" to separate paths.]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Paths</backend_model>
</field>
<field id="heading_scan" translate="label" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0">
<label>1. Scan and Reindex</label>
<frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model>
<attribute type="shared">1</attribute>
</field>
<field id="scan" translate="label" type="button" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Start Scan and Reindex Process</label>
<frontend_model>Apptrian\ImageOptimizer\Block\Adminhtml\Button\Scan</frontend_model>
</field>
<field id="heading_optimize" translate="label" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0">
<label>2. Optimize</label>
<frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model>
<attribute type="shared">1</attribute>
</field>
<field id="optimize" translate="label" type="button" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Start Optimization Process</label>
<frontend_model>Apptrian\ImageOptimizer\Block\Adminhtml\Button\Optimize</frontend_model>
</field>
<field id="heading_stats" translate="label" sortOrder="8" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Progress Bar</label>
<frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model>
<attribute type="shared">1</attribute>
</field>
<field id="stats" translate="label tooltip" type="button" sortOrder="9" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Bar on the right represents how many images are optimized.</label>
<tooltip><![CDATA[Every time you click "Start Optimization Process" (or cron job executes) depending on "Batch Size" value and number of indexed files, progress bar will move.<br />If progress bar does not move most likely reason is file permissions.<br /><strong>Utility binaries located in module_root/bin must be executable which means you have to set right permissions on files inside this directory.</strong><br />If you are getting less than 100% despite you clicked "Start Optimization Process" button more than several times and the percentage is stuck to same value that means some of your files are corrupt. To find out which files are corrupt in "Utility" section for "Log Utility Output" select "Yes". Refresh your cache and click "Start Optimization Process" button again. After it is done you will find a list of corrupt files in<br />var/log/debug.log<br />You can inspect the log and fix the files yourself. Do not forget to set "Log Utility Output" back to "No".]]></tooltip>
<frontend_model>Apptrian\ImageOptimizer\Block\Adminhtml\Stats</frontend_model>
</field>
<field id="heading_clear" translate="label" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Clear Index</label>
<frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model>
<attribute type="shared">1</attribute>
</field>
<field id="clear" translate="label" type="button" sortOrder="11" showInDefault="1" showInWebsite="0" showInStore="0">
<label>This resets above progress bar and you will have to index and optimize all images again.</label>
<frontend_model>Apptrian\ImageOptimizer\Block\Adminhtml\Button\Clear</frontend_model>
</field>
</group>
<group id="cron" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Cron</label>
<field id="heading_warning" translate="label" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
<label>WARNING! Do not change any options! (Unless you know what you are doing.)</label>
<frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model>
<attribute type="shared">1</attribute>
</field>
<field id="enabled_scan" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Enable Scan and Reindex Process Cron Job</label>
<comment>Enables or disables scan and reindex process cron job.</comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="expression_scan" translate="label comment tooltip" type="text" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Cron Expression for Scan and Reindex Process</label>
<comment><![CDATA[Cron expression used to trigger scan and reindex process. Example: 15 4 * * * (once a day at 4:15 AM)]]></comment>
<tooltip><![CDATA[Examples:<br />*/5 * * * * (every 5 minutes)<br />0,30 * * * * (twice an hour)<br />0 * * * * (once an hour)<br />0 0,12 * * * (twice a day)<br />0 0 * * * (once a day)<br />0 0 * * 0 (once a week)<br />0 0 1,15 * * (1st and 15th)<br />0 0 1 * * (once a month)<br />0 0 1 1 * (once a year)]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Cron</backend_model>
<depends>
<field id="enabled_scan">1</field>
</depends>
</field>
<field id="enabled_optimize" translate="label comment" type="select" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Enable Optimization Process Cron Job</label>
<comment>Enables or disables optimization process cron job.</comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="expression_optimize" translate="label comment tooltip" type="text" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Cron Expression for Optimization Process</label>
<comment><![CDATA[Cron expression used to trigger image optimization process. Example: 0 * * * * (once an hour)]]></comment>
<tooltip><![CDATA[Examples:<br />*/5 * * * * (every 5 minutes)<br />0,30 * * * * (twice an hour)<br />0 * * * * (once an hour)<br />0 0,12 * * * (twice a day)<br />0 0 * * * (once a day)<br />0 0 * * 0 (once a week)<br />0 0 1,15 * * (1st and 15th)<br />0 0 1 * * (once a month)<br />0 0 1 1 * (once a year)]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Cron</backend_model>
<depends>
<field id="enabled_optimize">1</field>
</depends>
</field>
</group>
<group id="utility" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Utility</label>
<field id="heading_warning" translate="label" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
<label>WARNING! Do not change any options! (Unless you know what you are doing.)</label>
<frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model>
<attribute type="shared">1</attribute>
</field>
<field id="use64bit" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Use 64-bit Utilities</label>
<comment>Use only if your site is on 64-bit server.</comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="log_output" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Log Utility Output</label>
<comment>Log output information generated by utilities.</comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="permissions" translate="label comment tooltip" type="text" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Force File Permissions</label>
<comment>Force file permissions after optimization. Example: 0644</comment>
<tooltip><![CDATA[Optimization utilities might change permissions of files they optimize (usually to 0644 or 0666). Use this option to force permissions you want. If you do not want to force file permission leave this field empty.]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Permissions</backend_model>
</field>
<field id="path" translate="label comment tooltip" type="text" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Utilities Path</label>
<comment>Path to optimization utilities.</comment>
<tooltip><![CDATA[Path is relative to module root directory.<br />Module root directory can be <strong>app/code/Apptrian/ImageOptimizer</strong><br />or<br /><strong>vendor/apptrian/image-optimizer</strong><br />Use "/" as directory separator.<br />Inside this directory you must have following directories:<br /><strong>elf32</strong><br /><strong>elf64</strong><br /><strong>win32</strong><br /><strong>win64</strong><br /> Inside these directories put <strong>command line binaries</strong>.]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Path</backend_model>
</field>
<field id="gif" translate="label comment tooltip" type="text" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0">
<label>GIF Utility</label>
<comment>Optimization utility for .gif files.</comment>
<tooltip><![CDATA[Do not include the .exe extension for Windows binaries.<br /><strong>Command line binaries only!</strong>]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Utility</backend_model>
</field>
<field id="gif_path" translate="label comment tooltip" type="text" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0">
<label>GIF Utility Exact Path</label>
<comment>WARNING! Read the tooltip for more information.</comment>
<tooltip><![CDATA[If utility you want to use is already installed on your server, you can specify exact path to it. When generating command this exact path to utility will be used and then "Utility Options" will be appended to it. Example:<br/>/usr/bin/gifsicle<br/>If your utility is added to your system path (which means it is accessible from every directory) then type only utility executable filename. If you do not want to use this option leave this field empty. (Do not leave any white space characters.)]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\ExactPath</backend_model>
</field>
<field id="gif_options" translate="label comment tooltip" type="text" sortOrder="8" showInDefault="1" showInWebsite="0" showInStore="0">
<label>GIF Utility Options</label>
<comment>Options for optimization of .gif files.</comment>
<tooltip><![CDATA[The <strong>%filepath%</strong> will be substituted with image file path by the extension.]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Options</backend_model>
</field>
<field id="jpg" translate="label comment tooltip" type="text" sortOrder="9" showInDefault="1" showInWebsite="0" showInStore="0">
<label>JPG Utility</label>
<comment>Optimization utility for .jpg files.</comment>
<tooltip><![CDATA[Do not include the .exe extension for Windows binaries.<br /><strong>Command line binaries only!</strong>]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Utility</backend_model>
</field>
<field id="jpg_path" translate="label comment tooltip" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0">
<label>JPG Utility Exact Path</label>
<comment>WARNING! Read the tooltip for more information.</comment>
<tooltip><![CDATA[If utility you want to use is already installed on your server, you can specify exact path to it. When generating command this exact path to utility will be used and then "Utility Options" will be appended to it. Example:<br/>/usr/bin/jpegtran<br/>If your utility is added to your system path (which means it is accessible from every directory) then type only utility executable filename. If you do not want to use this option leave this field empty. (Do not leave any white space characters.)]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\ExactPath</backend_model>
</field>
<field id="jpg_options" translate="label comment tooltip" type="text" sortOrder="11" showInDefault="1" showInWebsite="0" showInStore="0">
<label>JPG Utility Options</label>
<comment>Options for optimization of .jpg files.</comment>
<tooltip><![CDATA[The <strong>%filepath%</strong> will be substituted with image file path by the extension.]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Options</backend_model>
</field>
<field id="png" translate="label comment tooltip" type="text" sortOrder="12" showInDefault="1" showInWebsite="0" showInStore="0">
<label>PNG Utility</label>
<comment>Optimization utility for .png files.</comment>
<tooltip><![CDATA[Do not include the .exe extension for Windows binaries.<br /><strong>Command line binaries only!</strong>]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Utility</backend_model>
</field>
<field id="png_path" translate="label comment tooltip" type="text" sortOrder="13" showInDefault="1" showInWebsite="0" showInStore="0">
<label>PNG Utility Exact Path</label>
<comment>WARNING! Read the tooltip for more information.</comment>
<tooltip><![CDATA[If utility you want to use is already installed on your server, you can specify exact path to it. When generating command this exact path to utility will be used and then "Utility Options" will be appended to it. Example:<br/>/usr/bin/optipng<br/>If your utility is added to your system path (which means it is accessible from every directory) then type only utility executable filename. If you do not want to use this option leave this field empty. (Do not leave any white space characters.)]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\ExactPath</backend_model>
</field>
<field id="png_options" translate="label comment tooltip" type="text" sortOrder="14" showInDefault="1" showInWebsite="0" showInStore="0">
<label>PNG Utility Options</label>
<comment>Options for optimization of .png files.</comment>
<tooltip><![CDATA[The <strong>%filepath%</strong> will be substituted with image file path by the extension.]]></tooltip>
<validate>validate-no-html-tags</validate>
<backend_model>Apptrian\ImageOptimizer\Model\Config\Options</backend_model>
</field>
</group>
</section>
</system>
</config>
<?xml version="1.0"?>
<!--
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<apptrian_imageoptimizer>
<general>
<enabled>1</enabled>
<batch_size>50</batch_size>
<paths>pub</paths>
</general>
<cron>
<enabled_scan>1</enabled_scan>
<expression_scan>15 4 * * *</expression_scan>
<enabled_optimize>1</enabled_optimize>
<expression_optimize>0 * * * *</expression_optimize>
</cron>
<utility>
<use64bit>0</use64bit>
<log_output>0</log_output>
<permissions></permissions>
<path>bin</path>
<gif>gifsicle</gif>
<gif_path></gif_path>
<gif_options>-b -O3 %filepath%</gif_options>
<jpg>jpegtran</jpg>
<jpg_path></jpg_path>
<jpg_options>-copy none -optimize -progressive -outfile %filepath% %filepath%</jpg_options>
<png>optipng</png>
<png_path></png_path>
<png_options>-o7 -quiet -strip all %filepath%</png_options>
</utility>
</apptrian_imageoptimizer>
</default>
</config>
<?xml version="1.0"?>
<!--
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/cron_groups.xsd">
<group id="apptrian_imageoptimizer">
<schedule_generate_every>15</schedule_generate_every>
<schedule_ahead_for>20</schedule_ahead_for>
<schedule_lifetime>15</schedule_lifetime>
<history_cleanup_every>10</history_cleanup_every>
<history_success_lifetime>60</history_success_lifetime>
<history_failure_lifetime>600</history_failure_lifetime>
<use_separate_process>0</use_separate_process>
</group>
</config>
<?xml version="1.0"?>
<!--
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
<group id="apptrian_imageoptimizer">
<job name="apptrian_imageoptimizer_scan" instance="Apptrian\ImageOptimizer\Cron\Scan" method="execute">
<config_path>apptrian_imageoptimizer/cron/expression_scan</config_path>
</job>
<job name="apptrian_imageoptimizer_optimize" instance="Apptrian\ImageOptimizer\Cron\Optimize" method="execute">
<config_path>apptrian_imageoptimizer/cron/expression_optimize</config_path>
</job>
</group>
</config>
<?xml version="1.0"?>
<!--
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Apptrian_ImageOptimizer" setup_version="2.2.3"/>
</config>
"Are you sure you want to do this?","Are you sure you want to do this?"
"Clear Index","Clear Index"
"Start Optimization Process","Start Optimization Process"
"Start Scan and Reindex Process","Start Scan and Reindex Process"
"(%s of %s files)","(%s of %s files)"
"Clear index operation completed successfully.","Clear index operation completed successfully."
"Clear index operation failed.","Clear index operation failed."
"Optimization operations completed successfully.","Optimization operations completed successfully."
"Optimization failed.","Optimization failed."
"Optimization failed because PHP exec() function is disabled.","Optimization failed because PHP exec() function is disabled."
"Scan and reindex operations completed successfully.","Scan and reindex operations completed successfully."
"Scanning and reindexing failed.","Scanning and reindexing failed."
"Please correct Batch Size: ""%1"".","Please correct Batch Size: ""%1""."
"Please correct Cron Expression: ""%1"".","Please correct Cron Expression: ""%1""."
"Please correct Utilities Path: ""%1"".","Please correct Utilities Path: ""%1""."
"Please correct Utility Options: ""%1"".","Please correct Utility Options: ""%1""."
"Please correct Paths: ""%1"".","Please correct Paths: ""%1""."
"Please correct Force File Permissions: ""%1"".","Please correct Force File Permissions: ""%1""."
"Please correct Utility field: ""%1"".","Please correct Utility field: ""%1""."
"Apptrian Extensions","Apptrian Extensions"
"Info","Info"
"Image Optimizer","Image Optimizer"
"About","About"
"General","General"
"Enabled","Enabled"
"Enables or disables extension.","Enables or disables extension."
"Batch Size","Batch Size"
"Number of images to be optimized per request.","Number of images to be optimized per request."
"Paths","Paths"
"Paths to be scanned for images.","Paths to be scanned for images."
"Paths are relative to your Magento root directory.<br />Use ""/"" as directory separator.<br />Use "";"" to separate paths.","Paths are relative to your Magento root directory.<br />Use ""/"" as directory separator.<br />Use "";"" to separate paths."
"1. Scan and Reindex","1. Scan and Reindex"
"2. Optimize","2. Optimize"
"Progress Bar","Progress Bar"
"Bar on the right represents how many images are optimized.","Bar on the right represents how many images are optimized."
"Every time you click ""Start Optimization Process"" (or cron job executes) depending on ""Batch Size"" value and number of indexed files, progress bar will move.<br />If progress bar does not move most likely reason is file permissions.<br /><strong>Utility binaries located in module_root/bin must be executable which means you have to set right permissions on files inside this directory.</strong><br />If you are getting less than 100% despite you clicked ""Start Optimization Process"" button more than several times and the percentage is stuck to same value that means some of your files are corrupt. To find out which files are corrupt in ""Utility"" section for ""Log Utility Output"" select ""Yes"". Refresh your cache and click ""Start Optimization Process"" button again. After it is done you will find a list of corrupt files in<br />var/log/debug.log<br />You can inspect the log and fix the files yourself. Do not forget to set ""Log Utility Output"" back to ""No"".","Every time you click ""Start Optimization Process"" (or cron job executes) depending on ""Batch Size"" value and number of indexed files, progress bar will move.<br />If progress bar does not move most likely reason is file permissions.<br /><strong>Utility binaries located in module_root/bin must be executable which means you have to set right permissions on files inside this directory.</strong><br />If you are getting less than 100% despite you clicked ""Start Optimization Process"" button more than several times and the percentage is stuck to same value that means some of your files are corrupt. To find out which files are corrupt in ""Utility"" section for ""Log Utility Output"" select ""Yes"". Refresh your cache and click ""Start Optimization Process"" button again. After it is done you will find a list of corrupt files in<br />var/log/debug.log<br />You can inspect the log and fix the files yourself. Do not forget to set ""Log Utility Output"" back to ""No""."
"This resets above progress bar and you will have to index and optimize all images again.","This resets above progress bar and you will have to index and optimize all images again."
"Cron","Cron"
"WARNING! Do not change any options! (Unless you know what you are doing.)","WARNING! Do not change any options! (Unless you know what you are doing.)"
"Enable Scan and Reindex Process Cron Job","Enable Scan and Reindex Process Cron Job"
"Enables or disables scan and reindex process cron job.","Enables or disables scan and reindex process cron job."
"Cron Expression for Scan and Reindex Process","Cron Expression for Scan and Reindex Process"
"Cron expression used to trigger scan and reindex process. Example: 15 4 * * * (once a day at 4:15 AM)","Cron expression used to trigger scan and reindex process. Example: 15 4 * * * (once a day at 4:15 AM)"
"Examples:<br />*/5 * * * * (every 5 minutes)<br />0,30 * * * * (twice an hour)<br />0 * * * * (once an hour)<br />0 0,12 * * * (twice a day)<br />0 0 * * * (once a day)<br />0 0 * * 0 (once a week)<br />0 0 1,15 * * (1st and 15th)<br />0 0 1 * * (once a month)<br />0 0 1 1 * (once a year)","Examples:<br />*/5 * * * * (every 5 minutes)<br />0,30 * * * * (twice an hour)<br />0 * * * * (once an hour)<br />0 0,12 * * * (twice a day)<br />0 0 * * * (once a day)<br />0 0 * * 0 (once a week)<br />0 0 1,15 * * (1st and 15th)<br />0 0 1 * * (once a month)<br />0 0 1 1 * (once a year)"
"Enable Optimization Process Cron Job","Enable Optimization Process Cron Job"
"Enables or disables optimization process cron job.","Enables or disables optimization process cron job."
"Cron Expression for Optimization Process","Cron Expression for Optimization Process"
"Cron expression used to trigger image optimization process. Example: 0 * * * * (once an hour)","Cron expression used to trigger image optimization process. Example: 0 * * * * (once an hour)"
"Utility","Utility"
"Use 64-bit Utilities","Use 64-bit Utilities"
"Use only if your site is on 64-bit server.","Use only if your site is on 64-bit server."
"Log Utility Output","Log Utility Output"
"Log output information generated by utilities.","Log output information generated by utilities."
"Force File Permissions","Force File Permissions"
"Force file permissions after optimization. Example: 0644","Force file permissions after optimization. Example: 0644"
"Optimization utilities might change permissions of files they optimize (usually to 0644 or 0666). Use this option to force permissions you want. If you do not want to force file permission leave this field empty.","Optimization utilities might change permissions of files they optimize (usually to 0644 or 0666). Use this option to force permissions you want. If you do not want to force file permission leave this field empty."
"Utilities Path","Utilities Path"
"Path to optimization utilities.","Path to optimization utilities."
"Path is relative to module root directory.<br />Module root directory can be <strong>app/code/Apptrian/ImageOptimizer</strong><br />or<br /><strong>vendor/apptrian/image-optimizer</strong><br />Use ""/"" as directory separator.<br />Inside this directory you must have following directories:<br /><strong>elf32</strong><br /><strong>elf64</strong><br /><strong>win32</strong><br /><strong>win64</strong><br /> Inside these directories put <strong>command line binaries</strong>.","Path is relative to module root directory.<br />Module root directory can be <strong>app/code/Apptrian/ImageOptimizer</strong><br />or<br /><strong>vendor/apptrian/image-optimizer</strong><br />Use ""/"" as directory separator.<br />Inside this directory you must have following directories:<br /><strong>elf32</strong><br /><strong>elf64</strong><br /><strong>win32</strong><br /><strong>win64</strong><br /> Inside these directories put <strong>command line binaries</strong>."
"GIF Utility","GIF Utility"
"Optimization utility for .gif files.","Optimization utility for .gif files."
"Do not include the .exe extension for Windows binaries.<br /><strong>Command line binaries only!</strong>","Do not include the .exe extension for Windows binaries.<br /><strong>Command line binaries only!</strong>"
"GIF Utility Exact Path","GIF Utility Exact Path"
"WARNING! Read the tooltip for more information.","WARNING! Read the tooltip for more information."
"If utility you want to use is already installed on your server, you can specify exact path to it. When generating command this exact path to utility will be used and then ""Utility Options"" will be appended to it. Example:<br/>/usr/bin/gifsicle<br/>If your utility is added to your system path (which means it is accessible from every directory) then type only utility executable filename. If you do not want to use this option leave this field empty. (Do not leave any white space characters.)","If utility you want to use is already installed on your server, you can specify exact path to it. When generating command this exact path to utility will be used and then ""Utility Options"" will be appended to it. Example:<br/>/usr/bin/gifsicle<br/>If your utility is added to your system path (which means it is accessible from every directory) then type only utility executable filename. If you do not want to use this option leave this field empty. (Do not leave any white space characters.)"
"GIF Utility Options","GIF Utility Options"
"Options for optimization of .gif files.","Options for optimization of .gif files."
"The <strong>%filepath%</strong> will be substituted with image file path by the extension.","The <strong>%filepath%</strong> will be substituted with image file path by the extension."
"JPG Utility","JPG Utility"
"Optimization utility for .jpg files.","Optimization utility for .jpg files."
"JPG Utility Exact Path","JPG Utility Exact Path"
"If utility you want to use is already installed on your server, you can specify exact path to it. When generating command this exact path to utility will be used and then ""Utility Options"" will be appended to it. Example:<br/>/usr/bin/jpegtran<br/>If your utility is added to your system path (which means it is accessible from every directory) then type only utility executable filename. If you do not want to use this option leave this field empty. (Do not leave any white space characters.)","If utility you want to use is already installed on your server, you can specify exact path to it. When generating command this exact path to utility will be used and then ""Utility Options"" will be appended to it. Example:<br/>/usr/bin/jpegtran<br/>If your utility is added to your system path (which means it is accessible from every directory) then type only utility executable filename. If you do not want to use this option leave this field empty. (Do not leave any white space characters.)"
"JPG Utility Options","JPG Utility Options"
"Options for optimization of .jpg files.","Options for optimization of .jpg files."
"PNG Utility","PNG Utility"
"Optimization utility for .png files.","Optimization utility for .png files."
"PNG Utility Exact Path","PNG Utility Exact Path"
"If utility you want to use is already installed on your server, you can specify exact path to it. When generating command this exact path to utility will be used and then ""Utility Options"" will be appended to it. Example:<br/>/usr/bin/optipng<br/>If your utility is added to your system path (which means it is accessible from every directory) then type only utility executable filename. If you do not want to use this option leave this field empty. (Do not leave any white space characters.)","If utility you want to use is already installed on your server, you can specify exact path to it. When generating command this exact path to utility will be used and then ""Utility Options"" will be appended to it. Example:<br/>/usr/bin/optipng<br/>If your utility is added to your system path (which means it is accessible from every directory) then type only utility executable filename. If you do not want to use this option leave this field empty. (Do not leave any white space characters.)"
"PNG Utility Options","PNG Utility Options"
"Options for optimization of .png files.","Options for optimization of .png files."
<?php
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
use \Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Apptrian_ImageOptimizer',
__DIR__
);
<?xml version="1.0"?>
<!--
/**
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="Apptrian_ImageOptimizer::css/apptrian_imageoptimizer.css" media="all"/>
</head>
</page>
/*!
* @category Apptrian
* @package Apptrian_ImageOptimizer
* @author Apptrian
* @copyright Copyright (c) Apptrian (http://www.apptrian.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License
*/
.apptrian-imageoptimizer-bar-wrapper {
display: inline-block;
width: 98%;
margin: .4em .4em -0.7em 0;
}
.apptrian-imageoptimizer-bar-outer {
position: relative;
background: #eee;
height: 2em;
border: 1px solid #adadad;
border-radius: 4px;
}
.apptrian-imageoptimizer-bar-inner {
height: 2em;
border-radius: 4px;
background: rgb(210,255,82);
background: -moz-linear-gradient(top, rgba(210,255,82,1) 0%, rgba(145,232,66,1) 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(210,255,82,1)), color-stop(100%,rgba(145,232,66,1)));
background: -webkit-linear-gradient(top, rgba(210,255,82,1) 0%,rgba(145,232,66,1) 100%);
background: -o-linear-gradient(top, rgba(210,255,82,1) 0%,rgba(145,232,66,1) 100%);
background: -ms-linear-gradient(top, rgba(210,255,82,1) 0%,rgba(145,232,66,1) 100%);
background: linear-gradient(to bottom, rgba(210,255,82,1) 0%,rgba(145,232,66,1) 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#d2ff52', endColorstr='#91e842',GradientType=0 );
}
.apptrian-imageoptimizer-bar-text {
position: absolute;
top: 0;
left: 0;
height: 2em;
width: 100%;
text-align: center;
}
.apptrian-imageoptimizer-bar-text span {
line-height: 2em;
vertical-align: middle;
}
.accordion .config .value.with-tooltip {
font-size: 1em;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment