├── .gitignore ├── LICENCE.md ├── Plugin.php ├── README.md ├── assets └── default-not-found.jpg ├── classes └── Image.php ├── composer.json ├── lang ├── de │ └── lang.php ├── en │ └── lang.php ├── fr │ └── lang.php ├── hu │ └── lang.php └── zh-cn │ └── lang.php ├── models ├── Settings.php └── settings │ ├── _tinypng_hint.htm │ ├── _tinypng_stats.htm │ └── fields.yaml └── updates └── version.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.lock 3 | .DS_Store -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | # MIT license 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Plugin.php: -------------------------------------------------------------------------------- 1 | 'toughdeveloper.imageresizer::lang.plugin.name', 21 | 'description' => 'toughdeveloper.imageresizer::lang.plugin.description', 22 | 'author' => 'Tough Developer', 23 | 'icon' => 'icon-picture-o', 24 | 'homepage' => 'https://github.com/toughdeveloper/oc-imageresizer-plugin' 25 | ]; 26 | } 27 | 28 | /** 29 | * Registers any back-end permissions used by this plugin. 30 | * 31 | * @return array 32 | */ 33 | public function registerPermissions() 34 | { 35 | return [ 36 | 'toughdeveloper.imageresizer.access_settings' => [ 37 | 'tab' => 'toughdeveloper.imageresizer::lang.permission.tab', 38 | 'label' => 'toughdeveloper.imageresizer::lang.permission.label' 39 | ] 40 | ]; 41 | } 42 | 43 | public function boot(){ 44 | Validator::extend('valid_tinypng_key', function($attribute, $value, $parameters) { 45 | try { 46 | \Tinify\setKey($value); 47 | \Tinify\validate(); 48 | } catch(\Tinify\Exception $e) { 49 | return false; 50 | } 51 | 52 | return true; 53 | }); 54 | } 55 | 56 | public function registerMarkupTags() 57 | { 58 | return [ 59 | 'filters' => [ 60 | 'resize' => function($file_path, $width = false, $height = false, $options = []) { 61 | $image = new Image($file_path); 62 | return $image->resize($width, $height, $options); 63 | }, 64 | 'imageWidth' => function($image) { 65 | if (!$image instanceOf Image) { 66 | $image = new Image($image); 67 | } 68 | return getimagesize($image->getCachedImagePath())[0]; 69 | }, 70 | 'imageHeight' => function($image) { 71 | if (!$image instanceOf Image) { 72 | $image = new Image($image); 73 | } 74 | return getimagesize($image->getCachedImagePath())[1]; 75 | } 76 | ] 77 | ]; 78 | } 79 | 80 | public function registerSettings() 81 | { 82 | return [ 83 | 'settings' => [ 84 | 'label' => 'toughdeveloper.imageresizer::lang.settings.label', 85 | 'icon' => 'icon-picture-o', 86 | 'description' => 'toughdeveloper.imageresizer::lang.settings.description', 87 | 'class' => 'ToughDeveloper\ImageResizer\Models\Settings', 88 | 'order' => 0, 89 | 'permissions' => ['toughdeveloper.imageresizer.access_settings'] 90 | ] 91 | ]; 92 | } 93 | 94 | public function registerListColumnTypes() 95 | { 96 | return [ 97 | 'thumb' => [$this, 'evalThumbListColumn'], 98 | ]; 99 | } 100 | 101 | public function evalThumbListColumn($value, $column, $record) 102 | { 103 | $config = $column->config; 104 | 105 | // Get config options with defaults 106 | $width = isset($config['width']) ? $config['width'] : 50; 107 | $height = isset($config['height']) ? $config['height'] : 50; 108 | $options = isset($config['options']) ? $config['options'] : []; 109 | 110 | // attachMany relation? 111 | if (isset($record->attachMany[$column->columnName])) 112 | { 113 | $file = $value->first(); 114 | } 115 | // attachOne relation? 116 | else if (isset($record->attachOne[$column->columnName])) 117 | { 118 | $file = $value; 119 | } 120 | // Mediafinder 121 | else 122 | { 123 | $file = storage_path() . '/app/media' . $value; 124 | } 125 | 126 | $image = new Image($file); 127 | return $image->resize($width, $height, $options)->render(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Image Resizer 2 | 3 | - [Introduction](#introduction) 4 | - [Available filters](#filters) 5 | - [Using a string](#string) 6 | - [Using a variable](#variable) 7 | - [resize()](#resize) 8 | - [imageWidth() - imageHeight()](#imageDimensions) 9 | - [Image Compression](#compression) 10 | 11 | 12 | ## Introduction 13 | 14 | Resizes an image to the required dimensions. It accepts a string with a file path to the image or a `October\Rain\Database\Attach\File` object (you will have one of these if you have used the attachOne or AttachMany relationship) 15 | 16 | Please note, the not found image can be overwritten via the settings in the admin area. 17 | 18 | 19 | ## Available filters 20 | [`resize(int $width [, int $height , array $options])`](#resize), [`imageWidth()`](#imageDimensions), [`imageHeight()`](#imageDimensions) 21 | 22 | 23 | ### Using a string 24 | 25 | Please note, if the filter alters the URL, you must apply resize afterwards 26 | 27 | ``` 28 | {{ 'assets/graphics/background.jpg' | theme | resize(500,500) }} 29 | ``` 30 | 31 | 32 | ### Using a variable 33 | 34 | ``` 35 | {{ property.image | resize(500) }} 36 | ``` 37 | 38 | 39 | ## resize(int $width [, int $height , array $options]) 40 | 41 | Resize an image according to the given params. If `$width` or `$height` is `0`, that value is calculated using original image ratio 42 | 43 | ### Options 44 | Key | Description | Default | Options 45 | --- | --- | --- | --- 46 | mode | How the image should be fitted to dimensions | auto | exact, portrait, landscape, auto, fit or crop 47 | offset | Offset the resized image | [0,0] | [int, int] 48 | extension | The extension on the image to return | auto | auto, jpg, jpeg, gif, png 49 | quality | The quality of compression _*requires cache clear_ | 95 | 0-100 50 | sharpen | Sharpen the image across a scale of 0 - 100 _*requires cache clear_ | 0 | 0-100 51 | compress | Whether the image should be compressed or not. Only takes effect when TinyPng compression is enabled. | true | true,false 52 | 53 | 54 | ### Usage in template 55 | ``` 56 | {{ property.image | resize(500, false, { mode: 'crop', quality: '80', extension: 'png' }) }} 57 | ``` 58 | 59 | ### Usage in PHP 60 | 61 | The image resizer can also be used easily in PHP, as follows: 62 | 63 | ``` 64 | use ToughDeveloper\ImageResizer\Classes\Image; 65 | 66 | $image = new Image('/path/to/image.jpg'); 67 | $image->resize(150, 200, [ 'mode' => 'crop' ]); 68 | ``` 69 | 70 | ### Usage in Backend List 71 | 72 | The image resizer can also be used on backend lists with the type of `thumb`, e.g. 73 | 74 | ``` 75 | image: 76 | label: Image 77 | type: thumb 78 | ``` 79 | 80 | This works with: 81 | 82 | - AttachMany (uses first image) [Docs](https://octobercms.com/docs/backend/forms#widget-fileupload) 83 | - AttachOne [Docs](https://octobercms.com/docs/backend/forms#widget-fileupload) 84 | - Mediafinder [Docs](https://octobercms.com/docs/backend/forms#widget-mediafinder) 85 | 86 | You can also optionally pass width (default 50), height (default 50) and options as follows: 87 | 88 | ``` 89 | image: 90 | label: Image 91 | type: thumb 92 | width: 75 93 | height: 100 94 | options: 95 | mode: crop 96 | ``` 97 | 98 | 99 | ## imageWidth() - imageHeight() 100 | 101 | Return current image width/height - useful if you need to know the size of an image resized only by one side. 102 | ``` 103 | {{ '/path/to/image.jpg' | resize(250) | imageHeight() }} 104 | ``` 105 | 106 | 107 | ## Image Compression via TinyPNG 108 | 109 | The plugin integrates with the TinyPNG API to provide image compression. A developer API key is required, to obtain one visit https://tinypng.com/developers. Once obtained, enter it in the Image Resizer Settings area of October CMS backend. 110 | 111 | TinyPNG offer 500 free compression per month, the plugin automatically caches resized images to save credits, an option to not compress certain images is also available. 112 | 113 | If you are focussed on pagespeed, it is recommended to set your image quality at 70-80 to obtain the lowest filesize whilst still retaining high quality images. -------------------------------------------------------------------------------- /assets/default-not-found.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt-pawley/oc-imageresizer-plugin/1b978d16df3f409013c118371e97680023345588/assets/default-not-found.jpg -------------------------------------------------------------------------------- /classes/Image.php: -------------------------------------------------------------------------------- 1 | originalFilePath = $filePath; 43 | 44 | // Settings are needed often, so offset to variable 45 | $this->settings = Settings::instance(); 46 | 47 | // Create a new file object 48 | $this->file = new File; 49 | 50 | if ($filePath instanceof File) { 51 | $this->filePath = $filePath->getLocalPath(); 52 | $this->file->file_name = $filePath; 53 | return; 54 | } 55 | 56 | $this->file->file_name = $filePath; 57 | 58 | $this->filePath = (file_exists($filePath)) 59 | ? $filePath 60 | : $this->parseFileName($filePath); 61 | } 62 | 63 | /** 64 | * Resizes an Image 65 | * 66 | * @param integer $width The target width 67 | * @param integer $height The target height 68 | * @param array $options The options 69 | * 70 | * @return string 71 | */ 72 | public function resize($width = false, $height = false, $options = []) 73 | { 74 | // Parse the default settings 75 | $this->options = $this->parseDefaultSettings($options); 76 | 77 | // Not a file? Display the not found image 78 | if (!is_file($this->filePath)) { 79 | return $this->notFoundImage($width, $height); 80 | } 81 | 82 | // Not a supported extension? Return the image 83 | if (!$this->hasSupportedExtension()) { 84 | return $this; 85 | } 86 | 87 | // If extension is auto, set the actual extension 88 | if (strtolower($this->options['extension']) == 'auto') { 89 | $this->options['extension'] = $this->file->getExtension(); 90 | } 91 | 92 | // Set a disk name, this enables caching 93 | $this->file->disk_name = $this->cacheKey(); 94 | 95 | // Set the thumbfilename to save passing variables to many functions 96 | $this->thumbFilename = $this->getThumbFilename($width, $height); 97 | 98 | // If the image is cached, don't try resized it. 99 | if (! $this->isImageCached()) { 100 | // Set the file to be created from another file 101 | $this->file->fromFile($this->filePath); 102 | 103 | // Resize it 104 | $thumb = $this->file->getThumb($width, $height, $this->options); 105 | 106 | // Not a gif file? Compress with tinyPNG 107 | if ($this->isCompressionEnabled()) { 108 | $this->compressWithTinyPng(); 109 | } 110 | 111 | // Touch the cached image with the original mtime to align them 112 | touch($this->getCachedImagePath(), filemtime($this->filePath)); 113 | 114 | $this->deleteTempFile(); 115 | } 116 | 117 | // Return the URL 118 | return $this; 119 | } 120 | 121 | /** 122 | * Gets the path for the thumbnail 123 | * @return string 124 | */ 125 | public function getCachedImagePath($public = false) 126 | { 127 | // Not a support file extension? Just return the original image 128 | if (!$this->hasSupportedExtension()) { 129 | return ($public === true) 130 | ? url(str_replace(base_path() . '/', '', $this->filePath)) 131 | : $this->filePath; 132 | } 133 | 134 | $filePath = $this->file->getStorageDirectory() . $this->getPartitionDirectory() . $this->thumbFilename; 135 | 136 | if ($public === true) { 137 | return url('/storage/app/' . $filePath); 138 | } 139 | 140 | return storage_path('app/' . $filePath); 141 | } 142 | 143 | protected function deleteTempFile() 144 | { 145 | $path = storage_path('app/' . $this->file->getStorageDirectory() . $this->getPartitionDirectory() . $this->file->disk_name); 146 | if (file_exists($path)) { 147 | unlink($path); 148 | } 149 | } 150 | 151 | /** 152 | * Parse the file name to get a relative path for the file 153 | * This is mostly required for scenarios where a twig filter, e.g. theme has been applied. 154 | * @return string 155 | */ 156 | protected function parseFileName($filePath) 157 | { 158 | $path = urldecode(parse_url($filePath, PHP_URL_PATH)); 159 | 160 | // Create array of commonly used folders 161 | // These will be used to try capture the actual file path to an image without the sub-directory path 162 | $folders = [ 163 | config('cms.themesPath'), 164 | config('cms.pluginsPath'), 165 | config('cms.storage.uploads.path'), 166 | config('cms.storage.media.path') 167 | ]; 168 | 169 | foreach($folders as $folder) 170 | { 171 | if (str_contains($path, $folder)) 172 | { 173 | $paths = explode($folder, $path, 2); 174 | return base_path($folder . end($paths)); 175 | } 176 | } 177 | 178 | return base_path($path); 179 | } 180 | 181 | /** 182 | * Works out the default settings 183 | * @return string 184 | */ 185 | protected function parseDefaultSettings($options = []) 186 | { 187 | if (!isset($options['mode']) && $this->settings->default_mode) { 188 | $options['mode'] = $this->settings->default_mode; 189 | } 190 | if (!isset($options['offset']) && is_int($this->settings->default_offset_x) && is_int($this->settings->default_offset_y)) { 191 | $options['offset'] = [$this->settings->default_offset_x, $this->settings->default_offset_y]; 192 | } 193 | if (!isset($options['extension']) && $this->settings->default_extension) { 194 | $options['extension'] = $this->settings->default_extension; 195 | } 196 | if (!isset($options['quality']) && is_int($this->settings->default_quality)) { 197 | $options['quality'] = $this->settings->default_quality; 198 | } 199 | if (!isset($options['sharpen']) && is_int($this->settings->default_sharpen)) { 200 | $options['sharpen'] = $this->settings->default_sharpen; 201 | } 202 | if (!isset($options['compress'])) { 203 | $options['compress'] = true; 204 | } 205 | 206 | return $options; 207 | } 208 | 209 | /** 210 | * Creates a unique disk name for an image 211 | * @return string 212 | */ 213 | protected function cacheKey() 214 | { 215 | $diskName = $this->originalFilePath; 216 | 217 | // Ensures a unique filepath when tinypng compression is enabled 218 | if ($this->isCompressionEnabled()) { 219 | $diskName .= 'tinypng'; 220 | } 221 | 222 | return md5($diskName); 223 | } 224 | 225 | /** 226 | * Serves a not found image 227 | * @return string 228 | */ 229 | protected function notFoundImage($width, $height) 230 | { 231 | // Have we got a custom not found image? If so, serve this. 232 | if ($this->settings->not_found_image) { 233 | $imagePath = base_path() . config('cms.storage.media.path') . $this->settings->not_found_image; 234 | } 235 | 236 | // If we do not have an existing custom not found image, use the default from this plugin 237 | if (!isset($imagePath) || !file_exists($imagePath)) { 238 | $imagePath = plugins_path('toughdeveloper/imageresizer/assets/default-not-found.jpg'); 239 | } 240 | 241 | // Create a new Image object to resize 242 | $file = new Self($imagePath); 243 | 244 | // Return in the specified dimensions 245 | return $file->resize($width, $height, [ 246 | 'mode' => 'crop' 247 | ]); 248 | } 249 | 250 | /** 251 | * Compresses a png image using tinyPNG 252 | * @return string 253 | */ 254 | protected function compressWithTinyPng() 255 | { 256 | try { 257 | Tinify::setKey($this->settings->tinypng_developer_key); 258 | 259 | $filePath = $this->getCachedImagePath(); 260 | $source = Source::fromFile($filePath); 261 | $source->toFile($filePath); 262 | } 263 | catch (\Exception $e) { 264 | // Log error - may help debug 265 | \Log::error('Tiny PNG compress failed', [ 266 | 'message' => $e->getMessage(), 267 | 'code' => $e->getCode() 268 | ]); 269 | } 270 | 271 | } 272 | 273 | /** 274 | * Checks if the requested resize/compressed image is already cached. 275 | * Removes the cached image if the original image has a different mtime. 276 | * 277 | * @return bool 278 | */ 279 | protected function isImageCached() 280 | { 281 | // if there is no cached image return false 282 | if (!is_file($cached_img = $this->getCachedImagePath())) { 283 | return false; 284 | } 285 | 286 | // if cached image mtime match, the image is already cached 287 | if (filemtime($this->filePath) === filemtime($cached_img)) { 288 | return true; 289 | } 290 | 291 | // delete older cached file 292 | unlink($cached_img); 293 | 294 | // generate new cache file 295 | return false; 296 | } 297 | 298 | /** 299 | * Checks if image compression is enabled for this image. 300 | * @return bool 301 | */ 302 | protected function isCompressionEnabled() 303 | { 304 | return ($this->options['extension'] != 'gif' && $this->settings->enable_tinypng && $this->options['compress']); 305 | } 306 | 307 | /** 308 | * Generates a partition for the file. 309 | * return /ABC/DE1/234 for an name of ABCDE1234. 310 | * @param Attachment $attachment 311 | * @param string $styleName 312 | * @return mixed 313 | */ 314 | protected function getPartitionDirectory() 315 | { 316 | return implode('/', array_slice(str_split($this->cacheKey(), 3), 0, 3)) . '/'; 317 | } 318 | 319 | /** 320 | * Generates a thumbnail filename. 321 | * @return string 322 | */ 323 | protected function getThumbFilename($width, $height) 324 | { 325 | $width = (integer) $width; 326 | $height = (integer) $height; 327 | 328 | return 'thumb__' . $width . '_' . $height . '_' . $this->options['offset'][0] . '_' . $this->options['offset'][1] . '_' . $this->options['mode'] . '.' . $this->options['extension']; 329 | } 330 | 331 | /** 332 | * Checks if it is a resizable file extension 333 | * @return boolean 334 | */ 335 | protected function hasSupportedExtension() 336 | { 337 | return in_array($this->file->getExtension(), File::$imageExtensions); 338 | } 339 | 340 | /** 341 | * Render an image tag 342 | * @return string 343 | */ 344 | public function render() 345 | { 346 | return ''; 347 | } 348 | 349 | /** 350 | * Magic method to return the file path 351 | * @return string 352 | */ 353 | public function __toString() 354 | { 355 | return $this->getCachedImagePath(true); 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toughdeveloper/imageresizer", 3 | "type": "october-plugin", 4 | "description": "October CMS Plugin to resize and compress images.", 5 | "license": "MIT", 6 | "website": "https://octobercms.com/plugin/toughdeveloper-imageresizer", 7 | "require": { 8 | "composer/installers": "~1.0", 9 | "tinify/tinify": "^1.3" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lang/de/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Bildskalierung', 6 | 'description' => 'Stellt Twig-Filter zum Skalieren von Bildern bereit.' 7 | ], 8 | 'settings' => [ 9 | 'label' => 'Bildskalierung Einstellungen', 10 | 'description' => 'Einstellungen der Bildskalierung verwalten', 11 | 'tab_default' => 'Vorgaben', 12 | 'tab_advanced' => 'Erweitert', 13 | 'not_found_image_label' => 'Nicht gefunden Bild', 14 | 'not_found_image_comment' => 'Zeigt ein anpassbares Bild an, sollte das Bild nicht existieren.', 15 | 'default_mode_label' => 'Standard Modus', 16 | 'default_mode_comment' => 'Wie soll das Bild skaliert werden.', 17 | 'default_offset_x_label' => 'Standard Abstand X', 18 | 'default_offset_x_comment' => 'Setzt einen horizontalen Abstand.', 19 | 'default_offset_y_label' => 'Standard Abstand Y', 20 | 'default_offset_y_comment' => 'Setzt einen vertikalen Abstand.', 21 | 'default_extension_label' => 'Standard Erweiterung', 22 | 'default_extension_comment' => 'Die Erweiterung des zurückgegebenen Bildes.', 23 | 'default_quality_label' => 'Standard Qualität', 24 | 'default_quality_comment' => 'Die Qualität der Komprimierung (erfordert das Leeren des Chaches).', 25 | 'default_sharpen_label' => 'Standard Schärfung', 26 | 'default_sharpen_comment' => 'Schärft das Bild von 0 - 100 (erfordert das Leeren des Chaches).', 27 | 'tinypng_hint' => 'Um einen Entwickler-Schlüssel für TinyPNG zu bekommen, bitte https://tinypng.com/developers besuchen. Mit Angabe der E-Mail Addresse wird ein Link zum Schlüssel gesendet. Die ersten 500 Bilder pro Monat sind gratis, komprimierte Bilder werden gespeichert um die Anfragen zu reduzieren. Für die meisten Seiten reicht dies aus. Es wird geraten dieses Feature nur so wenig wie möglich und nur Live zu benutzen.', 28 | 'enable_tinypng_label' => 'Komprimiere Bilder mit TinyPNG', 29 | 'enable_tinypng_comment' => 'Fügt die Möglichkeit hinzu Bilder mit der tinypng.com API zu komprimieren und so die Dateigröße zu minimieren', 30 | 'tinypng_developer_key_label' => 'Entwickler Schlüssel', 31 | 'tinypng_developer_key_comment' => 'Siehe oben für eine Anleitung zum Erhalt eines Schlüssels', 32 | 'auto' => 'Auto', 33 | 'mode_exact' => 'Exakt', 34 | 'mode_portrait' => 'Hochformat', 35 | 'mode_landscape' => 'Querformat', 36 | 'mode_crop' => 'Abschneiden', 37 | 'tinypng_invalid_key' => 'Der tinypng Schlüssel konnte nicht validiert werden, bitte überprüfen und erneut probieren.', 38 | 'tinypng_compressed_images' => 'Komprimierte Bilder', 39 | 'tinypng_remaining_compressions' => 'Verbleibende kostenlose Komprimierungen', 40 | 'tinypng_days_until_reset' => 'Tage bis zum Reset' 41 | ], 42 | 'permission' => [ 43 | 'tab' => 'Bildskalierung', 44 | 'label' => 'Einstellungen verwalten' 45 | ] 46 | ]; 47 | -------------------------------------------------------------------------------- /lang/en/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Image Resizer', 6 | 'description' => 'Provides Twig filter to resize images on the fly.' 7 | ], 8 | 'settings' => [ 9 | 'label' => 'Image Resizer Settings', 10 | 'description' => 'Configure Image Resizer Settings', 11 | 'tab_default' => 'Defaults', 12 | 'tab_advanced' => 'Advanced', 13 | 'not_found_image_label' => 'Not Found Image', 14 | 'not_found_image_comment' => 'Displays a customizable image if the image doesn\'t exist.', 15 | 'default_mode_label' => 'Default mode', 16 | 'default_mode_comment' => 'How the image should be fitted to dimensions.', 17 | 'default_offset_x_label' => 'Default offset X', 18 | 'default_offset_x_comment' => 'Offset the resized image horizontally.', 19 | 'default_offset_y_label' => 'Default offset Y', 20 | 'default_offset_y_comment' => 'Offset the resized image vertically.', 21 | 'default_extension_label' => 'Default extension', 22 | 'default_extension_comment' => 'The extension on the image to return.', 23 | 'default_quality_label' => 'Default quality', 24 | 'default_quality_comment' => 'The quality of compression (requires cache clear).', 25 | 'default_sharpen_label' => 'Default sharpen', 26 | 'default_sharpen_comment' => 'Sharpen the image across a scale of 0 - 100 (requires cache clear).', 27 | 'tinypng_hint' => 'To obtain your developer key for TinyPNG, please visit https://tinypng.com/developers. Enter your email address and a link to your developer key will be emailed across to you. The first 500 compressions a month are free, compressed images are cached to reduce the number of requests and for most sites this will suffice. It is recommended to keep the number of requests to a minimum, to only enable this setting on production servers.', 28 | 'enable_tinypng_label' => 'Compress images with TinyPNG', 29 | 'enable_tinypng_comment' => 'Adds the ability to run images through tinypng.com API to reduce filesize', 30 | 'tinypng_developer_key_label' => 'Developer Key', 31 | 'tinypng_developer_key_comment' => 'See above for details of how to obtain this', 32 | 'auto' => 'Auto', 33 | 'mode_exact' => 'Exact', 34 | 'mode_portrait' => 'Portrait', 35 | 'mode_landscape' => 'Landscape', 36 | 'mode_crop' => 'Crop', 37 | 'tinypng_invalid_key' => 'The tinypng key entered could not be validated, please check the key and try again.', 38 | 'tinypng_compressed_images' => 'Compressed Images', 39 | 'tinypng_remaining_compressions' => 'Remaining Free Compressions', 40 | 'tinypng_days_until_reset' => 'Days until reset' 41 | ], 42 | 'permission' => [ 43 | 'tab' => 'Image Resizer', 44 | 'label' => 'Manage Settings' 45 | ] 46 | ]; 47 | -------------------------------------------------------------------------------- /lang/fr/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Image Resizer', 6 | 'description' => 'Fournit un filtre Twig pour redimensionner les images.' 7 | ], 8 | 'settings' => [ 9 | 'label' => 'Paramètres Image Resizer', 10 | 'description' => 'Configurer les paramètres d\'Image Resizer', 11 | 'tab_default' => 'Réglages généraux', 12 | 'tab_advanced' => 'Réglages avancés', 13 | 'not_found_image_label' => 'Image introuvable', 14 | 'not_found_image_comment' => 'Affiche une image personnalisée si l\'image n\'existe pas.', 15 | 'default_mode_label' => 'Mode par défaut', 16 | 'default_mode_comment' => 'Comment l\'image doit être adaptée aux dimensions.', 17 | 'default_offset_x_label' => 'Décalage par défaut X', 18 | 'default_offset_x_comment' => 'Décalez l\'image redimensionnée horizontalement.', 19 | 'default_offset_y_label' => 'Décalage par défaut Y', 20 | 'default_offset_y_comment' => 'Décalez l\'image redimensionnée verticalement', 21 | 'default_extension_label' => 'Extension par défaut', 22 | 'default_extension_comment' => 'L\'extension de l\'image affiché.', 23 | 'default_quality_label' => 'Qualité par défaut', 24 | 'default_quality_comment' => 'La qualité de la compression (nécessite la suppression du cache).', 25 | 'default_sharpen_label' => 'Accentuage par défaut', 26 | 'default_sharpen_comment' => 'Accentuez l\'image sur une échelle de 0 à 100 (nécessite la suppression du cache).', 27 | 'tinypng_hint' => 'Pour obtenir votre clé de développeur pour TinyPNG, consultez la page https://tinypng.com/developers. Entrez votre adresse e-mail et un lien vers votre clé développeur vous sera envoyé par courriel. Les 500 premières compressions par mois sont gratuites, les images compressées sont mises en cache afin de réduire le nombre de demandes. Cela suffira pour la plupart des sites. Il est recommandé de limiter le nombre de demandes au minimum afin d\'activer ce paramètre uniquement sur les serveurs de production.', 28 | 'enable_tinypng_label' => 'Compresser les images avec TinyPNG', 29 | 'enable_tinypng_comment' => 'Ajoute la possibilité de compresser des images via l\'API tinypng.com afin de réduire la taille du fichier.', 30 | 'tinypng_developer_key_label' => 'Clé de développeur', 31 | 'tinypng_developer_key_comment' => 'Voir ci-dessus pour savoir comment obtenir la clé.', 32 | 'auto' => 'Auto', 33 | 'mode_exact' => 'Exact', 34 | 'mode_portrait' => 'Portrait', 35 | 'mode_landscape' => 'Paysage', 36 | 'mode_crop' => 'Rogner', 37 | 'tinypng_invalid_key' => 'La clé TinyPNG saisie n\'a pas pu être validée. Veuillez vérifier la clé et réessayer.', 38 | 'tinypng_compressed_images' => 'Images compressées', 39 | 'tinypng_remaining_compressions' => 'Compressions gratuites restantes', 40 | 'tinypng_days_until_reset' => 'Jours jusqu\'à la réinitialisation' 41 | ], 42 | 'permission' => [ 43 | 'tab' => 'Image Resizer', 44 | 'label' => 'Gérer les paramètres' 45 | ] 46 | ]; 47 | -------------------------------------------------------------------------------- /lang/hu/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Képméretezés', 6 | 'description' => 'Képek dinamikus átméretezése és módosítása.' 7 | ], 8 | 'settings' => [ 9 | 'label' => 'Képméretezés', 10 | 'description' => 'Szolgáltatáshoz tartozó beállítások', 11 | 'tab_default' => 'Általános', 12 | 'tab_advanced' => 'Kiegészítők', 13 | 'not_found_image_label' => 'Helyettesítő kép', 14 | 'not_found_image_comment' => 'Ha nem létezik az adott kép, akkor ez a kép jelenik meg.', 15 | 'default_mode_label' => 'Alapértelmezett mód', 16 | 'default_mode_comment' => 'A kép arányának meghatározása.', 17 | 'default_offset_x_label' => 'Alapértelmezett X eltolás', 18 | 'default_offset_x_comment' => 'A kép vízszintes eltolásának mértéke képpontban.', 19 | 'default_offset_y_label' => 'Alapértelmezett Y eltolás', 20 | 'default_offset_y_comment' => 'A kép függőleges eltolásának mértéke képpontban.', 21 | 'default_extension_label' => 'Alapértelmezett kiterjesztés', 22 | 'default_extension_comment' => 'A kép kiterjesztésének fajtája.', 23 | 'default_quality_label' => 'Alapértelmezett minőség', 24 | 'default_quality_comment' => 'A kép minőségének mértéke. 0 és 100 közötti érték lehet.', 25 | 'default_sharpen_label' => 'Alapértelmezett élesítés', 26 | 'default_sharpen_comment' => 'A kép élesítésének mértéke. 0 és 100 közötti érték lehet.', 27 | 'tinypng_hint' => 'A TinyPNG használatához először regisztráljon a https://tinypng.com/developers címen. Ezt követően e-mailben fog kapni egy linket, amin keresztül elérheti a személyes fejlesztői kulcsát. Az első 500 tömörítés ingyenes minden hónapban. Javasoljuk, hogy a havi keret túllépésének elkerülése érdekében, csak éles weboldalnál kapcsolja be a szolgáltatást.', 28 | 'enable_tinypng_label' => 'Képek tömörítése a TinyPNG segítségével', 29 | 'enable_tinypng_comment' => 'A png kiterjesztésű képek méretének csökkentése a szolgáltatással használatával.', 30 | 'tinypng_developer_key_label' => 'Fejlesztői kulcs', 31 | 'tinypng_developer_key_comment' => 'A részleteket a fenti leírásban találja.', 32 | 'auto' => 'Automatikus', 33 | 'mode_exact' => 'Pontos', 34 | 'mode_portrait' => 'Álló', 35 | 'mode_landscape' => 'Fekvő', 36 | 'mode_crop' => 'Levágott', 37 | 'tinypng_invalid_key' => 'A megadott tinypng kulcsot nem lehet érvényesíteni. Kérjük ellenőrizze és próbálja újra.', 38 | 'tinypng_compressed_images' => 'Tömörített képek', 39 | 'tinypng_remaining_compressions' => 'Fennmaradó kompresszió', 40 | 'tinypng_days_until_reset' => 'Napok törlésig' 41 | ], 42 | 'permission' => [ 43 | 'tab' => 'Képméretezés', 44 | 'label' => 'Beállítások kezelése' 45 | ] 46 | ]; 47 | -------------------------------------------------------------------------------- /lang/zh-cn/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Image Resizer', 6 | 'description' => '提供图片大小条这个的 Twig filter。' 7 | ], 8 | 'settings' => [ 9 | 'label' => 'Image Resizer 设置', 10 | 'description' => '配置图片大小调节设置', 11 | 'tab_default' => '默认', 12 | 'tab_advanced' => '高级', 13 | 'not_found_image_label' => '未找到图片', 14 | 'not_found_image_comment' => '在图片不存在时展示一个自定义的图片。', 15 | 'default_mode_label' => '默认模式', 16 | 'default_mode_comment' => '图像应该如何与尺寸匹配。', 17 | 'default_offset_x_label' => '默认 X 轴偏移量', 18 | 'default_offset_x_comment' => '调整大小时垂直方向上的偏移量。', 19 | 'default_offset_y_label' => '默认 Y 轴偏移量', 20 | 'default_offset_y_comment' => '调整大小时垂直方向上的偏移量。', 21 | 'default_extension_label' => '默认扩展名', 22 | 'default_extension_comment' => '返回的图片的扩展名。', 23 | 'default_quality_label' => '默认质量', 24 | 'default_quality_comment' => '图片压缩质量(需要清空缓存)。', 25 | 'default_sharpen_label' => '默认锐化程度', 26 | 'default_sharpen_comment' => '在 0 - 100 范围内锐化图片(需要清空缓存)。', 27 | 'tinypng_hint' => '要申请自己的 TinyPNG 开发者 key, 请访问 https://tinypng.com/developers。输入你的邮箱地址,一个你开发者 key 的链接将会通过邮件发送给你。每个月的前 500 次压缩免费,压缩后的图片会缓存下来,用以减少请求次数,这对于大部分网站已经足够了。建议尽量减少请求数量,仅在生产服务器上启用此设置。', 28 | 'enable_tinypng_label' => '使用 TinyPNG 压缩图片', 29 | 'enable_tinypng_comment' => '提供通过 tinypng.com API 来减少文件体积的能力', 30 | 'tinypng_developer_key_label' => '开发者 Key', 31 | 'tinypng_developer_key_comment' => '要了解如何生成此 Key,请查看上面的提示', 32 | 'auto' => 'Auto', 33 | 'mode_exact' => 'Exact', 34 | 'mode_portrait' => 'Portrait', 35 | 'mode_landscape' => 'Landscape', 36 | 'mode_crop' => 'Crop', 37 | 'tinypng_invalid_key' => '输入的 tinypng 密钥无法验证,请检查密钥并重试', 38 | 'tinypng_compressed_images' => 'Compressed Images', 39 | 'tinypng_remaining_compressions' => 'Remaining Free Compressions', 40 | 'tinypng_days_until_reset' => 'Days until reset' 41 | ], 42 | 'permission' => [ 43 | 'tab' => 'Image Resizer', 44 | 'label' => '管理设置' 45 | ] 46 | ]; 47 | -------------------------------------------------------------------------------- /models/Settings.php: -------------------------------------------------------------------------------- 1 | 'integer', 21 | 'default_offset_y' => 'integer', 22 | 'default_quality' => 'integer', 23 | 'default_sharpen' => 'integer' 24 | ]; 25 | 26 | public $rules = [ 27 | 'default_quality' => 'integer|between:0,100', 28 | 'default_sharpen' => 'integer|between:0,100', 29 | 'tinypng_developer_key' => 'required_if:enable_tinypng,1' 30 | ]; 31 | 32 | public $customMessages = []; 33 | 34 | public function __construct(){ 35 | $this->customMessages['valid_tinypng_key'] = Lang::get('toughdeveloper.imageresizer::lang.settings.tinypng_invalid_key'); 36 | 37 | parent::__construct(); 38 | } 39 | 40 | public function beforeValidate() 41 | { 42 | if ($this->enable_tinypng == 1) { 43 | $this->rules['tinypng_developer_key'] .= '|valid_tinypng_key'; 44 | } 45 | } 46 | 47 | // Default setting data 48 | public function initSettingsData() 49 | { 50 | $this->default_extension = 'auto'; 51 | $this->default_mode = 'auto'; 52 | $this->default_offset_x = 0; 53 | $this->default_offset_y = 0; 54 | $this->default_quality = 95; 55 | $this->default_sharpen = 0; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /models/settings/_tinypng_hint.htm: -------------------------------------------------------------------------------- 1 |

