├── .editorconfig ├── .gitattributes ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Code-of-Conduct.md ├── LICENSE ├── README.md ├── _config └── extensions.yml ├── composer.json ├── docs └── en │ └── Configuration.md └── src ├── Flysystem └── FlysystemAssetStore.php └── Tasks └── ImageOptimiserTask.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the properties used in this file, 2 | # please see the EditorConfig documentation: 3 | # http://editorconfig.org 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 4 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [{*.yml,package.json}] 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axllent/silverstripe-image-optimiser/aaeff5c7be237f4c771e47827530becfbf32534a/.gitattributes -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Notable changes to this project will be documented in this file. 4 | 5 | ## [1.0.5] 6 | 7 | - Add support for Silverstripe 5 8 | 9 | ## [1.0.4] 10 | 11 | - Bugfix, disable ImageOptimiserTask with `protected $enabled` 12 | 13 | 14 | ## [1.0.3] 15 | 16 | - Disable ImageOptimiserTask due to filesystem/database mismatch 17 | 18 | 19 | ## [1.0.2] 20 | 21 | - Skip empty files in ImageOptimiserTask 22 | 23 | 24 | ## [1.0.1] 25 | 26 | - Remove debugging information 27 | 28 | 29 | ## [1.0.0] 30 | 31 | - Initial release 32 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | If you wish to contribute to the module then please do so via the 4 | [github project page](https://github.com/axllent/silverstripe-image-optimiser). 5 | -------------------------------------------------------------------------------- /Code-of-Conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Any discussions about this module, issues or pull requests should be done through the Github project page. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2019 -> now() Techno Joy www.technojoy.co.nz 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Optimised images for Silverstripe 2 | 3 | A module to automatically optimise/compress uploaded as well as any resampled 4 | (cropped, scaled etc) images in Silverstripe. Images (JPG, PNG & GIF) are automatically 5 | optimised, provided you have the correct binaries installed (see "Installation" below). 6 | 7 | The module overrides the default `FlysystemAssetStore` to and transparently optimises 8 | the image before adding the image to the store. 9 | 10 | 11 | ## Requirements 12 | 13 | - `silverstripe/framework` ^4.0 || ^5.0 14 | - `silverstripe/assets` ^1.10 || ^2.0 15 | - [spatie/image-optimizer](https://github.com/spatie/image-optimizer) - automatically installed 16 | - JpegOptim, Optipng, Pngquant 2 & Gifsicle binaries (see below) 17 | 18 | 19 | ## Optimisation tools 20 | 21 | The module uses [spatie/image-optimizer](https://github.com/spatie/image-optimizer) and will use the 22 | following optimisers if they are both present and in your default path on your system: 23 | 24 | - [JpegOptim](https://github.com/tjko/jpegoptim) 25 | - [Optipng](http://optipng.sourceforge.net/) 26 | - [Pngquant 2](https://pngquant.org/) 27 | - [Gifsicle](http://www.lcdf.org/gifsicle/) 28 | 29 | 30 | ## Installation 31 | 32 | ```shell 33 | composer require axllent/silverstripe-image-optimiser 34 | ``` 35 | 36 | ### Installing the utilities on Ubuntu: 37 | 38 | ```bash 39 | sudo apt-get install jpegoptim optipng pngquant gifsicle 40 | ``` 41 | 42 | 43 | ### Installing the utilities on Alpine Linux: 44 | 45 | ```bash 46 | apk add jpegoptim optipng pngquant gifsicle 47 | ``` 48 | 49 | 50 | ## Usage 51 | 52 | Assuming you have the necessary binaries installed, it should "just work" with the default settings 53 | once you have flushed your Silverstripe installation. 54 | 55 | For custom optimisation settings, please refer to the 56 | [Configuration documentation](docs/en/Configuration.md). 57 | -------------------------------------------------------------------------------- /_config/extensions.yml: -------------------------------------------------------------------------------- 1 | --- 2 | Name: imageoptimiserflysystemassetstore 3 | After: 4 | - "assetscore" 5 | - "assetstore" 6 | --- 7 | SilverStripe\Core\Injector\Injector: 8 | SilverStripe\Assets\Storage\AssetStore: 9 | class: Axllent\ImageOptimiser\Flysystem\FlysystemAssetStore 10 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "axllent/silverstripe-image-optimiser", 3 | "description": "Automatically optimise all uploaded & resampled images in Silverstripe", 4 | "type": "silverstripe-vendormodule", 5 | "homepage": "https://github.com/axllent/silverstripe-image-optimiser", 6 | "keywords": [ 7 | "silverstripe", 8 | "upload", 9 | "image", 10 | "compress", 11 | "optimisation" 12 | ], 13 | "license": "MIT", 14 | "authors": [ 15 | { 16 | "name": "Ralph Slooten", 17 | "homepage": "http://www.axllent.org/" 18 | } 19 | ], 20 | "support": { 21 | "issues": "https://github.com/axllent/silverstripe-image-optimiser/issues" 22 | }, 23 | "require": { 24 | "silverstripe/framework": "^4.0 || ^5.0", 25 | "silverstripe/assets": "^1.10 || ^2.0", 26 | "spatie/image-optimizer": "^1.6.4" 27 | }, 28 | "suggest": { 29 | "axllent/silverstripe-scaled-uploads": "Automatically scale down large uploaded images" 30 | }, 31 | "autoload": { 32 | "psr-4": { 33 | "Axllent\\ImageOptimiser\\": "src/" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docs/en/Configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | There probably is very little reason to change the default optimisation configuration. 4 | 5 | Below is an example config with the default options. Each optimisation "chain" is specified by the 6 | full ClassName, and the optional command line options for the binary. These chains use the default 7 | [spatie/image-optimizer](https://github.com/spatie/image-optimizer) classes and values, 8 | so if you wish to add your own classes then please refer to the 9 | [spatie/image-optimizer documentation](https://github.com/spatie/image-optimizer#writing-a-custom-optimizers). 10 | 11 | ```yaml 12 | Axllent\ImageOptimiser\Flysystem\FlysystemAssetStore: 13 | chains: 14 | Spatie\ImageOptimizer\Optimizers\Jpegoptim: 15 | - "--max=85" 16 | - "--all-progressive" 17 | Spatie\ImageOptimizer\Optimizers\Pngquant: 18 | - "--force" 19 | Spatie\ImageOptimizer\Optimizers\Optipng: 20 | - "-i0" 21 | - "-o2" 22 | - "-quiet" 23 | Spatie\ImageOptimizer\Optimizers\Gifsicle: 24 | - "-b" 25 | - "-O3" 26 | ``` 27 | -------------------------------------------------------------------------------- /src/Flysystem/FlysystemAssetStore.php: -------------------------------------------------------------------------------- 1 | [ 31 | '--max=85', 32 | '--all-progressive', 33 | ], 34 | 'Spatie\ImageOptimizer\Optimizers\Pngquant' => [ 35 | '--force', 36 | ], 37 | 'Spatie\ImageOptimizer\Optimizers\Optipng' => [ 38 | '-i0', 39 | '-o2', 40 | '-quiet', 41 | ], 42 | 'Spatie\ImageOptimizer\Optimizers\Gifsicle' => [ 43 | '-b', 44 | '-O3', 45 | ], 46 | ]; 47 | 48 | /** 49 | * Asset Store file from local file 50 | * 51 | * @param string $path Local path 52 | * @param string $filename Optional filename 53 | * @param string $hash Optional hash 54 | * @param string $variant Optional variant 55 | * @param array $config Optional config options 56 | * 57 | * @return void 58 | */ 59 | public function setFromLocalFile($path, $filename = null, $hash = null, $variant = null, $config = []) 60 | { 61 | $this->optimisePath($path, $filename); 62 | 63 | return parent::setFromLocalFile($path, $filename, $hash, $variant, $config); 64 | } 65 | 66 | /** 67 | * Asset Store file from string 68 | * 69 | * @param string $data File string 70 | * @param string $filename Optional file name 71 | * @param string $hash Optional hash 72 | * @param string $variant Optional variant 73 | * @param array $config Optional config options 74 | * 75 | * @return void 76 | */ 77 | public function setFromString($data, $filename, $hash = null, $variant = null, $config = []) 78 | { 79 | if ($filename) { 80 | $extension = substr(strrchr($filename, '.'), 1); 81 | $tmp_file = TEMP_PATH . DIRECTORY_SEPARATOR . 'raw_' . uniqid() . '.' . $extension; 82 | file_put_contents($tmp_file, $data); 83 | $this->optimisePath($tmp_file, $filename); 84 | $data = file_get_contents($tmp_file); 85 | unlink($tmp_file); 86 | } 87 | 88 | return parent::setFromString($data, $filename, $hash, $variant, $config); 89 | } 90 | 91 | /** 92 | * Optimise a file path 93 | * Silently ignores unsupported filetypes 94 | * 95 | * @param string $path Path to file 96 | * @param string $filename File name 97 | * 98 | * @return void 99 | */ 100 | private function optimisePath($path, $filename = null) 101 | { 102 | if (!$filename) { 103 | // we do not know the name, so probably cannot 104 | // identify what file it actually is, skip processing 105 | return; 106 | } 107 | 108 | $extension = strtolower(substr(strrchr($filename, '.'), 1)); 109 | 110 | $tmp_file = TEMP_PATH . DIRECTORY_SEPARATOR . 'optim_' . uniqid() . '.' . $extension; 111 | 112 | copy($path, $tmp_file); 113 | 114 | $chains = $this->config()->get('chains'); 115 | 116 | // create optimizer 117 | $optimizer = new OptimizerChain(); 118 | foreach ($chains as $class => $options) { 119 | $optimizer->addOptimizer( 120 | new $class($options) 121 | ); 122 | } 123 | 124 | $optimizer->optimize($tmp_file); 125 | 126 | $raw_size = filesize($path); 127 | $opt_size = filesize($tmp_file); 128 | 129 | if ($raw_size > $opt_size && $opt_size > 0) { 130 | $raw = file_get_contents($tmp_file); 131 | file_put_contents($path, $raw); 132 | } 133 | 134 | unlink($tmp_file); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Tasks/ImageOptimiserTask.php: -------------------------------------------------------------------------------- 1 | _findOrOptimiseAllImagesAndVariants(false); 72 | 73 | if ($request->getVar('run') || Director::is_cli()) { 74 | 75 | } else { 76 | print '
There are ' . count($all_files) . ' images and image variants.
77 |Please ensure that you have backups before running this task as 78 | this will overwrite each image (if sufficiently optimised).
79 |Depending on how many images you have and how big they are, 80 | this process can take a significant amount of time, which may time 81 | out in your browser.
82 |
Running this task on the command line is strongly recommended
83 | (vendor/bin/sake dev/tasks/ImageOptimiser
)