diff --git a/amd/build/appear.min.js b/amd/build/appear.min.js index f24ffaf..b73bf1f 100644 --- a/amd/build/appear.min.js +++ b/amd/build/appear.min.js @@ -1 +1 @@ -define(["jquery"],function(a){return function(a){function b(b){return a(b).filter(function(){return a(this).is(":appeared")})}function c(){g=!1;for(var a=0,c=e.length;a=e&&h-k<=e+j.height()&&g+c.width()+l>=d&&g-l<=d+j.width()},a.fn.extend({appear:function(b){i=a.extend({},h,b||{});var e=this.selector||this;if(!f){var j=function(){g||(g=!0,setTimeout(c,i.interval))};a(window).scroll(j).resize(j),f=!0}return i.force_process&&setTimeout(c,i.interval),d(e),a(e)}}),a.extend({force_appear:function(){return!!f&&(c(),!0)}})}(function(){return"undefined"!=typeof module?require("jquery"):a}()),a}); \ No newline at end of file +define(["jquery"],function(a){var b=a;return b(),function(a){function b(b){return a(b).filter(function(){return a(this).is(":appeared")})}function c(){g=!1;for(var a=0,c=e.length;a=e&&h-k<=e+j.height()&&g+c.width()+l>=d&&g-l<=d+j.width()},a.fn.extend({appear:function(b){i=a.extend({},h,b||{});var e=this.selector||this;if(!f){var j=function(){g||(g=!0,setTimeout(c,i.interval))};a(window).scroll(j).resize(j),f=!0}return i.force_process&&setTimeout(c,i.interval),d(e),a(e)}}),a.extend({force_appear:function(){return!!f&&(c(),!0)}})}(function(){return"undefined"!=typeof module?require("jquery"):a}()),a}); \ No newline at end of file diff --git a/amd/build/imageopt.min.js b/amd/build/imageopt.min.js index 923c4e9..cf3c342 100644 --- a/amd/build/imageopt.min.js +++ b/amd/build/imageopt.min.js @@ -1 +1 @@ -define(["filter_imageopt/appear"],function(a){return{init:function(){a(document).ready(function(){a(document.body).on("appear","img[data-loadonvisible]",function(b,c){c.each(function(){var b=a(this).data("loadonvisible");a(this).attr("src",b),a(this).removeAttr("data-loadonvisible"),a(this).addClass("imageopt_loading"),a(this).on("load",function(){a(this).removeClass("imageopt_loading")})})});var b={appeartopoffset:100,appearleftoffset:100};a("img[data-loadonvisible]").appear(b),a.force_appear(),function(){var b=location.hash;a(window).on("popstate hashchange",function(){var c=location.hash;c!==b&&window.setTimeout(function(){a.force_appear()},200),b=c})}()})}}}); \ No newline at end of file +define(["filter_imageopt/appear"],function(a){return{init:function(){var b=function(b,c){a(b).attr("src",c),a(b).removeAttr("data-loadonvisible"),a(b).addClass("imageopt_loading"),a(b).on("load",function(){a(b).removeClass("imageopt_loading")})};a(document).ready(function(){a(document.body).on("appear","img[data-loadonvisible]",function(c,d){d.each(function(){var c=a(this).data("loadonvisible");b(this,c)})});var c={appeartopoffset:100,appearleftoffset:100};a("img[data-loadonvisible]").appear(c),a.force_appear(),function(){var b=location.hash;a(window).on("popstate hashchange",function(){var c=location.hash;c!==b&&window.setTimeout(function(){a.force_appear()},200),b=c})}()})}}}); \ No newline at end of file diff --git a/amd/src/appear.js b/amd/src/appear.js index dcbaf91..16f912f 100644 --- a/amd/src/appear.js +++ b/amd/src/appear.js @@ -2,6 +2,7 @@ define(['jquery'], function(jQuery) { var $ = jQuery; + $(); // This is here to stop linter moaning about unuse. /* * jQuery appear plugin diff --git a/amd/src/imageopt.js b/amd/src/imageopt.js index 0dcb896..241a34c 100644 --- a/amd/src/imageopt.js +++ b/amd/src/imageopt.js @@ -27,16 +27,27 @@ define(['filter_imageopt/appear'], function($) { return { init:function() { + + /** + * Load optimised image. + * @param {Element} el + * @param {string} imgurl + * @returns {void} + */ + var loadOptimisedImg = function(el, imgurl) { + $(el).attr('src', imgurl); + $(el).removeAttr('data-loadonvisible'); + $(el).addClass('imageopt_loading'); + $(el).on('load', function() { + $(el).removeClass('imageopt_loading'); + }); + }; + $(document).ready(function() { $(document.body).on('appear', 'img[data-loadonvisible]', function(e, appeared) { appeared.each(function() { var imgurl = $(this).data('loadonvisible'); - $(this).attr('src', imgurl); - $(this).removeAttr('data-loadonvisible'); - $(this).addClass('imageopt_loading'); - $(this).on('load', function() { - $(this).removeClass('imageopt_loading'); - }); + loadOptimisedImg(this, imgurl); }); }); // Appear configuration - start loading images when they are out of the view port by 400px. diff --git a/classes/image.php b/classes/image.php index 187cc93..ea3f24b 100644 --- a/classes/image.php +++ b/classes/image.php @@ -58,18 +58,25 @@ class image { * Creates a resized version of image and stores copy in file area * * @param stored_file $originalfile + * @param string $resizefilepath + * @param bool | string $resizefilename * @param int $newwidth; * @param int $newheight; * @return stored_file */ public static function resize ( stored_file $originalfile, + $resizefilepath, $resizefilename = false, $newwidth = false, $newheight = false, $jpgquality = 90 ) { + if (substr($resizefilepath, -1) !== '/') { + $resizefilepath .= '/'; + } + if ($resizefilename === false) { $resizefilename = $originalfile->get_filename(); } @@ -91,7 +98,7 @@ public static function resize ( // Create temporary image for processing. $tmpimage = tempnam(sys_get_temp_dir(), 'tmpimg'); - \file_put_contents($tmpimage, $originalfile->get_content()); + file_put_contents($tmpimage, $originalfile->get_content()); if (!$newheight) { $m = $imageinfo->height / $imageinfo->width; // Multiplier to work out $newheight. @@ -103,47 +110,47 @@ public static function resize ( $t = null; switch ($imageinfo->mimetype) { case 'image/gif': - if (\function_exists('imagecreatefromgif')) { - $im = \imagecreatefromgif($tmpimage); + if (function_exists('imagecreatefromgif')) { + $im = imagecreatefromgif($tmpimage); } else { - \debugging('GIF not supported on this server'); + debugging('GIF not supported on this server'); unlink ($tmpimage); return false; } // Guess transparent colour from GIF. - $transparent = \imagecolortransparent($im); + $transparent = imagecolortransparent($im); if ($transparent != -1) { - $t = \imagecolorsforindex($im, $transparent); + $t = imagecolorsforindex($im, $transparent); } break; case 'image/jpeg': - if (\function_exists('imagecreatefromjpeg')) { - $im = \imagecreatefromjpeg($tmpimage); + if (function_exists('imagecreatefromjpeg')) { + $im = imagecreatefromjpeg($tmpimage); } else { - \debugging('JPEG not supported on this server'); + debugging('JPEG not supported on this server'); unlink ($tmpimage); return false; } // If the user uploads a jpeg them we should process as a jpeg if possible. - if (\function_exists('imagejpeg')) { + if (function_exists('imagejpeg')) { $imagefnc = 'imagejpeg'; $filters = null; // Not used. $quality = $jpgquality; - } else if (\function_exists('imagepng')) { + } else if (function_exists('imagepng')) { $imagefnc = 'imagepng'; $filters = PNG_NO_FILTER; $quality = 1; } else { - \debugging('Jpeg and png not supported on this server, please fix server configuration'); + debugging('Jpeg and png not supported on this server, please fix server configuration'); unlink ($tmpimage); return false; } break; case 'image/png': - if (\function_exists('imagecreatefrompng')) { - $im = \imagecreatefrompng($tmpimage); + if (function_exists('imagecreatefrompng')) { + $im = imagecreatefrompng($tmpimage); } else { - \debugging('PNG not supported on this server'); + debugging('PNG not supported on this server'); unlink ($tmpimage); return false; } @@ -156,59 +163,70 @@ public static function resize ( // The default for all images other than jpegs is to try imagepng first. if (empty($imagefnc)) { - if (\function_exists('imagepng')) { + if (function_exists('imagepng')) { $imagefnc = 'imagepng'; $filters = PNG_NO_FILTER; $quality = 1; - } else if (\function_exists('imagejpeg')) { + } else if (function_exists('imagejpeg')) { $imagefnc = 'imagejpeg'; $filters = null; // Not used. $quality = $jpgquality; } else { - \debugging('Jpeg and png not supported on this server, please fix server configuration'); + debugging('Jpeg and png not supported on this server, please fix server configuration'); return false; } } - if (\function_exists('imagecreatetruecolor')) { - $newimage = \imagecreatetruecolor($newwidth, $newheight); + if (function_exists('imagecreatetruecolor')) { + $newimage = imagecreatetruecolor($newwidth, $newheight); if ($imageinfo->mimetype != 'image/jpeg' and $imagefnc === 'imagepng') { if ($t) { // Transparent GIF hacking... - $transparentcolour = \imagecolorallocate($newimage , $t['red'] , $t['green'] , $t['blue']); - \imagecolortransparent($newimage , $transparentcolour); + $transparentcolour = imagecolorallocate($newimage , $t['red'] , $t['green'] , $t['blue']); + imagecolortransparent($newimage , $transparentcolour); } - \imagealphablending($newimage, false); - $color = \imagecolorallocatealpha($newimage, 0, 0, 0, 127); - \imagefill($newimage, 0, 0, $color); - \imagesavealpha($newimage, true); + imagealphablending($newimage, false); + $color = imagecolorallocatealpha($newimage, 0, 0, 0, 127); + imagefill($newimage, 0, 0, $color); + imagesavealpha($newimage, true); } } else { - $newimage = \imagecreate($newwidth, $newheight); + $newimage = imagecreate($newwidth, $newheight); } - \imagecopybicubic($newimage, $im, 0, 0, 0, 0, $newwidth, $newheight, $imageinfo->width, $imageinfo->height); + imagecopybicubic($newimage, $im, 0, 0, 0, 0, $newwidth, $newheight, $imageinfo->width, $imageinfo->height); - $fs = \get_file_storage(); + $fs = get_file_storage(); $newimageparams = array( 'contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid, - 'filepath' => '/' + 'filepath' => $resizefilepath ); - \ob_start(); + $dirs = explode('/', $resizefilepath); + $dirpath = '/'; + + foreach ($dirs as $dir) { + if (empty($dir)) { + continue; + } + $dirpath .= $dir.'/'; + $fs->create_directory($contextid, $component, $filearea, $itemid, $dirpath); + } + + ob_start(); if (!$imagefnc($newimage, null, $quality, $filters)) { return false; } - $data = \ob_get_clean(); - \imagedestroy($newimage); + $data = ob_get_clean(); + imagedestroy($newimage); $newimageparams['filename'] = $resizefilename; - if ($resizefilename == $originalfile->get_filename()) { + if ($resizefilename === $originalfile->get_filename() && $resizefilepath === $originalfile->get_filepath()) { $originalfile->delete(); } $file1 = $fs->create_file_from_string($newimageparams, $data); diff --git a/classes/local.php b/classes/local.php new file mode 100644 index 0000000..c8569c0 --- /dev/null +++ b/classes/local.php @@ -0,0 +1,93 @@ +. + +/** + * Local class for local people (we'll have no trouble here). + * + * @package filter_imageopt + * @author Guy Thomas + * @copyright Copyright (c) 2017 Guy Thomas. + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace filter_imageopt; + +defined('MOODLE_INTERNAL') || die(); + +class local { + + const REGEXP_IMGSRC = '/]*(src=["|\']((?:.*)(pluginfile.php(?:.*)))["|\'])(?:.*)>/isU'; + + const REGEXP_SRC = '/(?:.*)(pluginfile.php(?:.*))/'; + /** + * Get the optimised path for a file path. + * @param type $filepath + * @return type + */ + public static function get_optimised_path($filepath) { + $maxwidth = get_config('filter_imageopt', 'maxwidth'); + + $tmparr = explode('/', $filepath); + if ($tmparr[0] === 'pluginfile.php') { + array_shift($tmparr); + } + + $tmparr[count($tmparr)-1] = 'imageopt/'.$maxwidth.'/'.$tmparr[count($tmparr)-1]; + $optimisedpath = implode('/', $tmparr); + if (substr($optimisedpath, 0, 1) !== '/') { + $optimisedpath = '/'.$optimisedpath; + } + return $optimisedpath; + } + + public static function get_img_path_from_src($src) { + $matches = []; + + preg_match(self::REGEXP_SRC, $src, $matches); + + return $matches[1]; + } + + /** + * Get's an image file from the plugin file path. + * + * @param str $pluginfilepath pluginfile.php/ + * @return \stored_file + */ + public static function get_img_file($pluginfilepath) { + $fs = get_file_storage(); + + if (strpos($pluginfilepath, 'pluginfile.php') === 0) { + $pluginfilepath = substr($pluginfilepath, strlen('pluginfile.php')); + } + + $tmparr = explode('/', $pluginfilepath); + + for ($t = 4; $t < count($tmparr); $t++) { + $tmparr[$t] = urldecode($tmparr[$t]); + } + + // If no item id then put one in. + if (!is_number($tmparr[4])) { + $tmparr[4] = '0/'.$tmparr[4]; + } + + $path = implode('/', $tmparr); + + $file = $fs->get_file_by_hash(sha1($path)); + + return $file; + } +} \ No newline at end of file diff --git a/classes/test_util.php b/classes/test_util.php deleted file mode 100644 index a3998e9..0000000 --- a/classes/test_util.php +++ /dev/null @@ -1,51 +0,0 @@ -. - -/** - * Testing utility. - * Allows for private / protected properties to be called against an object for php unit testing purposes. - * @package filter_imageopt - * @author Guy Thomas - * @copyright Copyright (c) 2017 Blackboard Inc. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace filter_imageopt; - -defined('MOODLE_INTERNAL') || die(); - -/** - * Testing utility class. - * @package filter_imageopt - * @author Guy Thomas - * @copyright Copyright (c) 2017 Blackboard Inc. - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class test_util { - - /** - * Call a private / protected method against an object. - * @param string|mixed $object - * @param string $method - * @param array $args - * @return mixed - */ - public static function call_restricted_method($object, $method, array $args) { - $method = new \ReflectionMethod($object, $method); - $method->setAccessible(true); - return $method->invokeArgs($object, $args); - } -} diff --git a/filter.php b/filter.php index 2df7e7f..b26e16c 100644 --- a/filter.php +++ b/filter.php @@ -17,7 +17,7 @@ /** * Image optimiser * @package filter_imageopt - * @author Guy Thomas + * @author Guy Thomas * @copyright Copyright (c) Guy Thomas. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -25,6 +25,7 @@ defined('MOODLE_INTERNAL') || die(); use filter_imageopt\image; +use filter_imageopt\local; /** * Image optimiser - main filter class. @@ -40,11 +41,6 @@ class filter_imageopt extends moodle_text_filter { */ private $config; - /** - * Regex to extract and process img. - */ - const REGEXP_IMGSRC = '/]*(src=["|\']((?:.*)(pluginfile.php(?:.*)))["|\'])(?:.*)>/isU'; - public function __construct(context $context, array $localconfig) { global $CFG; @@ -59,33 +55,6 @@ public function __construct(context $context, array $localconfig) { parent::__construct($context, $localconfig); } - /** - * Get's an image file from the plugin file path. - * - * @param str $pluginfilepath pluginfile.php/ - * @return bool|stored_file - */ - private function get_img_file($pluginfilepath) { - $tmparr = explode('/', $pluginfilepath); - - $contextid = urldecode($tmparr[1]); - $component = urldecode($tmparr[2]); - - if (count($tmparr) === 5) { - $area = urldecode($tmparr[3]); - $item = 0; - $filename = urldecode($tmparr[4]); - } else if (count($tmparr) === 6) { - $area = urldecode($tmparr[3]); - $item = urldecode($tmparr[4]); - $filename = urldecode($tmparr[5]); - } - - $fs = get_file_storage(); - $file = $fs->get_file($contextid, $component, $area, $item, '/', $filename); - return $file; - } - private function empty_image($width, $height) { // @codingStandardsIgnoreStart $svg = <<config->maxwidth; - $filename = $file->get_filename(); - $contextid = $file->get_contextid(); - $component = $file->get_component(); - $area = $file->get_filearea(); - $item = $file->get_itemid(); - return new moodle_url( - $CFG->wwwroot.'/pluginfile.php/'.$contextid.'/filter_imageopt/'.$area.'/'.$item.'/'.$component.'/'.$maxwidth. - '/'.$filename - ); - } - /** * Add width and height to img tag and return modified tag with width and height * @param string $img @@ -171,12 +121,76 @@ private function img_add_width_height($img, $width, $height) { return $img; } + /** + * Create image optimiser url - will take an original file and resize it then forward on. + * @param stored_file $file + * @param string $originalsrc + * @return moodle_url + */ + public function imageopturl(stored_file $file, $originalsrc) { + global $CFG; + $maxwidth = $this->config->maxwidth; + $filename = $file->get_filename(); + $contextid = $file->get_contextid(); + + $url = $CFG->wwwroot.'/pluginfile.php/'.$contextid.'/filter_imageopt/'.$maxwidth.'/'. + base64_encode($originalsrc).'/'.$filename; + + return new moodle_url($url); + } + + private function process_img_tag(array $match) { + global $CFG; + + $fs = get_file_storage(); + + $maxwidth = $this->config->maxwidth; + + $optimisedavailable = false; + + // Don't process images that aren't in this site. + if (stripos($match[2], $CFG->wwwroot) === false) { + return $match[0]; + } + + $file = local::get_img_file($match[3]); + + if (!$file) { + return $match[0]; + } + + if (stripos($match[3], 'imageopt/'.$maxwidth.'/') === false) { + $optimisedpath = local::get_optimised_path($match[3]); + $pathnamehash = sha1($optimisedpath); + $optimisedavailable = $fs->file_exists_by_hash($pathnamehash); + } + + $originalsrc = $match[2]; + + if ($optimisedavailable) { + $optimisedsrc = new moodle_url('pluginfile.php'.$optimisedpath); + $optimisedsrc = $optimisedsrc->out(); + } else { + $optimisedsrc = $this->imageopturl($file, $originalsrc); + } + + if (empty($this->config->loadonvisible) || $this->config->loadonvisible < 999) { + return $this->apply_loadonvisible($match, $file, $originalsrc, $optimisedsrc, $optimisedavailable); + } else { + return $this->apply_img_tag($match, $file, $originalsrc, $optimisedsrc); + } + } + /** * Place hold images so that they are loaded when visible. * @param array $match (0 - full img tag, 1 src tag and contents, 2 - contents of src, 3 - pluginfile.php/) + * @param stored_file $file + * @param string $originalsrc + * @param string $optimisedsrc + * @param bool $optimisedavailable * @return string */ - private function apply_loadonvisible(array $match) { + private function apply_loadonvisible(array $match, stored_file $file, $originalsrc, $optimisedsrc, $optimisedavailable = false) { global $PAGE; static $jsloaded = false; @@ -186,7 +200,7 @@ private function apply_loadonvisible(array $match) { // This is so we can make the first couple of images load immediately without placeholding. if ($imgcount <= $this->config->loadonvisible) { - return $this->process_image_tag($match); + return $this->apply_img_tag($match, $file, $originalsrc, $optimisedsrc); } if (!$jsloaded) { @@ -198,14 +212,13 @@ private function apply_loadonvisible(array $match) { // Full image tag + attributes, etc. $img = $match[0]; + // If this text already has load on visible applied then just return it. if (stripos('data-loadonvisible', $match[0]) !== false) { return ($img); } $maxwidth = $this->config->maxwidth; - $file = $this->get_img_file($match[3]); - if (!$file) { return $img; } @@ -221,11 +234,12 @@ private function apply_loadonvisible(array $match) { if (!$file) { $loadonvisible = $match[2]; } else { - - $loadonvisible = $this->imageopturl($file); + $loadonvisible = $optimisedsrc; } - $img = str_ireplace('get_img_file($match[3]); + $file = local::get_img_file($match[3]); if (!$file) { return $match[0]; } @@ -264,11 +281,15 @@ private function process_image_tag($match) { return $match[0]; } - $newsrc = $this->imageopturl($file); + $newsrc = $optimisedsrc; $img = $this->img_add_width_height($match[0], $width, $height); - return str_replace($match[2], $newsrc, $img); + $img = str_replace($match[2], $newsrc, $img); + + $img = str_ireplace('config->loadonvisible) || $this->config->loadonvisible < 999) { - $search = self::REGEXP_IMGSRC; - $filtered = preg_replace_callback($search, 'self::apply_loadonvisible', $filtered); - } else { - $search = self::REGEXP_IMGSRC; - $filtered = preg_replace_callback($search, 'self::process_image_tag', $filtered); - } + $search = local::REGEXP_IMGSRC; + $filtered = preg_replace_callback($search, 'self::process_img_tag', $filtered); if (empty($filtered)) { return $text; diff --git a/lang/en/filter_imageopt.php b/lang/en/filter_imageopt.php index a77b0c6..4bf8d97 100644 --- a/lang/en/filter_imageopt.php +++ b/lang/en/filter_imageopt.php @@ -16,7 +16,7 @@ /** * Image optimiser filter. - * @author Guy Thomas + * @author Guy Thomas * @copyright Copyright (c) Guy Thomas. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/lib.php b/lib.php index a272a52..eac25b0 100644 --- a/lib.php +++ b/lib.php @@ -23,6 +23,7 @@ */ use filter_imageopt\image; +use filter_imageopt\local; defined('MOODLE_INTERNAL') || die(); @@ -39,43 +40,60 @@ * @return bool */ function filter_imageopt_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) { - if (count($args) < 2) { - throw new coding_exception('Bad image url, args should contain item id and original component'); - } - $item = $args[0]; - $component = $args[1]; - $maxwidth = $args[2]; - $filename = $args[3]; - $pathinfo = pathinfo($filename); + $src = base64_decode(clean_param($args[0], PARAM_ALPHANUMEXT)); // PARAM_BASE64 did not work for me. - $resizename = $pathinfo['filename'].'_opt_'.$maxwidth.'.'.$pathinfo['extension']; + $imgpath = local::get_img_path_from_src($src); + $optimisedpath = local::get_optimised_path($imgpath); + $optimisedurl = new moodle_url($CFG->wwwroot.str_replace('//' ,'/', '/pluginfile.php/'.$optimisedpath)); $fs = get_file_storage(); - $file = $fs->get_file($context->id, $component, $filearea, $item, '/', $filename); - $originalts = $file->get_timemodified(); + $optimisedfile = local::get_img_file($optimisedpath); + + if ($optimisedfile) { + redirect($optimisedurl); + die; + } + + $originalfile = local::get_img_file($imgpath); + + $regex = '/imageopt\/(\d*)/'; + $matches = []; + preg_match($regex, $optimisedpath, $matches); + $maxwidth = ($matches[1]); + $item = $originalfile->get_itemid(); + $component = $originalfile->get_component(); + $filename = $originalfile->get_filename(); + $filearea = $originalfile->get_filearea(); + $pathinfo = pathinfo($filename); + + $originalts = $originalfile->get_timemodified(); - $imageinfo = (object) $file->get_imageinfo(); + $imageinfo = (object) $originalfile->get_imageinfo(); if ($imageinfo->width <= $maxwidth) { - send_stored_file($file, null, 0, false); - return true; + redirect($src); + die; } - $resizedfile = $fs->get_file($context->id, $component, $filearea, $item, '/', $resizename); // Make sure resized file is fresh. - if ($resizedfile && ($resizedfile->get_timemodified() < $originalts)) { - $resizedfile->delete(); - $resizedfile = false; + if ($optimisedfile && ($optimisedfile->get_timemodified() < $originalts)) { + $optimisedfile->delete(); + $optimisedfile = false; } - if (!$resizedfile) { - $resizedfile = image::resize($file, $resizename, $maxwidth); + if (!$optimisedfile) { + $spos = strripos($optimisedpath, '/'.$filename); + $optimisedpathonly = substr($optimisedpath, 0, $spos); + $spos = stripos($optimisedpathonly, '/'.$context->id.'/'.$component.'/'.$filearea); + $spos += strlen('/'.$context->id.'/'.$component.'/'.$filearea); + $optimisedpathonly = substr($optimisedpathonly, $spos); + $optimisedfile = image::resize($originalfile, $optimisedpathonly, $filename, $maxwidth); } - if (!$resizedfile) { - send_stored_file($file, null, 0, false); - return true; + if (!$optimisedfile) { + redirect($src); + die; } else { - send_stored_file($resizedfile, null, 0, false); - return true; + redirect($optimisedurl); + die; } } diff --git a/settings.php b/settings.php index b2025dd..fe9dcd8 100644 --- a/settings.php +++ b/settings.php @@ -16,7 +16,7 @@ /** * Image optimiser settings. - * @author Guy Thomas + * @author Guy Thomas * @copyright Copyright (c) 2017 Guy Thomas. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/tests/filter_test.php b/tests/filter_test.php index 0440f23..b4d7abb 100644 --- a/tests/filter_test.php +++ b/tests/filter_test.php @@ -17,14 +17,14 @@ /** * Tests for filter class * @package filter_imageopt - * @author Guy Thomas + * @author Guy Thomas * @copyright Guy Thomas 2017. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); -use filter_imageopt\test_util; +use filter_imageopt\local; global $CFG; @@ -34,7 +34,7 @@ /** * Tests for filter class * @package filter_imageopt - * @author Guy Thomas + * @author Guy Thomas * @copyright Guy Thomas 2017. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -44,7 +44,7 @@ class filter_imageopt_filter_testcase extends advanced_testcase { * Test regex works with sample img tag + pluginfile.php src. */ public function test_regex() { - $regex = filter_imageopt::REGEXP_IMGSRC; + $regex = local::REGEXP_IMGSRC; $matches = []; $img = 'assertContains('width="'.$size[0].'" height="'.$size[1].'"', $emptyimage); } } @@ -109,11 +109,12 @@ public function test_imageopturl() { $file = $this->std_file_record($context, $fixturefile); $filter = new filter_imageopt($context, []); - $url = test_util::call_restricted_method($filter, 'imageopturl', [$file]); + $originalurl = 'http://somesite/pluginfile.php/somefile.jpg'; + + $url = phpunit_util::call_internal_method($filter, 'imageopturl', [$file, $originalurl], get_class($filter)); $expected = new moodle_url( - $CFG->wwwroot.'/pluginfile.php/'.$context->id.'/filter_imageopt/'.$file->get_filearea().'/'. - $file->get_itemid().'/'.$file->get_component().'/480'. + $CFG->wwwroot.'/pluginfile.php/'.$context->id.'/filter_imageopt/480'.'/'.base64_encode($originalurl). '/'.$file->get_filename()); $this->assertEquals($expected, $url); } @@ -133,14 +134,14 @@ public function test_img_add_width_height() { $filter = new filter_imageopt($context, []); $img = ''; - $img = test_util::call_restricted_method($filter, 'img_add_width_height', [$img, 800, 400]); + $img = phpunit_util::call_internal_method($filter, 'img_add_width_height', [$img, 800, 400], get_class($filter)); $expected = ''; $this->assertEquals($expected, $img); $img = ''; $expected = ''; - $img = test_util::call_restricted_method($filter, 'img_add_width_height', [$img, 800, 400]); + $img = phpunit_util::call_internal_method($filter, 'img_add_width_height', [$img, 800, 400], get_class($filter)); $this->assertEquals($expected, $img); } @@ -196,7 +197,7 @@ public function test_get_img_file() { $filepath = 'pluginfile.php/'.$context->id.'/mod_label/intro/'.$fixturefile; /** @var stored_file $imgfile */ - $imgfile = test_util::call_restricted_method($filter, 'get_img_file', [$filepath]); + $imgfile = phpunit_util::call_internal_method(null, 'get_img_file', [$filepath], 'filter_imageopt\local'); $this->assertNotEmpty($imgfile); $this->assertEquals($fixturefile, $imgfile->get_filename()); } @@ -224,7 +225,7 @@ private function create_image_file_text($fixturefile) { $labeltxt = file_rewrite_pluginfile_urls($label->intro, 'pluginfile.php', $context->id, $file->get_component(), $file->get_filearea(), 0); $matches = []; - $regex = filter_imageopt::REGEXP_IMGSRC; + $regex = local::REGEXP_IMGSRC; preg_match($regex, $labeltxt, $matches); return [$labeltxt, $matches, $file]; } @@ -251,17 +252,24 @@ public function test_apply_loadonvisible() { /** @var stored_file $file */ $file = $file; - $regex = filter_imageopt::REGEXP_IMGSRC; + $filter = new filter_imageopt(context_helper::instance_by_id($file->get_contextid()), []); + + $regex = local::REGEXP_IMGSRC; preg_match($regex, $labeltxt, $matches); - $filter = new filter_imageopt(context_helper::instance_by_id($file->get_contextid()), []); - $str = test_util::call_restricted_method($filter, 'apply_loadonvisible', [$matches]); + $originalsrc = $matches[2]; + + $optimisedsrc = $filter->imageopturl($file, $originalsrc); + + $str = phpunit_util::call_internal_method($filter, 'apply_loadonvisible', [$matches, $file, $originalsrc, $optimisedsrc], + get_class($filter)); $loadonvisibleurl = $CFG->wwwroot.'/pluginfile.php/'.$file->get_contextid().'/filter_imageopt/'. - $file->get_filearea().'/0/'.$file->get_component().'/'.$maxwidth.'/'.$fixturefile; + $maxwidth.'/~base64url~/'.$fixturefile; // Test filter plugin img, lazy load. - $this->assertContains('assertRegExp($regex, $str); $this->assertContains('src="data:image/svg+xml;utf8,', $str); } @@ -275,8 +283,8 @@ public function test_apply_loadonvisible() { private function filter_imageopt_url_from_file(stored_file $file, $maxwidth) { global $CFG; - $url = $CFG->wwwroot.'/pluginfile.php/'.$file->get_contextid().'/filter_imageopt/'.$file->get_filearea().'/'. - $file->get_itemid().'/'.$file->get_component().'/'.$maxwidth.'/'.$file->get_filename(); + $url = $CFG->wwwroot.'/pluginfile.php/'.$file->get_contextid().'/filter_imageopt/'.$maxwidth. + '/~base64url~/'.$file->get_filename(); return $url; } @@ -285,7 +293,7 @@ private function filter_imageopt_url_from_file(stored_file $file, $maxwidth) { * Test processing image src. * @throws coding_exception */ - public function test_process_image_tag() { + public function test_apply_img_tag() { $this->resetAfterTest(); $this->setAdminUser(); @@ -302,9 +310,15 @@ public function test_process_image_tag() { $filter = new filter_imageopt($context, []); - $processed = test_util::call_restricted_method($filter, 'process_image_tag', [$matches]); + $originalsrc = $matches[2]; + $optimisedsrc = $filter->imageopturl($file, $originalsrc); + + $processed = phpunit_util::call_internal_method($filter, 'apply_img_tag', [$matches, $file, $originalsrc, $optimisedsrc], + get_class($filter)); + $postfilterurl = $this->filter_imageopt_url_from_file($file, $maxwidth); - $this->assertContains('src="'.$postfilterurl, $processed); + $regex = '/'.str_replace('~base64url~', '(?:[A-z|0-9|=]*)', preg_quote($postfilterurl, '/')).'/'; + $this->assertRegExp($regex, $processed); } @@ -335,8 +349,11 @@ public function test_filter() { $prefilterurl = $CFG->wwwroot.'/pluginfile.php/'.$context->id.'/mod_label/intro/0/testpng_2880x1800.png'; $this->assertContains($prefilterurl, $labeltxt); $postfilterurl = $this->filter_imageopt_url_from_file($file, $maxwidth); - $this->assertContains('src="'.$postfilterurl, $filtered); - $this->assertNotContains('src="'.$prefilterurl, $filtered); + $regex = '/src="'.str_replace('~base64url~', '(?:[A-z|0-9|=]*)', preg_quote($postfilterurl, '/')).'/'; + $this->assertRegExp($regex, $filtered); + + // We need a space before src so it doesn't trigger on original-src. + $this->assertNotContains(' src="'.$prefilterurl, $filtered); $this->assertNotContains('data-loadonvisible="'.$postfilterurl, $filtered); $this->assertNotContains('data-loadonvisible="'.$prefilterurl, $filtered); @@ -347,9 +364,12 @@ public function test_filter() { $prefilterurl = $CFG->wwwroot.'/pluginfile.php/'.$context->id.'/mod_label/intro/0/testpng_2880x1800.png'; $this->assertContains($prefilterurl, $labeltxt); $postfilterurl = $this->filter_imageopt_url_from_file($file, $maxwidth); - $this->assertContains('data-loadonvisible="'.$postfilterurl, $filtered); + + $regex = '/data-loadonvisible="'.str_replace('~base64url~', '(?:[A-z|0-9|=]*)', preg_quote($postfilterurl, '/')).'/'; + $this->assertRegExp($regex, $filtered); + $this->assertNotContains('data-loadonvisible="'.$prefilterurl, $filtered); $this->assertNotContains('src="'.$postfilterurl, $filtered); $this->assertNotContains('src="'.$prefilterurl, $filtered); } -} +} \ No newline at end of file diff --git a/version.php b/version.php index 8893146..dd9cc03 100644 --- a/version.php +++ b/version.php @@ -16,13 +16,13 @@ /** * Image Optimiser - * @author Guy Thomas + * @author Guy Thomas * @copyright Copyright (c) 2016 Guy Thomas. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2017110300; +$plugin->version = 2017110301; $plugin->requires = 2011111500; $plugin->component = 'filter_imageopt'; $plugin->maturity = MATURITY_BETA;