-------------------------------------------------------------------------------- /models/settings/_tinypng_stats.htm: -------------------------------------------------------------------------------- 1 | formWidget->data->tinypng_developer_key; 4 | 5 | if ($key) { 6 | try { 7 | \Tinify\setKey($key); 8 | \Tinify\validate(); 9 | 10 | $validKey = true; 11 | 12 | $compressionsThisMonth = (int) \Tinify\compressionCount(); 13 | $remainingFree = max(0, 500 - $compressionsThisMonth); 14 | } catch(\Tinify\Exception $e) { 15 | 16 | } 17 | } 18 | 19 | $currentDate = new DateTime(date('Y-m-d')); 20 | $currentDate->add(new DateInterval('P1M')); 21 | $firstDayOfNextMonth = $currentDate->format('Y-m-1'); 22 | $firstDayOfNextMonth = new DateTime($firstDayOfNextMonth); 23 | 24 | $now = new DateTime(); 25 | $interval = $now->diff($firstDayOfNextMonth); 26 | $daysUntilRenew = $interval->format('%a'); 27 | 28 | 29 | if ($validKey) 30 | { 31 | ?> 32 |
33 |
34 |
35 |
    36 |
  • 37 |
  • 38 |
39 |
40 |
41 |

42 |

43 |
44 |
45 |
46 |