diff --git a/README.md b/README.md index 7fa8987..8914339 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -imageSlim 1.0 (beta) +imageSlim 1.0 =============== The Image Slenderizer, for MODX. @@ -77,8 +77,12 @@ Properties Yes &remoteImages - Allow imageSlim to work with images from other servers.
Requires proper settings for allow_url_fopen in PHP and phpthumb_nohotlink_enabled and phpthumb_nohotlink_valid_domains in the MODX system settings (core > phpthumb)
Remote images take longer to process than local ones, obviously. + Allow imageSlim to work with images from other servers.
Requires cURL and proper settings for phpthumb_nohotlink_enabled and phpthumb_nohotlink_valid_domains in the MODX system settings (core > phpthumb)
Remote images take longer to process than local ones obviously, but imageSlim does cache them locally in assets/components/imageslim/cache/, so it's only a one-time performance hit. No + + &remoteTimeout + Maximum amount of time to allow for a remote image download.
Units: seconds + 5 &q JPEG quality: 1 (worst) – 95 (best) diff --git a/_build/build.transport.php b/_build/build.transport.php index f4db3d1..5f04f0d 100644 --- a/_build/build.transport.php +++ b/_build/build.transport.php @@ -30,13 +30,13 @@ define('PKG_NAME','imageSlim'); define('PKG_NAME_LOWER','imageslim'); define('PKG_VERSION','1.0.0'); -define('PKG_RELEASE','beta1'); +define('PKG_RELEASE','pl'); define('PKG_CATEGORY','imageSlim'); /* Set package options - you can turn these on one-by-one * as you create the transport package * */ -$hasAssets = false; /* Transfer the files in the assets dir. */ +$hasAssets = true; /* Transfer the files in the assets dir. */ $hasCore = true; /* Transfer the files in the core dir. */ $hasSnippets = true; $hasChunks = false; diff --git a/_build/data/properties/properties.imageslim.php b/_build/data/properties/properties.imageslim.php index 5b3bb6c..57fc835 100644 --- a/_build/data/properties/properties.imageslim.php +++ b/_build/data/properties/properties.imageslim.php @@ -3,85 +3,98 @@ /** * Default properties for the imageSlim snippet * @author Jason Grant - * 02/22/13 * * @package imageslim * @subpackage build */ $properties = array( - array( - 'name' => 'convertThreshold', - 'desc' => 'prop_is.convertThreshold_desc', - 'type' => 'textfield', - 'options' => '', - 'value' => '', - 'lexicon' => 'imageslim:default' - ), - array( - 'name' => 'fixAspect', - 'desc' => 'prop_is.fixAspect_desc', - 'type' => 'combo-boolean', - 'options' => '', - 'value' => '1', - 'lexicon' => 'imageslim:default' - ), - array( - 'name' => 'maxHeight', - 'desc' => 'prop_is.maxHeight_desc', - 'type' => 'integer', - 'options' => '', - 'value' => '', - 'lexicon' => 'imageslim:default' - ), - array( - 'name' => 'maxWidth', - 'desc' => 'prop_is.maxWidth_desc', - 'type' => 'integer', - 'options' => '', - 'value' => '', - 'lexicon' => 'imageslim:default' - ), - array( - 'name' => 'phpthumbof', - 'desc' => 'prop_is.phpthumbof_desc', - 'type' => 'textfield', - 'options' => '', - 'value' => '', - 'lexicon' => 'imageslim:default' - ), - array( - 'name' => 'q', - 'desc' => 'prop_is.q_desc', - 'type' => 'integer', - 'options' => '', - 'value' => '', - 'lexicon' => 'imageslim:default' - ), - array( - 'name' => 'remoteImages', - 'desc' => 'prop_is.remoteImages_desc', - 'type' => 'combo-boolean', - 'options' => '', - 'value' => '0', - 'lexicon' => 'imageslim:default' - ), - array( - 'name' => 'scale', - 'desc' => 'prop_is.scale_desc', - 'type' => 'textfield', - 'options' => '', - 'value' => '1', - 'lexicon' => 'imageslim:default' - ), - array( - 'name' => 'debug', - 'desc' => 'prop_is.debug_desc', - 'type' => 'combo-boolean', - 'options' => '', - 'value' => '0', - 'lexicon' => 'imageslim:default' - ) + array( + 'name' => 'fixAspect', + 'desc' => 'prop_is.fixAspect_desc', + 'type' => 'combo-boolean', + 'options' => '', + 'value' => '1', + 'area' => 'Dimensions', + 'lexicon' => 'imageslim:default' + ), + array( + 'name' => 'maxHeight', + 'desc' => 'prop_is.maxHeight_desc', + 'type' => 'integer', + 'options' => '', + 'value' => '', + 'area' => 'Dimensions', + 'lexicon' => 'imageslim:default' + ), + array( + 'name' => 'maxWidth', + 'desc' => 'prop_is.maxWidth_desc', + 'type' => 'integer', + 'options' => '', + 'value' => '', + 'area' => 'Dimensions', + 'lexicon' => 'imageslim:default' + ), + array( + 'name' => 'scale', + 'desc' => 'prop_is.scale_desc', + 'type' => 'textfield', + 'options' => '', + 'value' => '1', + 'area' => 'Dimensions', + 'lexicon' => 'imageslim:default' + ), + array( + 'name' => 'remoteImages', + 'desc' => 'prop_is.remoteImages_desc', + 'type' => 'combo-boolean', + 'options' => '', + 'value' => '0', + 'area' => 'Remote Images', + 'lexicon' => 'imageslim:default' + ), + array( + 'name' => 'remoteTimeout', + 'desc' => 'prop_is.remoteTimeout_desc', + 'type' => 'integer', + 'options' => '', + 'value' => '5', + 'area' => 'Remote Images', + 'lexicon' => 'imageslim:default' + ), + array( + 'name' => 'convertThreshold', + 'desc' => 'prop_is.convertThreshold_desc', + 'type' => 'textfield', + 'options' => '', + 'value' => '', + 'lexicon' => 'imageslim:default' + ), + array( + 'name' => 'phpthumbof', + 'desc' => 'prop_is.phpthumbof_desc', + 'type' => 'textfield', + 'options' => '', + 'value' => '', + 'lexicon' => 'imageslim:default' + ), + array( + 'name' => 'q', + 'desc' => 'prop_is.q_desc', + 'type' => 'integer', + 'options' => '', + 'value' => '', + 'lexicon' => 'imageslim:default' + ), + array( + 'name' => 'debug', + 'desc' => 'prop_is.debug_desc', + 'type' => 'combo-boolean', + 'options' => '', + 'value' => '0', + 'lexicon' => 'imageslim:default' + ) ); return $properties; \ No newline at end of file diff --git a/_build/validators/preinstall.script.php b/_build/validators/preinstall.script.php index 97a483d..7d57e08 100644 --- a/_build/validators/preinstall.script.php +++ b/_build/validators/preinstall.script.php @@ -4,7 +4,6 @@ * * Copyright 2013 Jason Grant * @author Jason Grant - * 2/22/13 * * imageSlim is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -60,11 +59,11 @@ $modx->log(xPDO::LOG_LEVEL_ERROR,'imageSlim requires the PHP DOM extension [ http://www.php.net/manual/en/book.dom.php ]'); $success = false; } - if (ini_get('allow_url_fopen')) { - $modx->log(xPDO::LOG_LEVEL_INFO,'PHP: allow_url_fopen: yes | imageSlim can work with remote images'); + if (function_exists('curl_init')) { + $modx->log(xPDO::LOG_LEVEL_INFO,'cURL - OK'); } else { - $modx->log(xPDO::LOG_LEVEL_INFO,'PHP: allow_url_fopen: NO | Change this setting for imageSlim to work with remote images'); + $modx->log(xPDO::LOG_LEVEL_INFO,'cURL - NOT FOUND | imageSlim will skip any remote images'); } break; diff --git a/assets/components/imageslim/cache/index.html b/assets/components/imageslim/cache/index.html new file mode 100644 index 0000000..e69de29 diff --git a/assets/components/imageslim/index.html b/assets/components/imageslim/index.html new file mode 100644 index 0000000..e69de29 diff --git a/core/components/imageslim/docs/changelog.txt b/core/components/imageslim/docs/changelog.txt index ad2dd59..921c233 100644 --- a/core/components/imageslim/docs/changelog.txt +++ b/core/components/imageslim/docs/changelog.txt @@ -1,6 +1,11 @@ Changelog for imageSlim +imageSlim 1.0.0-pl +--------------------- +* uses cURL for remote images +* adds caching of remote images and a max download time threshold + imageSlim 1.0.0-beta1 ------------------------- +--------------------- Initial Version \ No newline at end of file diff --git a/core/components/imageslim/elements/snippets/imageslim.snippet.php b/core/components/imageslim/elements/snippets/imageslim.snippet.php index 0b4441a..93c514e 100644 --- a/core/components/imageslim/elements/snippets/imageslim.snippet.php +++ b/core/components/imageslim/elements/snippets/imageslim.snippet.php @@ -19,7 +19,7 @@ * * @package imageslim * @author Jason Grant - * @version 1.0.0-beta1 + * @version 1.0.0-pl */ /** @@ -37,15 +37,16 @@ * Properties * ---------- * - * @property scale - (float) - * @property conventThreshold - (float) - * @property maxWidth - (int) - * @property maxHeight - (int) - * @property phpthumbof - (string) - * @property fixAspect - (boolean) - * @property remoteImages - (boolean) - * @property q - (int) - * @property debug - (boolean) + * @property float scale + * @property float conventThreshold + * @property integer maxWidth + * @property integer maxHeight + * @property string phpthumbof + * @property boolean fixAspect + * @property boolean remoteImages + * @property integer remoteTimeout + * @property integer q + * @property boolean debug * * See the default properties for a description of each. * @@ -64,32 +65,32 @@ $maxWidth = isset($maxWidth) && $maxWidth !== '' ? (int) $maxWidth: 999999; $maxHeight = isset($maxHeight) && $maxHeight !== '' ? (int) $maxHeight: 999999; $phpthumbof = isset($phpthumbof) ? $phpthumbof : ''; -$fixAspect = isset($fixAspect) ? (boolean) $fixAspect : TRUE; -$remoteImages = isset($remoteImages) ? (boolean) $remoteImages : FALSE; -!empty($q) && $q = (int) $q; -$debug = isset($debug) ? (boolean) $debug : FALSE; - -$debug && $debugstr = "i m a g e S l i m [1.0.0-beta1]\nscale:$scale convertThreshold:" . ($convertThreshold ? $convertThreshold / 1024 . 'KB' : 'none') . "\nmaxWidth:$maxWidth maxHeight:$maxHeight q:$q\nfixAspect:$fixAspect phpthumbof:$phpthumbof\n"; -$debug && $debugstr .= "Remote images:$remoteImages allow_url_fopen:" . ini_get('allow_url_fopen') . "\n"; - -$remoteImages = $remoteImages && ini_get('allow_url_fopen'); // remote images won't work without this setting on -if ( $remoteImages && $modx->config['phpthumb_nohotlink_enabled'] ) { // if it's enabled, get a list of allowed domains - $remoteDomains = explode(',', $modx->config['phpthumb_nohotlink_valid_domains']); - $remoteDomainsCount = count($remoteDomains); - $debug && $debugstr .= "phpthumb_nohotlink: Enabled valid_domains:{$modx->config['phpthumb_nohotlink_valid_domains']}\n"; -} +$fixAspect = isset($fixAspect) ? (bool) $fixAspect : TRUE; +$remoteImages = isset($remoteImages) ? (bool) $remoteImages && function_exists('curl_init') : FALSE; +$remoteTimeout = isset($remoteTimeout) ? (int) $remoteTimeout : 5; +$q = empty($q) ? '' : (int) $q; +$debug = isset($debug) ? (bool) $debug : FALSE; + +$debug && $debugstr = "i m a g e S l i m [1.0.0-pl]\nscale:$scale convertThreshold:" . ($convertThreshold ? $convertThreshold / 1024 . 'KB' : 'none') . "\nmaxWidth:$maxWidth maxHeight:$maxHeight q:$q\nfixAspect:$fixAspect phpthumbof:$phpthumbof\nRemote images:$remoteImages Timeout:$remoteTimeout cURL:" . (!function_exists('curl_init') ? 'not ':'') . "installed\n"; +$cachePath = MODX_ASSETS_PATH . 'components/imageslim/cache/'; +$remoteDomains = FALSE; $dom = new DOMDocument; @$dom->loadHTML('' . $input); // load this mother up foreach ($dom->getElementsByTagName('img') as $node) { // for all our images $src = $node->getAttribute('src'); - $file = $size = $isRemote = FALSE; - if ( preg_match('/^(?:https?:)?\/\/(.+?)\//i', $src, $matches) ) { // if we've got a remote image to work with - $isRemote = $allowedDomain = TRUE; + $file = $size = FALSE; + if ( preg_match('/^(?:https?:)?\/\/(.+?)\/(.+)/i', $src, $matches) ) { // if we've got a remote image to work with + $allowedDomain = TRUE; $file = $src; if ( $remoteImages && $modx->config['phpthumb_nohotlink_enabled'] ) { // if nohotlink is enabled, make sure it's an allowed domain $allowedDomain = FALSE; // domains will only be allowed if they match one in phpthumb_nohotlink_valid_domains + if ($remoteDomains === FALSE) { // first time through get a list of allowed domains + $remoteDomains = explode(',', $modx->config['phpthumb_nohotlink_valid_domains']); + $remoteDomainsCount = count($remoteDomains); + $debug && $debugstr .= "\nphpthumb_nohotlink: Enabled valid_domains:{$modx->config['phpthumb_nohotlink_valid_domains']}\n"; + } for ($i=0; $i < $remoteDomainsCount && !$allowedDomain; ++$i) { $allowedDomain = preg_match("/{$remoteDomains[$i]}$/i", $matches[1]); } @@ -99,6 +100,34 @@ continue; } $debug && $debugstr .= "\nsrc:$src\nDomain:{$matches[1]} Allowed:$allowedDomain\n"; + $file = $cachePath . preg_replace("/[^\w\d\-_\.]/", '-', "{$matches[1]}-{$matches[2]}"); + if (!file_exists($file)) { // if it's not in our cache, go get it + $debug && $debugstr .= "Retrieving $src\nTarget filename: $file\n"; + $fh = fopen($file, 'wb'); + if (!$fh) { + $debug && $debugstr .= "*** Error *** Can't write to cache directory $cachePath\n"; + continue; + } + $curlFail = FALSE; + $ch = curl_init($src); + curl_setopt_array($ch, array( + CURLOPT_TIMEOUT => $remoteTimeout, + CURLOPT_FILE => $fh, + CURLOPT_FAILONERROR => TRUE + )); + curl_exec($ch); + if (curl_errno($ch)) { + $debug && $debugstr .= 'cURL error: ' . curl_error($ch) . " *** Skipping ***\n"; + $curlFail = TRUE; + } + curl_close($ch); + fclose($fh); + if ($curlFail) { // if we didn't get it, skip and don't cache + unlink($file); + continue; + } + } + elseif ($debug) { $debugstr .= "Retrieved from cache:$file\n";} } else { $file = MODX_BASE_PATH . rawurldecode(ltrim($src, '/')); // Fix spaces and other encoded characters in the URL @@ -124,11 +153,11 @@ $styles = array(); preg_match_all('/([\w-]+)\s*:\s*([^;]+)\s*;?/', $styleAttr, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $styles[$match[1]] = $match[2]; } // bust everything out into an array - if ( @$styles['width'] && stripos($styles['width'], 'px') ) { // if we have a width in pixels + if ( isset($styles['width']) && stripos($styles['width'], 'px') ) { // if we have a width in pixels preg_match('/\d+/', $styles['width'], $matches); $wCss = $matches[0]; // get just the value } - if ( @$styles['height'] && stripos($styles['height'], 'px') ) { // same deal for height + if ( isset($styles['height']) && stripos($styles['height'], 'px') ) { // same deal for height preg_match('/\d+/', $styles['height'], $matches); $hCss = $matches[0]; } @@ -183,7 +212,7 @@ $opts['w'] = $opts['h'] * $ar; } else { unset($opts['w']); } // forget about width, since height is our limiting dimension - $debug && $debugstr .= '++(H) ' . (@$opts['w'] ? $opts['w'] : '') . " realh:{$opts['h']}\n"; + $debug && $debugstr .= '++(H) ' . (isset($opts['w']) ? $opts['w'] : '') . " realh:{$opts['h']}\n"; if ($maxHeight && $h > $maxHeight) { $h = $maxHeight; $w = round($maxHeight * $ar); @@ -191,8 +220,8 @@ $debug && $debugstr .= "++ w:$w h:$h\n"; } } - @$opts['w'] && $opts['w'] = round($opts['w']); // round these to integers - @$opts['h'] && $opts['h'] = round($opts['h']); + if (isset($opts['w'])) { $opts['w'] = round($opts['w']); } // round these to integers + if (isset($opts['h'])) { $opts['h'] = round($opts['h']); } if ($adjustDisplaySize) { // ok, update our display size if we need do if ($wCss) { // if the width was in an inline style (and in px), use that @@ -209,13 +238,7 @@ } if ($convertThreshold !== FALSE && $type !== 'jpeg') { - if ($isRemote) { - $fsize = @array_change_key_case(@get_headers($file, 1), CASE_LOWER); - if ( strcasecmp($fsize[0], 'HTTP/1.1 200 OK') !== 0 ) { $fsize = $fsize['content-length'][1]; } - else { $fsize = $fsize['content-length']; } // the file size is in a different place if it's a redirect - } - else { $fsize = @filesize($file); } - + $fsize = filesize($file); if ($fsize > $convertThreshold) { // if we've got a non-jpeg that's too big, convert it to jpeg $opts['f'] = 'jpeg'; $debug && $debugstr .= "File size:$fsize Threshold exceeded; converting to jpeg.\n"; @@ -223,11 +246,11 @@ } if (!empty($opts)) { // have we anything to do for this lovely image? - $aspectNeedsFix && $opts['zc'] = 1; + if ($aspectNeedsFix) { $opts['zc'] = 1; } if ( !isset($opts['f']) ) { // if output file type isn't user specified... $opts['f'] = ($type === 'jpeg' ? 'jpeg' : 'png'); // if it's a gif or bmp let's just make it a png, shall we? } - $q && $opts['f'] === 'jpeg' && $opts['q'] = $q; // add user-specified jpeg quality if it's relevant + if ($q && $opts['f'] === 'jpeg') { $opts['q'] = $q; } // add user-specified jpeg quality if it's relevant $image = array(); $image['input'] = $file; $option_str = ''; @@ -235,7 +258,7 @@ if (is_array($v)) { // handle any array options like fltr[] foreach($v as $param) { $option_str .= $k . "[]=$param&"; } } - else { $option_str .= "$k=$v&";} + else { $option_str .= "$k=$v&"; } } $image['options'] = rtrim($option_str, '&'); $debug && $debugstr .= "phpthumbof options: {$image['options']}\n"; @@ -248,7 +271,6 @@ } } -if ($debug) echo "\n"; - -// Return the output, stripping off the tags and CR characters that DOM adds (?) -return str_replace(' ', '', substr($dom->saveXML($dom->getElementsByTagName('body')->item(0)), 6, -7) ); \ No newline at end of file +$output = str_replace(' ', '', substr($dom->saveXML($dom->getElementsByTagName('body')->item(0)), 6, -7) ); // strip off the tags and CR characters that DOM adds (?) +$debug && $output = "\n$output"; +return $output; \ No newline at end of file diff --git a/core/components/imageslim/lexicon/en/default.inc.php b/core/components/imageslim/lexicon/en/default.inc.php index d9aea26..fa6d5bb 100644 --- a/core/components/imageslim/lexicon/en/default.inc.php +++ b/core/components/imageslim/lexicon/en/default.inc.php @@ -33,6 +33,7 @@ $_lang['prop_is.maxWidth_desc'] = 'Maximum display width of an image.
Units: pixels'; $_lang['prop_is.phpthumbof_desc'] = 'An optional string of parameters to pass to phpThumbOf.
Be careful with this one though, since phpThumbOf will be run on every image in the input, not just the oversized ones.
Certain parameters—w, h, f, q, zc—may be overridden by imageSlim depending on the image and other settings.'; $_lang['prop_is.q_desc'] = 'JPEG quality: 1 (worst) – 95 (best)
Default: 75'; -$_lang['prop_is.remoteImages_desc'] = 'Allow imageSlim to work with images from other servers.
Requires proper settings for allow_url_fopen in PHP and phpthumb_nohotlink_enabled and phpthumb_nohotlink_valid_domains in the MODX system settings (core > phpthumb)
Remote images take longer to process than local ones, obviously.'; +$_lang['prop_is.remoteImages_desc'] = 'Allow imageSlim to work with images from other servers.
Requires cURL and proper settings for phpthumb_nohotlink_enabled and phpthumb_nohotlink_valid_domains in the MODX system settings (core > phpthumb)
Remote images take longer to process than local ones, obviously.'; +$_lang['prop_is.remoteTimeout_desc'] = 'Maximum amount of time to allow for a remote image download.
Units: seconds
Default: 5'; $_lang['prop_is.scale_desc'] = 'Allow the natural size of the image to exceed its display size by this factor.
Use a value between 1.5 and 2 for retina displays. A scale of 1 will keep the image’s natural size the same as its display size.'; $_lang['prop_is.debug_desc'] = 'Output debug info in an HTML comment'; \ No newline at end of file