From 46c7db19ea639b7ea952dd8a1e8bbd4339d820c1 Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Sat, 20 May 2017 15:06:35 -0400 Subject: [PATCH 01/27] change symfony kernel version comparison mechanism --- Utility/Framework/SymfonyFramework.php | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Utility/Framework/SymfonyFramework.php b/Utility/Framework/SymfonyFramework.php index a9e16c8f2..0596381ed 100644 --- a/Utility/Framework/SymfonyFramework.php +++ b/Utility/Framework/SymfonyFramework.php @@ -13,6 +13,9 @@ use Symfony\Component\HttpKernel\Kernel; +/** + * @internal + */ class SymfonyFramework { /** @@ -75,19 +78,6 @@ public static function isKernelLessThan($major, $minor = null, $patch = null) */ private static function kernelVersionCompare($operator, $major, $minor = null, $patch = null) { - $vernum = $major; - $kernel = Kernel::MAJOR_VERSION; - - if ($minor) { - $vernum .= '.'.$minor; - $kernel .= '.'.Kernel::MINOR_VERSION; - - if ($patch) { - $vernum .= '.'.$patch; - $kernel .= '.'.Kernel::RELEASE_VERSION; - } - } - - return version_compare($kernel, $vernum, $operator); + return version_compare(Kernel::VERSION_ID, sprintf("%d%'.02d%'.02d", $major, $minor ?: 0, $patch ?: 0), $operator); } } From 6cef67fcfcb432cf56fce771001499b0b0ae61fc Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Sat, 20 May 2017 17:24:51 -0400 Subject: [PATCH 02/27] code style fixes --- Async/CacheResolved.php | 5 +++-- Async/ResolveCache.php | 5 +++-- Async/ResolveCacheProcessor.php | 7 ++++--- Async/Topics.php | 1 + LiipImagineBundle.php | 2 +- Tests/Async/CacheResolvedTest.php | 3 ++- Tests/Async/ResolveCacheProcessorTest.php | 7 ++++--- Tests/Async/ResolveCacheTest.php | 3 ++- 8 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Async/CacheResolved.php b/Async/CacheResolved.php index d52bcb005..cc9ce63d1 100644 --- a/Async/CacheResolved.php +++ b/Async/CacheResolved.php @@ -1,4 +1,5 @@ add(Topics::CACHE_RESOLVED, 'The topic contains messages about resolved image\'s caches') ); } - } + } } diff --git a/Tests/Async/CacheResolvedTest.php b/Tests/Async/CacheResolvedTest.php index 51b0bd50a..e6439a3d9 100644 --- a/Tests/Async/CacheResolvedTest.php +++ b/Tests/Async/CacheResolvedTest.php @@ -1,4 +1,5 @@ 'http://example.com/barFilter/thePath', ), $message->getUris()); } -} \ No newline at end of file +} diff --git a/Tests/Async/ResolveCacheProcessorTest.php b/Tests/Async/ResolveCacheProcessorTest.php index d4bbada3a..02096d813 100644 --- a/Tests/Async/ResolveCacheProcessorTest.php +++ b/Tests/Async/ResolveCacheProcessorTest.php @@ -1,4 +1,5 @@ expects($this->atLeastOnce()) ->method('resolve') - ->willReturnCallback(function($path, $filter) { + ->willReturnCallback(function ($path, $filter) { return $path.$filter.'Uri'; }) ; @@ -278,7 +279,7 @@ public function testShouldSendMessageOnSuccessResolve() ->expects($this->once()) ->method('send') ->with(Topics::CACHE_RESOLVED, $this->isInstanceOf('Liip\ImagineBundle\Async\CacheResolved')) - ->willReturnCallback(function($topic, CacheResolved $message) use ($testCase) { + ->willReturnCallback(function ($topic, CacheResolved $message) use ($testCase) { $testCase->assertEquals('theImagePath', $message->getPath()); $testCase->assertEquals(array( 'fooFilter' => 'theImagePathfooFilterUri', @@ -341,4 +342,4 @@ private function createDummyBinary() { return new Binary('theContent', 'image/png', 'png'); } -} \ No newline at end of file +} diff --git a/Tests/Async/ResolveCacheTest.php b/Tests/Async/ResolveCacheTest.php index e58f914d1..3ebf03ee3 100644 --- a/Tests/Async/ResolveCacheTest.php +++ b/Tests/Async/ResolveCacheTest.php @@ -1,4 +1,5 @@ setExpectedException('LogicException', 'The message filters could be either null or array.'); ResolveCache::jsonDeserialize('{"path": "aPath", "filters": "stringFilterIsNotAllowed"}'); } -} \ No newline at end of file +} From 4c313a2ee9b2ef46879830c8ce4d09be4a3425a6 Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Sun, 21 May 2017 16:49:50 -0400 Subject: [PATCH 03/27] remove symfony 3.3 from allowed failures --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f10e7b763..04db419ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,8 +43,6 @@ matrix: - php: 7.1 env: SYMFONY_VERSION=dev-master allow_failures: - - php: 7.1 - env: SYMFONY_VERSION=3.3.x-dev - php: 7.1 env: SYMFONY_VERSION=dev-master From 251006a40943fb0851aeb9c16a9a2bd9a1492958 Mon Sep 17 00:00:00 2001 From: Maksim Kotlyar Date: Thu, 25 May 2017 16:57:25 +0300 Subject: [PATCH 04/27] [enqueue] Improve doc. --- Resources/doc/resolve-cache-images-in-background.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Resources/doc/resolve-cache-images-in-background.rst b/Resources/doc/resolve-cache-images-in-background.rst index 12aa82700..ece0b826c 100644 --- a/Resources/doc/resolve-cache-images-in-background.rst +++ b/Resources/doc/resolve-cache-images-in-background.rst @@ -32,9 +32,8 @@ It is based on `filesystem transport`_. enqueue: transport: - default: fs - fs: - store_dir: '%kernel.root_dir%/../var/queues' + # you could set other available transports + default: 'file://%kernel.root_dir%/../var/enqueue' client: ~ Step 2: Configure LiipImagineBundle From 6857ef5f04aad88b5aa790a082ac1dcde6a03f47 Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Tue, 6 Jun 2017 03:22:53 -0400 Subject: [PATCH 05/27] move to trusty for travis except for php 5.3 --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f10e7b763..1054933ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,16 @@ --- sudo: false +dist: trusty language: php php: - - 5.3 - 5.4 - 5.5 - 5.6 - 7.0 - 7.1 - - hhvm cache: directories: @@ -26,6 +25,7 @@ matrix: include: - php: 5.3 env: COMPOSER_FLAGS="--prefer-lowest" + dist: precise - php: 5.6 env: SYMFONY_VERSION=2.3.x-dev - php: 5.6 @@ -42,6 +42,7 @@ matrix: env: SYMFONY_VERSION=3.3.x-dev WITH_ENQUEUE=true - php: 7.1 env: SYMFONY_VERSION=dev-master + - php: hhvm allow_failures: - php: 7.1 env: SYMFONY_VERSION=3.3.x-dev From a045553ebc46e741d1eba93783ba726e6f9a41fd Mon Sep 17 00:00:00 2001 From: youser Date: Thu, 8 Jun 2017 12:38:57 +0300 Subject: [PATCH 06/27] Typo at log message --- DependencyInjection/Compiler/FiltersCompilerPass.php | 2 +- DependencyInjection/Compiler/LoadersCompilerPass.php | 2 +- DependencyInjection/Compiler/PostProcessorsCompilerPass.php | 2 +- DependencyInjection/Compiler/ResolversCompilerPass.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DependencyInjection/Compiler/FiltersCompilerPass.php b/DependencyInjection/Compiler/FiltersCompilerPass.php index 07b507727..f36daa57f 100644 --- a/DependencyInjection/Compiler/FiltersCompilerPass.php +++ b/DependencyInjection/Compiler/FiltersCompilerPass.php @@ -28,7 +28,7 @@ public function process(ContainerBuilder $container) foreach ($tags as $id => $tag) { $manager->addMethodCall('addLoader', array($tag[0]['loader'], new Reference($id))); - $this->log($container, 'Registered imagine-bimdle filter loader: %s', array($id)); + $this->log($container, 'Registered imagine-bundle filter loader: %s', array($id)); } } } diff --git a/DependencyInjection/Compiler/LoadersCompilerPass.php b/DependencyInjection/Compiler/LoadersCompilerPass.php index eba4b3e5c..ac97bcb96 100644 --- a/DependencyInjection/Compiler/LoadersCompilerPass.php +++ b/DependencyInjection/Compiler/LoadersCompilerPass.php @@ -28,7 +28,7 @@ public function process(ContainerBuilder $container) foreach ($tags as $id => $tag) { $manager->addMethodCall('addLoader', array($tag[0]['loader'], new Reference($id))); - $this->log($container, 'Registered imagine-bimdle binary loader: %s', array($id)); + $this->log($container, 'Registered imagine-bundle binary loader: %s', array($id)); } } } diff --git a/DependencyInjection/Compiler/PostProcessorsCompilerPass.php b/DependencyInjection/Compiler/PostProcessorsCompilerPass.php index 42aa0cff3..5e224d0af 100644 --- a/DependencyInjection/Compiler/PostProcessorsCompilerPass.php +++ b/DependencyInjection/Compiler/PostProcessorsCompilerPass.php @@ -33,7 +33,7 @@ public function process(ContainerBuilder $container) foreach ($tags as $id => $tag) { $manager->addMethodCall('addPostProcessor', array($tag[0]['post_processor'], new Reference($id))); - $this->log($container, 'Registered imagine-bimdle filter post-processor: %s', array($id)); + $this->log($container, 'Registered imagine-bundle filter post-processor: %s', array($id)); } } } diff --git a/DependencyInjection/Compiler/ResolversCompilerPass.php b/DependencyInjection/Compiler/ResolversCompilerPass.php index 5959057be..31c37f8b0 100644 --- a/DependencyInjection/Compiler/ResolversCompilerPass.php +++ b/DependencyInjection/Compiler/ResolversCompilerPass.php @@ -28,7 +28,7 @@ public function process(ContainerBuilder $container) foreach ($tags as $id => $tag) { $manager->addMethodCall('addResolver', array($tag[0]['resolver'], new Reference($id))); - $this->log($container, 'Registered imagine-bimdle cache resolver: %s', array($id)); + $this->log($container, 'Registered imagine-bundle cache resolver: %s', array($id)); } } } From f57164ff9259629fbb8f41b1e52ee1451558edb3 Mon Sep 17 00:00:00 2001 From: imanalopher Date: Sun, 18 Jun 2017 01:01:25 +0600 Subject: [PATCH 07/27] return filter function String --- Templating/ImagineExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Templating/ImagineExtension.php b/Templating/ImagineExtension.php index c19061aca..f3a0aaa31 100644 --- a/Templating/ImagineExtension.php +++ b/Templating/ImagineExtension.php @@ -48,7 +48,7 @@ public function getFilters() * @param array $runtimeConfig * @param string $resolver * - * @return \Twig_Markup + * @return string */ public function filter($path, $filter, array $runtimeConfig = array(), $resolver = null) { From 6e8ec6d12784f3717bd49cbc3b74914d3b6c6294 Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Tue, 20 Jun 2017 01:54:15 -0400 Subject: [PATCH 08/27] add chain loader implementation --- Binary/Loader/ChainLoader.php | 64 ++++++++ .../Factory/Loader/ChainLoaderFactory.php | 66 +++++++++ LiipImagineBundle.php | 2 + Resources/config/imagine.xml | 5 + Tests/Binary/Loader/ChainLoaderTest.php | 138 ++++++++++++++++++ .../Factory/Loader/ChainLoaderFactoryTest.php | 100 +++++++++++++ .../Loader/FileSystemLoaderFactoryTest.php | 2 +- .../Loader/FlysystemLoaderFactoryTest.php | 2 +- .../Binary/Loader/ChainLoaderTest.php | 42 ++++++ Tests/Functional/app/config/config.yml | 8 + Tests/LiipImagineBundleTest.php | 19 +++ 11 files changed, 446 insertions(+), 2 deletions(-) create mode 100644 Binary/Loader/ChainLoader.php create mode 100644 DependencyInjection/Factory/Loader/ChainLoaderFactory.php create mode 100644 Tests/Binary/Loader/ChainLoaderTest.php create mode 100644 Tests/DependencyInjection/Factory/Loader/ChainLoaderFactoryTest.php create mode 100644 Tests/Functional/Binary/Loader/ChainLoaderTest.php diff --git a/Binary/Loader/ChainLoader.php b/Binary/Loader/ChainLoader.php new file mode 100644 index 000000000..3516b38a5 --- /dev/null +++ b/Binary/Loader/ChainLoader.php @@ -0,0 +1,64 @@ +loaders = array_filter($loaders, function ($loader) { + return $loader instanceof LoaderInterface; + }); + } + + /** + * {@inheritdoc} + */ + public function find($path) + { + foreach ($this->loaders as $loader) { + try { + return $loader->find($path); + } catch (\Exception $loaderException) { + // handle exception later + } + } + + throw new NotLoadableException(vsprintf('Source image not resolvable "%s" using "%s" loaders.', array( + $path, + $this->getLoaderNamesString(), + ))); + } + + /** + * @return string + */ + private function getLoaderNamesString() + { + $names = array(); + foreach ($this->loaders as $n => $l) { + $names[] = sprintf('%s=[%s]', $n, get_class($l)); + } + + return implode(':', $names); + } +} diff --git a/DependencyInjection/Factory/Loader/ChainLoaderFactory.php b/DependencyInjection/Factory/Loader/ChainLoaderFactory.php new file mode 100644 index 000000000..d14046527 --- /dev/null +++ b/DependencyInjection/Factory/Loader/ChainLoaderFactory.php @@ -0,0 +1,66 @@ +getChildLoaderDefinition(); + $definition->replaceArgument(0, $this->createLoaderReferences($config['loaders'])); + + return $this->setTaggedLoaderDefinition($loaderName, $definition, $container); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'chain'; + } + + /** + * {@inheritdoc} + */ + public function addConfiguration(ArrayNodeDefinition $builder) + { + $builder + ->children() + ->arrayNode('loaders') + ->isRequired() + ->prototype('scalar') + ->cannotBeEmpty() + ->end() + ->end() + ->end(); + } + + /** + * @param string[] $loaders + * + * @return string[] + */ + private function createLoaderReferences(array $loaders) + { + return array_combine($loaders, array_map(function ($name) { + return new Reference(sprintf('liip_imagine.binary.loader.%s', $name)); + }, $loaders)); + } +} diff --git a/LiipImagineBundle.php b/LiipImagineBundle.php index 1c15f259b..1eef40644 100644 --- a/LiipImagineBundle.php +++ b/LiipImagineBundle.php @@ -19,6 +19,7 @@ use Liip\ImagineBundle\DependencyInjection\Compiler\MetadataReaderCompilerPass; use Liip\ImagineBundle\DependencyInjection\Compiler\PostProcessorsCompilerPass; use Liip\ImagineBundle\DependencyInjection\Compiler\ResolversCompilerPass; +use Liip\ImagineBundle\DependencyInjection\Factory\Loader\ChainLoaderFactory; use Liip\ImagineBundle\DependencyInjection\Factory\Loader\FileSystemLoaderFactory; use Liip\ImagineBundle\DependencyInjection\Factory\Loader\FlysystemLoaderFactory; use Liip\ImagineBundle\DependencyInjection\Factory\Loader\StreamLoaderFactory; @@ -55,6 +56,7 @@ public function build(ContainerBuilder $container) $extension->addLoaderFactory(new StreamLoaderFactory()); $extension->addLoaderFactory(new FileSystemLoaderFactory()); $extension->addLoaderFactory(new FlysystemLoaderFactory()); + $extension->addLoaderFactory(new ChainLoaderFactory()); if (class_exists('Enqueue\Bundle\DependencyInjection\Compiler\AddTopicMetaPass')) { $container->addCompilerPass(AddTopicMetaPass::create() diff --git a/Resources/config/imagine.xml b/Resources/config/imagine.xml index f8a6e8d5f..060dc6bb3 100644 --- a/Resources/config/imagine.xml +++ b/Resources/config/imagine.xml @@ -53,6 +53,7 @@ Liip\ImagineBundle\Binary\Loader\FileSystemLoader Liip\ImagineBundle\Binary\Loader\StreamLoader Liip\ImagineBundle\Binary\Loader\FlysystemLoader + Liip\ImagineBundle\Binary\Loader\ChainLoader @@ -249,6 +250,10 @@ + + + + diff --git a/Tests/Binary/Loader/ChainLoaderTest.php b/Tests/Binary/Loader/ChainLoaderTest.php new file mode 100644 index 000000000..87745263d --- /dev/null +++ b/Tests/Binary/Loader/ChainLoaderTest.php @@ -0,0 +1,138 @@ +getChainLoader(); + } + + public function testImplementsLoaderInterface() + { + $this->assertInstanceOf('\Liip\ImagineBundle\Binary\Loader\LoaderInterface', $this->getChainLoader()); + } + + /** + * @return array[] + */ + public static function provideLoadCases() + { + $file = pathinfo(__FILE__, PATHINFO_BASENAME); + + return array( + array( + __DIR__, + $file, + ), + array( + __DIR__.'/', + $file, + ), + array( + __DIR__, '/'. + $file, + ), + array( + __DIR__.'/../../Binary/Loader', + '/'.$file, + ), + array( + realpath(__DIR__.'/..'), + 'Loader/'.$file, + ), + array( + __DIR__.'/../', + '/Loader/../../Binary/Loader/'.$file, + ), + ); + } + + /** + * @dataProvider provideLoadCases + * + * @param string $root + * @param string $path + */ + public function testLoad($root, $path) + { + $this->assertValidLoaderFindReturn($this->getChainLoader(array($root))->find($path)); + } + + /** + * @return array[] + */ + public function provideInvalidPathsData() + { + return array( + array('../Loader/../../Binary/Loader/../../../Resources/config/routing.yaml'), + array('../../Binary/'), + ); + } + + /** + * @dataProvider provideInvalidPathsData + * + * @expectedException \Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException + * @expectedExceptionMessage Source image not resolvable + */ + public function testThrowsIfFileDoesNotExist($path) + { + $this->getChainLoader()->find($path); + } + + /** + * @return FileSystemLocator + */ + private function getFileSystemLocator() + { + return new FileSystemLocator(); + } + + /** + * @param string[] $paths + * + * @return ChainLoader + */ + private function getChainLoader(array $paths = array()) + { + return new ChainLoader(array( + 'foo' => new FileSystemLoader( + MimeTypeGuesser::getInstance(), + ExtensionGuesser::getInstance(), + $paths ?: array(__DIR__), + $this->getFileSystemLocator() + ), + )); + } + + /** + * @param FileBinary|mixed $return + * @param string|null $message + */ + private function assertValidLoaderFindReturn($return, $message = null) + { + $this->assertInstanceOf('\Liip\ImagineBundle\Model\FileBinary', $return, $message); + $this->assertStringStartsWith('text/', $return->getMimeType(), $message); + } +} diff --git a/Tests/DependencyInjection/Factory/Loader/ChainLoaderFactoryTest.php b/Tests/DependencyInjection/Factory/Loader/ChainLoaderFactoryTest.php new file mode 100644 index 000000000..f11e7bbc4 --- /dev/null +++ b/Tests/DependencyInjection/Factory/Loader/ChainLoaderFactoryTest.php @@ -0,0 +1,100 @@ +assertInstanceOf('\Liip\ImagineBundle\DependencyInjection\Factory\Loader\LoaderFactoryInterface', new ChainLoaderFactory()); + } + + public function testReturnsExpectedName() + { + $loader = new ChainLoaderFactory(); + + $this->assertEquals('chain', $loader->getName()); + } + + public function testCreateLoaderDefinition() + { + $container = new ContainerBuilder(); + + $loader = new ChainLoaderFactory(); + $loader->create($container, 'the_loader_name', array( + 'loaders' => array( + 'foo', + 'bar', + 'baz', + ), + )); + + $this->assertTrue($container->hasDefinition('liip_imagine.binary.loader.the_loader_name')); + + $loaderDefinition = $container->getDefinition('liip_imagine.binary.loader.the_loader_name'); + + $this->assertInstanceOfChildDefinition($loaderDefinition); + $this->assertEquals('liip_imagine.binary.loader.prototype.chain', $loaderDefinition->getParent()); + + foreach ($loaderDefinition->getArgument(0) as $reference) { + $this->assertInstanceOf('\Symfony\Component\DependencyInjection\Reference', $reference); + } + } + + public function testProcessOptionsOnAddConfiguration() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('chain', 'array'); + + $loader = new ChainLoaderFactory(); + $loader->addConfiguration($rootNode); + + $config = $this->processConfigTree($treeBuilder, array( + 'chain' => array( + 'loaders' => array( + 'foo', + 'bar', + ), + ), + )); + + $this->assertArrayHasKey('loaders', $config); + $this->assertSame(array('foo', 'bar'), $config['loaders']); + } + + /** + * @param TreeBuilder $treeBuilder + * @param array $configs + * + * @return array + */ + private function processConfigTree(TreeBuilder $treeBuilder, array $configs) + { + $processor = new Processor(); + + return $processor->process($treeBuilder->buildTree(), $configs); + } +} diff --git a/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php b/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php index e91cdc541..6bc8f8fc8 100644 --- a/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php +++ b/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php @@ -18,7 +18,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; /** - * @covers \Liip\ImagineBundle\DependencyInjection\Factory\Loader\FileSystemLoaderFactory + * @covers \Liip\ImagineBundle\DependencyInjection\Factory\Loader\FileSystemLoaderFactory */ class FileSystemLoaderFactoryTest extends FactoryTestCase { diff --git a/Tests/DependencyInjection/Factory/Loader/FlysystemLoaderFactoryTest.php b/Tests/DependencyInjection/Factory/Loader/FlysystemLoaderFactoryTest.php index 3e9f66f1e..9ba08eba0 100644 --- a/Tests/DependencyInjection/Factory/Loader/FlysystemLoaderFactoryTest.php +++ b/Tests/DependencyInjection/Factory/Loader/FlysystemLoaderFactoryTest.php @@ -19,7 +19,7 @@ /** * @requires PHP 5.4 * - * @covers \Liip\ImagineBundle\DependencyInjection\Factory\Loader\FlysystemLoaderFactory + * @covers \Liip\ImagineBundle\DependencyInjection\Factory\Loader\FlysystemLoaderFactory */ class FlysystemLoaderFactoryTest extends \Phpunit_Framework_TestCase { diff --git a/Tests/Functional/Binary/Loader/ChainLoaderTest.php b/Tests/Functional/Binary/Loader/ChainLoaderTest.php new file mode 100644 index 000000000..f95611823 --- /dev/null +++ b/Tests/Functional/Binary/Loader/ChainLoaderTest.php @@ -0,0 +1,42 @@ +getService(sprintf('liip_imagine.binary.loader.%s', $name)); + } + + public function testFind() + { + static::createClient(); + + $loader = $this->getLoader('baz'); + + foreach (array('images/cats.jpeg', 'images/cats2.jpeg', 'file.ext', 'bar-bundle-file.ext', 'foo-bundle-file.ext') as $file) { + $this->assertNotNull($loader->find($file)); + } + } +} diff --git a/Tests/Functional/app/config/config.yml b/Tests/Functional/app/config/config.yml index 125be17a4..2c7ebbfb7 100644 --- a/Tests/Functional/app/config/config.yml +++ b/Tests/Functional/app/config/config.yml @@ -37,6 +37,14 @@ liip_imagine: filesystem: data_root: "%kernel.root_dir%/../../Fixtures/FileSystemLocator/root-02" + baz: + chain: + loaders: + - foo + - bar + - default + - bundles_all + bundles_all: filesystem: data_root: ~ diff --git a/Tests/LiipImagineBundleTest.php b/Tests/LiipImagineBundleTest.php index 288a5cbeb..ab77c57ba 100644 --- a/Tests/LiipImagineBundleTest.php +++ b/Tests/LiipImagineBundleTest.php @@ -224,6 +224,25 @@ public function testAddFlysystemLoaderFactoryOnBuild() $bundle->build($containerMock); } + public function testAddChainLoaderFactoryOnBuild() + { + $extensionMock = $this->createLiipImagineExtensionMock(); + $extensionMock + ->expects($this->at(6)) + ->method('addLoaderFactory') + ->with($this->isInstanceOf('Liip\ImagineBundle\DependencyInjection\Factory\Loader\ChainLoaderFactory')); + + $containerMock = $this->createContainerBuilderMock(); + $containerMock + ->expects($this->atLeastOnce()) + ->method('getExtension') + ->with('liip_imagine') + ->will($this->returnValue($extensionMock)); + + $bundle = new LiipImagineBundle(); + $bundle->build($containerMock); + } + /** * @return \PHPUnit_Framework_MockObject_MockObject|ContainerBuilder */ From 2301d94794ecec4c31922d8c41ba660d428098cd Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Thu, 29 Jun 2017 02:56:31 -0400 Subject: [PATCH 09/27] add chain loader documentation --- Resources/doc/data-loader/chain.rst | 57 +++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Resources/doc/data-loader/chain.rst diff --git a/Resources/doc/data-loader/chain.rst b/Resources/doc/data-loader/chain.rst new file mode 100644 index 000000000..cfa905a3c --- /dev/null +++ b/Resources/doc/data-loader/chain.rst @@ -0,0 +1,57 @@ + +.. _data-loaders-chain: + +Chain Loader +============ + +The ``Chain`` data loader doesn't load the image binary itself; instead +it allows for loading the image binary using any number of other +configured data loaders. For example, if you configured both a +:ref:`filesystem ` and +:ref:`flysystem ` data loader, this loader can +be defined to load from both in a defined order, returning the image +binary from the first that responds. + +.. tip:: + + This loader iterates over the data loaders in the order they are + configured in the chain definition, returning an image binary from + the first loader that supports the passed file path. This means if + a file exists in more than one loader, the file will be returned + using the first one defined in your configuration file for this + chain loader. + + + +Configuration +------------- + +As this loader leverages any number of other configured loaders, its +configuration is relatively simple; it supports only a ``loaders`` +option that accepts an array of other configured loader names: + +.. code-block:: yaml + + # app/config/config.yml + + liip_imagine: + loaders: + foo: + filesystem: + # configure filesystem loader + + bar: + flysystem: + # configure flysystem loader + + baz: + stream: + # configure stream loader + + qux: + chain: + # use the "foo", "bar", and "baz" loaders + loaders: + - foo + - bar + - baz From 6d82293aa4d688988fbf7f02c1e674347b78ff9a Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Fri, 30 Jun 2017 00:57:14 -0400 Subject: [PATCH 10/27] add support for latest imagine library version 0.7.0 --- .travis.yml | 3 ++- composer.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 712b3f7e5..f52ea50ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ env: matrix: include: - php: 5.3 - env: COMPOSER_FLAGS="--prefer-lowest" + env: COMPOSER_FLAGS="--prefer-lowest" IMAGINE_VERSION=^0.6.3 dist: precise - php: 5.6 env: SYMFONY_VERSION=2.3.x-dev @@ -53,6 +53,7 @@ before_install: - if [ "${TRAVIS_PHP_VERSION}" == "5.3" ]; then composer remove --no-update --dev satooshi/php-coveralls; fi; - if [ "${SYMFONY_VERSION:0:3}" == "2.3" ]; then composer remove --no-update --dev friendsofphp/php-cs-fixer; fi; - if [ "${SYMFONY_VERSION:-x}" != "x" ]; then composer require "symfony/symfony:${SYMFONY_VERSION}" --no-update; fi; + - if [ "${IMAGINE_VERSION:-x}" != "x" ]; then composer require "imagine/Imagine:${IMAGINE_VERSION}" --no-update; fi; - if [ "${TRAVIS_PHP_VERSION}" != "hhvm" ] && [ "${TRAVIS_PHP_VERSION:0:3}" != "5.3" ]; then composer require --no-update --dev league/flysystem:~1.0; fi; - if [ "${TRAVIS_PHP_VERSION}" != "hhvm" ] && [ "${TRAVIS_PHP_VERSION:0:1}" != "7" ]; then yes "" | pecl -q install -f mongo; composer require --no-update --dev doctrine/mongodb-odm:~1.0; fi; - if [ "${TRAVIS_PHP_VERSION}" != "hhvm" ] && [ "${TRAVIS_PHP_VERSION:0:1}" == "7" ]; then yes "" | pecl -q install -f mongodb; travis_retry composer require --dev alcaeus/mongo-php-adapter:~1.0; composer require --no-update --dev doctrine/mongodb-odm:~1.0; fi diff --git a/composer.json b/composer.json index 0170a2c2e..2b16aa97c 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ }, "require": { "php": "^5.3.9|^7.0", - "imagine/Imagine": "^0.6.3,<0.7", + "imagine/Imagine": "^0.6.3|^0.7.0,<0.8", "symfony/asset": "~2.3|~3.0", "symfony/filesystem": "~2.3|~3.0", "symfony/finder": "~2.3|~3.0", From 309690d4811213dcb4b86d2ae5705699d40afa50 Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Mon, 22 May 2017 18:35:47 -0400 Subject: [PATCH 11/27] add resample filter implementation --- .../Imagine/Filter/LoadFilterException.php | 18 ++ .../Filter/Loader/ResampleFilterLoader.php | 153 ++++++++++++++++ Resources/config/imagine.xml | 6 + Resources/doc/filters/general.rst | 81 ++++++++ .../Loader/ResampleFilterLoaderTest.php | 30 +++ .../Loader/ResampleFilterLoaderTest.php | 173 ++++++++++++++++++ .../OptionsResolver/OptionsResolverTest.php | 34 +++- Utility/OptionsResolver/OptionsResolver.php | 35 +++- 8 files changed, 521 insertions(+), 9 deletions(-) create mode 100644 Exception/Imagine/Filter/LoadFilterException.php create mode 100644 Imagine/Filter/Loader/ResampleFilterLoader.php create mode 100644 Tests/Functional/Imagine/Filter/Loader/ResampleFilterLoaderTest.php create mode 100644 Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php diff --git a/Exception/Imagine/Filter/LoadFilterException.php b/Exception/Imagine/Filter/LoadFilterException.php new file mode 100644 index 000000000..8890f5ba7 --- /dev/null +++ b/Exception/Imagine/Filter/LoadFilterException.php @@ -0,0 +1,18 @@ +imagine = $imagine; + } + + /** + * @param ImageInterface $image + * @param array $options + * + * @throws LoadFilterException + * + * @return ImageInterface + */ + public function load(ImageInterface $image, array $options = array()) + { + $options = $this->resolveOptions($options); + $tmpFile = $this->getTemporaryFile($options['temp_dir']); + + try { + $image->save($tmpFile, $this->getImagineSaveOptions($options)); + $image = $this->imagine->open($tmpFile); + $this->delTemporaryFile($tmpFile); + } catch (\Exception $exception) { + $this->delTemporaryFile($tmpFile); + throw new LoadFilterException('Unable to save/open file in resample filter loader.', null, $exception); + } + + return $image; + } + + /** + * @param string $path + * + * @throws \RuntimeException + * + * @return string + */ + private function getTemporaryFile($path) + { + if (!is_dir($path) || false === $file = tempnam($path, 'liip-imagine-bundle')) { + throw new \RuntimeException(sprintf('Unable to create temporary file in "%s" base path.', $path)); + } + + return $file; + } + + /** + * @param $file + * + * @throws \RuntimeException + */ + private function delTemporaryFile($file) + { + if (file_exists($file)) { + unlink($file); + } + } + + /** + * @param array $options + * + * @return array + */ + private function getImagineSaveOptions(array $options) + { + $saveOptions = array( + 'resolution-units' => $options['unit'], + 'resolution-x' => $options['x'], + 'resolution-y' => $options['y'], + ); + + if (isset($options['filter'])) { + $saveOptions['resampling-filter'] = $options['filter']; + } + + return $saveOptions; + } + + /** + * @param array $options + * + * @return array + */ + private function resolveOptions(array $options) + { + $resolver = new OptionsResolver(); + + $resolver->setRequired(array('x', 'y', 'unit', 'temp_dir')); + $resolver->setDefined(array('filter')); + $resolver->setDefault('temp_dir', sys_get_temp_dir()); + $resolver->setDefault('filter', 'UNDEFINED'); + + $resolver->setAllowedTypes('x', array('int', 'float')); + $resolver->setAllowedTypes('y', array('int', 'float')); + $resolver->setAllowedTypes('temp_dir', array('string')); + $resolver->setAllowedTypes('filter', array('string')); + + $resolver->setAllowedValues('unit', array( + ImageInterface::RESOLUTION_PIXELSPERINCH, + ImageInterface::RESOLUTION_PIXELSPERCENTIMETER + )); + + $resolver->setNormalizer('filter', function (Options $options, $value) { + foreach (array('\Imagine\Image\ImageInterface::FILTER_%s', '\Imagine\Image\ImageInterface::%s', '%s') as $format) { + if (defined($constant = sprintf($format, strtoupper($value))) || defined($constant = sprintf($format, $value))) { + return constant($constant); + } + } + + throw new InvalidArgumentException( + 'Invalid value for "filter" option: must be a valid constant resolvable using one of formats '. + '"\Imagine\Image\ImageInterface::FILTER_%s", "\Imagine\Image\ImageInterface::%s", or "%s".' + ); + }); + + try { + return $resolver->resolve($options); + } catch (ExceptionInterface $exception) { + throw new InvalidArgumentException(sprintf('Invalid option(s) passed to %s::load().', __CLASS__), null, $exception); + } + } +} diff --git a/Resources/config/imagine.xml b/Resources/config/imagine.xml index f8a6e8d5f..c8715d5d4 100644 --- a/Resources/config/imagine.xml +++ b/Resources/config/imagine.xml @@ -47,6 +47,7 @@ Liip\ImagineBundle\Imagine\Filter\Loader\RotateFilterLoader Liip\ImagineBundle\Imagine\Filter\Loader\FlipFilterLoader Liip\ImagineBundle\Imagine\Filter\Loader\InterlaceFilterLoader + Liip\ImagineBundle\Imagine\Filter\Loader\ResampleFilterLoader @@ -230,6 +231,11 @@ + + + + + diff --git a/Resources/doc/filters/general.rst b/Resources/doc/filters/general.rst index 138dca36f..d5288be47 100644 --- a/Resources/doc/filters/general.rst +++ b/Resources/doc/filters/general.rst @@ -61,6 +61,7 @@ Background Options values: ``topleft``, ``top``, ``topright``, ``left``, ``center``, ``right``, ``bottomleft``, ``bottom``, and ``bottomright``. + .. _filter-grayscale: Grayscale @@ -126,6 +127,82 @@ Interlace Options ``plane``, and ``partition``. +.. _filter-resample: + +Resample +-------- + +The built-in ``resample`` filter provides a resampling transformation by allows you to +change the resolution of an image. This filter exposes a number of `resample options`_ which +may be used to configure its behavior. + +.. tip:: + + Resampling changes the image resolution (also known as "pixel density") of an image + and is useful when you need to present different versions of an image dependent on + the user's screen density. For example, you may need to provide a "normal" and a + "retina" variant. + + The use of "resolution" is not to be confused with "dimensions". This filter does not + affect the dimentions of an image, only the pixel density. + + +Example configuration: + +.. code-block:: yaml + + # app/config/config.yml + + liip_imagine: + filter_sets: + + # name our filter set "my_resample_filter" + my_resample_filter: + filters: + + # use and setup the "resample" filter + resample: + + # set the unit to use for pixel density + unit: ppi + + # set the horizontal pixel density + x: 72 + + # set the vertical pixel density + y: 72 + + # set the resampling filter + filter: lanczos + + # set the temporary path to use for resampling work + tmp_dir: /my/custom/temporary/directory/path + + +Resample Options +~~~~~~~~~~~~~~~~ + +:strong:`unit:` ``string`` + Sets the unit to use for pixel density, either "pixels per inch" or "pixels per centimeter". + Valid values: ``ppi`` and ``ppc``. + +:strong:`x:` ``int|float`` + Sets the horizontal (x) pixel density to resample the image to. + +:strong:`y:` ``int|float`` + Sets the vertical (y) pixel density to resample the image to. + +:strong:`filter:` ``string`` + Sets the optional filter to use during the resampling operation. It must be a string resolvable + as a constant from `Imagine\Image\ImageInterface`_ (you may omit the ``FILTER_`` prefix) + or a valid fully qualified constant. By default it is set to ``FILTER_UNDEFINED``. + +:strong:`tmp_dir:` ``string`` + Sets the optional temporary work directory. This filter requires a temporary location to save + out and read back in the image binary, as these operations are requires to resample an image. + By default, it is set to the value of the `sys_get_temp_dir()`_ function. + + .. _filter-strip: Strip @@ -211,3 +288,7 @@ Watermark Options The **position** option and **ordering** for this filter is significant. For example, calling a ``crop`` after this filter could unintentionally remove the watermark entirely from the final image. + + +.. _`Imagine\Image\ImageInterface`: https://imagine.readthedocs.io/en/master/_static/API/Imagine/Image/ImageInterface.html +.. _`sys_get_temp_dir()`: http://php.net/manual/en/function.sys-get-temp-dir.php diff --git a/Tests/Functional/Imagine/Filter/Loader/ResampleFilterLoaderTest.php b/Tests/Functional/Imagine/Filter/Loader/ResampleFilterLoaderTest.php new file mode 100644 index 000000000..ed4c65d08 --- /dev/null +++ b/Tests/Functional/Imagine/Filter/Loader/ResampleFilterLoaderTest.php @@ -0,0 +1,30 @@ +createClient(); + + $this->assertInstanceOf( + '\Liip\ImagineBundle\Imagine\Filter\Loader\ResampleFilterLoader', + self::$kernel->getContainer()->get('liip_imagine.filter.loader.resample') + ); + } +} diff --git a/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php b/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php new file mode 100644 index 000000000..efc5c88ad --- /dev/null +++ b/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php @@ -0,0 +1,173 @@ +markTestSkipped('Requires \Imagick class (`imagick` extension) to be available.'); + } + + $imgPath = realpath(__DIR__.'/../../../Fixtures/assets/cats.png'); + $tmpPath = tempnam(sys_get_temp_dir(), 'liip-imagine-bundle-test'); + $imagine = new Imagine(); + $ppc = 240.0; + + $image = $imagine->open($imgPath); + $image = $this->createResampleFilterLoaderInstance($imagine)->load($image, array( + 'x' => $ppc, + 'y' => $ppc, + 'unit' => 'ppc', + )); + $image->save($tmpPath); + + $imagick = new \Imagick($tmpPath); + $this->assertSame(array('x' => $ppc, 'y' => $ppc), $imagick->getImageResolution()); + + @unlink($tmpPath); + } + + /** + * @return array + */ + public static function provideOptionsData() + { + return array( + array(array('x' => 500, 'y' => 500, 'unit' => 'ppi')), + array(array('x' => 500, 'y' => 500, 'unit' => 'ppc')), + array(array('x' => 120, 'y' => 120, 'unit' => 'ppi', 'filter' => 'undefined')), + array(array('x' => 120, 'y' => 120, 'unit' => 'ppi', 'filter' => 'filter_undefined')), + array(array('x' => 120, 'y' => 120, 'unit' => 'ppi', 'filter' => 'lanczos')), + array(array('x' => 120, 'y' => 120, 'unit' => 'ppi', 'filter' => 'filter_lanczos')), + ); + } + + /** + * @param array $options + * + * @dataProvider provideOptionsData + */ + public function testOptions(array $options) + { + $image = $this->getImageInterfaceMock(); + $image->expects($this->once()) + ->method('save') + ->willReturn($image); + + $imagine = $this->createImagineInterfaceMock(); + $imagine->expects($this->once()) + ->method('open') + ->willReturn($image); + + $this->createResampleFilterLoaderInstance($imagine)->load($image, $options); + } + + /** + * @return array + */ + public static function provideInvalidOptionsData() + { + return array( + array(array()), + array(array( + 'x' => 'string-is-invalid-type', + 'y' => 120, + 'unit' => 'ppi', + )), + array(array( + 'x' => 120, + 'y' => array('is', 'invalid', 'type'), + 'unit' => 'ppi', + )), + array(array( + 'x' => 120, + 'y' => 120, + 'unit' => 'invalid-value', + )), + ); + } + + /** + * @dataProvider provideInvalidOptionsData + * + * @expectedException \Liip\ImagineBundle\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid option(s) passed to Liip\ImagineBundle\Imagine\Filter\Loader\ResampleFilterLoader::load(). + */ + public function testThrowsOnInvalidOptions(array $options) + { + $loader = $this->createResampleFilterLoaderInstance(); + $loader->load($this->getImageInterfaceMock(), $options); + } + + /** + * @expectedException \Liip\ImagineBundle\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid value for "filter" option: must be a valid constant resolvable using one of formats "\Imagine\Image\ImageInterface::FILTER_%s", "\Imagine\Image\ImageInterface::%s", or "%s". + */ + public function testThrowsOnInvalidFilterOption() + { + $loader = $this->createResampleFilterLoaderInstance(); + $loader->load($this->getImageInterfaceMock(), array( + 'x' => 120, + 'y' => 120, + 'unit' => 'ppi', + 'filter' => 'invalid-filter', + )); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessageRegExp {Unable to create temporary file in ".+" base path.} + */ + public function testThrowsOnInvalidTemporaryPathOption() + { + $loader = $this->createResampleFilterLoaderInstance(); + $loader->load($this->getImageInterfaceMock(), array( + 'x' => 120, + 'y' => 120, + 'unit' => 'ppi', + 'temp_dir' => '/this/path/does/not/exist/foo/bar/baz/qux', + )); + } + + /** + * @expectedException \Liip\ImagineBundle\Exception\Imagine\Filter\LoadFilterException + */ + public function testThrowsOnSaveOrOpenError() + { + $image = $this->getImageInterfaceMock(); + $image->expects($this->once()) + ->method('save') + ->willThrowException(new \Exception('Error saving file!')); + + $this->createResampleFilterLoaderInstance()->load($image, array('x' => 120, 'y' => 120, 'unit' => 'ppi')); + } + + /** + * @param ImagineInterface $imagine + * + * @return ResampleFilterLoader + */ + private function createResampleFilterLoaderInstance(ImagineInterface $imagine = null) + { + return new ResampleFilterLoader($imagine ?: $this->createImagineInterfaceMock()); + } +} diff --git a/Tests/Utility/OptionsResolver/OptionsResolverTest.php b/Tests/Utility/OptionsResolver/OptionsResolverTest.php index 666c9ea3a..87acb21bf 100644 --- a/Tests/Utility/OptionsResolver/OptionsResolverTest.php +++ b/Tests/Utility/OptionsResolver/OptionsResolverTest.php @@ -31,6 +31,20 @@ public function testOptionsResolver() $this->assertSame(100, $options['bar']); } + public function testDefinedOptions() + { + $r = static::setupOptionsResolver(); + $options = $r->resolve(array( + 'foo' => 'a', + 'bar' => 100, + 'baz' => 'a-string', + 'qux' => 1000, + )); + + $this->assertSame('a-string', $options['baz']); + $this->assertSame(1000, $options['qux']); + } + public function testDefaultOptions() { $r = static::setupOptionsResolver(); @@ -56,6 +70,7 @@ public function testOptionsNormalizer() /** * @expectedException \Symfony\Component\OptionsResolver\Exception\ExceptionInterface + * @expectedExceptionMessageRegExp {.+"bar", "baz", "foo", "qux"+} */ public function testInvalidOptions() { @@ -67,6 +82,7 @@ public function testInvalidOptions() /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage expected to be of type "integer" */ public function testRequiredTypes() { @@ -93,16 +109,18 @@ public function testAllowedValues() */ private static function setupOptionsResolver() { - $r = new OptionsResolver(); - $r->setRequired(array('foo', 'bar')); - $r->setAllowedValues('foo', array('a', 'b', 'c', 'z')); - $r->setDefault('foo', 'a'); - $r->setAllowedTypes('foo', array('string')); - $r->setAllowedTypes('bar', array('integer')); - $r->setNormalizer('foo', function (Options $options, $value) { + $resolver = new OptionsResolver(); + $resolver->setRequired(array('foo', 'bar')); + $resolver->setDefined(array('baz', 'qux')); + $resolver->setAllowedValues('foo', array('a', 'b', 'c', 'z')); + $resolver->setDefault('foo', 'a'); + $resolver->setAllowedTypes('foo', array('string')); + $resolver->setAllowedTypes('bar', array('integer')); + $resolver->setAllowedTypes('baz', array('string')); + $resolver->setNormalizer('foo', function (Options $options, $value) { return $value === 'c' ? 'z' : $value; }); - return $r; + return $resolver; } } diff --git a/Utility/OptionsResolver/OptionsResolver.php b/Utility/OptionsResolver/OptionsResolver.php index 47548b359..ec4680905 100644 --- a/Utility/OptionsResolver/OptionsResolver.php +++ b/Utility/OptionsResolver/OptionsResolver.php @@ -15,10 +15,20 @@ use Symfony\Component\OptionsResolver\OptionsResolver as BaseOptionsResolver; /** - * @deprecated Deprecated in v1.7.x and scheduled for removal in v2.0.x + * Provides a compatible interface for Symfony's newest OptionsResolver implementation, while internally supporting + * legacy variants of the interface. This allows for a clean migration plan in our 2.x branch where any classes + * implementing this must simple "use" the real "Symfony\Component\OptionsResolver\OptionsResolver" class without any + * changes to usage within the code (once support for Symfony <2.7 is dropped). + * + * @deprecated Deprecated in v1.7 and scheduled for removal in v2.0.x */ class OptionsResolver { + /** + * @var array + */ + private $defined = array(); + /** * @var array */ @@ -44,21 +54,41 @@ class OptionsResolver */ private $normalizers = array(); + /** + * @param array $defined + * + * @return $this + */ + public function setDefined(array $defined) + { + $this->defined = $defined; + + return $this; + } + /** * @param string $option * @param mixed $value + * + * @return $this */ public function setDefault($option, $value) { $this->defaults[$option] = $value; + + return $this; } /** * @param array $options + * + * @return $this */ public function setRequired(array $options) { $this->required = $options; + + return $this; } /** @@ -125,6 +155,8 @@ public function resolve(array $options) */ private function setupResolver(BaseOptionsResolver $resolver) { + $resolver->setDefined($this->defined); + foreach ($this->allowedValues as $option => $values) { $resolver->setAllowedValues($option, $values); } @@ -143,6 +175,7 @@ private function setupResolver(BaseOptionsResolver $resolver) */ private function setupResolverLegacy(BaseOptionsResolver $resolver) { + $resolver->setOptional($this->defined); $resolver->setAllowedValues($this->allowedValues); $resolver->setAllowedTypes($this->allowedTypes); $resolver->setNormalizers($this->normalizers); From 52eeb9bbe1d19c30e007ae67a4665ea46a83eab5 Mon Sep 17 00:00:00 2001 From: Remon van de Kamp Date: Sat, 20 May 2017 15:48:48 +0200 Subject: [PATCH 12/27] Pass roots to LocatorInterface directly instead - Allow construction of Locator without data roots for BC - Mark FilesystemLocator::setOptions deprecated --- Binary/Loader/FileSystemLoader.php | 50 ++++++++++++++----- Binary/Locator/FileSystemLocator.php | 10 ++++ .../Loader/FileSystemLoaderFactory.php | 31 +++++------- Resources/config/imagine.xml | 2 + Tests/Binary/Loader/FileSystemLoaderTest.php | 11 ++-- .../Locator/FileSystemInsecureLocatorTest.php | 5 +- .../Binary/Locator/FileSystemLocatorTest.php | 5 +- .../Loader/FileSystemLoaderFactoryTest.php | 15 ++++-- UPGRADE.md | 12 +++++ 9 files changed, 92 insertions(+), 49 deletions(-) diff --git a/Binary/Loader/FileSystemLoader.php b/Binary/Loader/FileSystemLoader.php index 7b454c5cd..aaba2bbe7 100644 --- a/Binary/Loader/FileSystemLoader.php +++ b/Binary/Loader/FileSystemLoader.php @@ -38,30 +38,56 @@ class FileSystemLoader implements LoaderInterface /** * @param MimeTypeGuesserInterface $mimeGuesser * @param ExtensionGuesserInterface $extensionGuesser - * @param string[] $dataRoots + * @param string[] $locatorOrDataRoots * @param LocatorInterface $locator */ public function __construct( MimeTypeGuesserInterface $mimeGuesser, ExtensionGuesserInterface $extensionGuesser, - $dataRoots + $locatorOrDataRoots /* LocatorInterface $locator */ ) { $this->mimeTypeGuesser = $mimeGuesser; $this->extensionGuesser = $extensionGuesser; - if (count($dataRoots) === 0) { - throw new InvalidArgumentException('One or more data root paths must be specified.'); - } + if (is_array($locatorOrDataRoots) || is_string($locatorOrDataRoots)) { // pre-1.9.0 behaviour + if (count((array) $locatorOrDataRoots) === 0) { + throw new InvalidArgumentException('One or more data root paths must be specified.'); + } - if (func_num_args() >= 4 && false === ($this->locator = func_get_arg(3)) instanceof LocatorInterface) { - throw new \InvalidArgumentException(sprintf('Method %s() expects a LocatorInterface for the forth argument.', __METHOD__)); - } elseif (func_num_args() < 4) { - @trigger_error(sprintf('Method %s() will have a forth `LocatorInterface $locator` argument in version 2.0. Not defining it is deprecated since version 1.7.2', __METHOD__), E_USER_DEPRECATED); - $this->locator = new FileSystemLocator(); - } + if (func_num_args() >= 4) { + if (func_get_arg(3) instanceof LocatorInterface) { + @trigger_error( + sprintf( + 'Passing a LocatorInterface as fourth parameter to %s() is deprecated. It needs to be the third parameter. ' . + 'The previous third parameter (data roots) is removed and the data roots must now be passed as a constructor argument ' . + 'to the LocatorInterface passed to this method.', + __METHOD__ + ), + E_USER_DEPRECATED + ); - $this->locator->setOptions(array('roots' => (array) $dataRoots)); + $this->locator = func_get_arg(3); + $this->locator->setOptions(array('roots' => (array) $locatorOrDataRoots)); + } else { + throw new \InvalidArgumentException(sprintf('Unknown call to %s(). Please check the method signature.', __METHOD__)); + } + } else { + @trigger_error( + sprintf( + 'Method %s() will expect the third parameter to be a LocatorInterface in version 2.0. Defining dataroots instead is deprecated since version 1.9.0', + __METHOD__ + ), + E_USER_DEPRECATED + ); + + $this->locator = new FileSystemLocator((array) $locatorOrDataRoots); + } + } elseif ($locatorOrDataRoots instanceof LocatorInterface) { + $this->locator = $locatorOrDataRoots; + } else { + throw new \InvalidArgumentException(sprintf('Method %s() expects a LocatorInterface for the third argument.', __METHOD__)); + } } /** diff --git a/Binary/Locator/FileSystemLocator.php b/Binary/Locator/FileSystemLocator.php index ad1a802a9..177190943 100644 --- a/Binary/Locator/FileSystemLocator.php +++ b/Binary/Locator/FileSystemLocator.php @@ -23,6 +23,11 @@ class FileSystemLocator implements LocatorInterface */ private $roots = array(); + public function __construct(array $dataRoots = array()) + { + $this->roots = array_map(array($this, 'sanitizeRootPath'), (array) $dataRoots); + } + /** * @param array[] $options */ @@ -37,6 +42,11 @@ public function setOptions(array $options = array()) throw new InvalidArgumentException(sprintf('Invalid options provided to %s()', __METHOD__), null, $e); } + @trigger_error( + sprintf('%s() is deprecated. Pass the dataroots to the constuctor instead.', __METHOD__), + E_USER_DEPRECATED + ); + $this->roots = array_map(array($this, 'sanitizeRootPath'), (array) $options['roots']); } diff --git a/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php b/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php index ccc517a65..2c399d0cb 100644 --- a/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php +++ b/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php @@ -14,8 +14,10 @@ use Liip\ImagineBundle\Exception\InvalidArgumentException; use Liip\ImagineBundle\Utility\Framework\SymfonyFramework; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Reference; class FileSystemLoaderFactory extends AbstractLoaderFactory @@ -25,9 +27,18 @@ class FileSystemLoaderFactory extends AbstractLoaderFactory */ public function create(ContainerBuilder $container, $loaderName, array $config) { + $parentLocatorServiceId = sprintf('liip_imagine.binary.locator.%s', $config['locator']); + + $locatorDefinition = class_exists('\Symfony\Component\DependencyInjection\ChildDefinition') ? + new ChildDefinition($parentLocatorServiceId) : new DefinitionDecorator($parentLocatorServiceId); + + $locatorServiceId = sprintf('liip_imagine.binary.locator.%s.%s', $config['locator'], $loaderName); + $container->setDefinition($locatorServiceId, $locatorDefinition); + + $locatorDefinition->replaceArgument(0, $this->resolveDataRoots($config['data_root'], $config['bundle_resources'], $container)); + $definition = $this->getChildLoaderDefinition(); - $definition->replaceArgument(2, $this->resolveDataRoots($config['data_root'], $config['bundle_resources'], $container)); - $definition->replaceArgument(3, $this->createLocatorReference($config['locator'])); + $definition->replaceArgument(2, new Reference($locatorServiceId)); return $this->setTaggedLoaderDefinition($loaderName, $definition, $container); } @@ -163,20 +174,4 @@ private function getBundlePathsUsingNamedObj(array $classes) return $paths; } - - /** - * @param string $reference - * - * @return Reference - */ - private function createLocatorReference($reference) - { - $name = sprintf('liip_imagine.binary.locator.%s', $reference); - - if (SymfonyFramework::hasDefinitionSharing()) { - return new Reference($name); - } - - return new Reference($name, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false); - } } diff --git a/Resources/config/imagine.xml b/Resources/config/imagine.xml index 060dc6bb3..a0c819505 100644 --- a/Resources/config/imagine.xml +++ b/Resources/config/imagine.xml @@ -257,10 +257,12 @@ + + diff --git a/Tests/Binary/Loader/FileSystemLoaderTest.php b/Tests/Binary/Loader/FileSystemLoaderTest.php index 7d0b00396..2a0909f5a 100644 --- a/Tests/Binary/Loader/FileSystemLoaderTest.php +++ b/Tests/Binary/Loader/FileSystemLoaderTest.php @@ -128,7 +128,7 @@ public function testMultipleRootLoadCases($root, $path) /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessageRegExp {Method .+ expects a LocatorInterface for the forth argument} + * @expectedExceptionMessageRegExp {Unknown call to [^(]+__construct\(\)\. Please check the method signature\.} */ public function testThrowsIfConstructionArgumentsInvalid() { @@ -146,7 +146,7 @@ public function testThrowsIfConstructionArgumentsInvalid() */ public function testThrowsIfZeroCountRootPathArray() { - $this->getFileSystemLoader(array()); + new FileSystemLoader(MimeTypeGuesser::getInstance(), ExtensionGuesser::getInstance(), array()); } /** @@ -206,9 +206,9 @@ public function testThrowsIfFileDoesNotExist() /** * @return FileSystemLocator */ - private function getFileSystemLocator() + private function getFileSystemLocator($dataRoots) { - return new FileSystemLocator(); + return new FileSystemLocator((array) $dataRoots); } /** @@ -230,8 +230,7 @@ private function getFileSystemLoader($root = null, LocatorInterface $locator = n return new FileSystemLoader( MimeTypeGuesser::getInstance(), ExtensionGuesser::getInstance(), - null !== $root ? $root : $this->getDefaultDataRoots(), - null !== $locator ? $locator : $this->getFileSystemLocator() + null !== $locator ? $locator : $this->getFileSystemLocator(null !== $root ? $root : $this->getDefaultDataRoots()) ); } diff --git a/Tests/Binary/Locator/FileSystemInsecureLocatorTest.php b/Tests/Binary/Locator/FileSystemInsecureLocatorTest.php index 1827e4f4d..058ee7626 100644 --- a/Tests/Binary/Locator/FileSystemInsecureLocatorTest.php +++ b/Tests/Binary/Locator/FileSystemInsecureLocatorTest.php @@ -24,10 +24,7 @@ class FileSystemInsecureLocatorTest extends AbstractFileSystemLocatorTest */ protected function getFileSystemLocator($paths) { - $locator = new FileSystemInsecureLocator(); - $locator->setOptions(array('roots' => (array) $paths)); - - return $locator; + return new FileSystemInsecureLocator((array) $paths); } public function testLoadsOnSymbolicLinks() diff --git a/Tests/Binary/Locator/FileSystemLocatorTest.php b/Tests/Binary/Locator/FileSystemLocatorTest.php index c91493627..8dc08c6a0 100644 --- a/Tests/Binary/Locator/FileSystemLocatorTest.php +++ b/Tests/Binary/Locator/FileSystemLocatorTest.php @@ -24,10 +24,7 @@ class FileSystemLocatorTest extends AbstractFileSystemLocatorTest */ protected function getFileSystemLocator($paths) { - $locator = new FileSystemLocator(); - $locator->setOptions(array('roots' => (array) $paths)); - - return $locator; + return new FileSystemLocator((array) $paths); } /** diff --git a/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php b/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php index 6bc8f8fc8..288a04286 100644 --- a/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php +++ b/Tests/DependencyInjection/Factory/Loader/FileSystemLoaderFactoryTest.php @@ -63,7 +63,8 @@ public function testCreateLoaderDefinitionOnCreate() $this->assertInstanceOfChildDefinition($loaderDefinition); $this->assertEquals('liip_imagine.binary.loader.prototype.filesystem', $loaderDefinition->getParent()); - $this->assertEquals(array('theDataRoot'), $loaderDefinition->getArgument(2)); + $locatorReference = $container->getDefinition('liip_imagine.binary.loader.the_loader_name')->getArgument(2); + $this->assertEquals(array('theDataRoot'), $container->getDefinition((string) $locatorReference)->getArgument(0)); } public function testCreateLoaderDefinitionOnCreateWithBundlesEnabledUsingMetadata() @@ -98,7 +99,8 @@ public function testCreateLoaderDefinitionOnCreateWithBundlesEnabledUsingMetadat 'LiipBarBundle' => $barBundleRootPath.'/Resources/public', ); - $this->assertEquals($expected, $container->getDefinition('liip_imagine.binary.loader.the_loader_name')->getArgument(2)); + $locatorReference = $container->getDefinition('liip_imagine.binary.loader.the_loader_name')->getArgument(2); + $this->assertEquals($expected, $container->getDefinition((string) $locatorReference)->getArgument(0)); } public function testCreateLoaderDefinitionOnCreateWithBundlesEnabledUsingMetadataAndBlacklisting() @@ -134,7 +136,8 @@ public function testCreateLoaderDefinitionOnCreateWithBundlesEnabledUsingMetadat 'LiipBarBundle' => $barBundleRootPath.'/Resources/public', ); - $this->assertEquals($expected, $container->getDefinition('liip_imagine.binary.loader.the_loader_name')->getArgument(2)); + $locatorReference = $container->getDefinition('liip_imagine.binary.loader.the_loader_name')->getArgument(2); + $this->assertEquals($expected, $container->getDefinition((string) $locatorReference)->getArgument(0)); } public function testCreateLoaderDefinitionOnCreateWithBundlesEnabledUsingMetadataAndWhitelisting() @@ -170,7 +173,8 @@ public function testCreateLoaderDefinitionOnCreateWithBundlesEnabledUsingMetadat 'LiipFooBundle' => $fooBundleRootPath.'/Resources/public', ); - $this->assertEquals($expected, $container->getDefinition('liip_imagine.binary.loader.the_loader_name')->getArgument(2)); + $locatorReference = $container->getDefinition('liip_imagine.binary.loader.the_loader_name')->getArgument(2); + $this->assertEquals($expected, $container->getDefinition((string) $locatorReference)->getArgument(0)); } public function testCreateLoaderDefinitionOnCreateWithBundlesEnabledUsingNamedObj() @@ -201,7 +205,8 @@ public function testCreateLoaderDefinitionOnCreateWithBundlesEnabledUsingNamedOb 'LiipBarBundle' => $barBundleRootPath.'/Resources/public', ); - $this->assertEquals($expected, $container->getDefinition('liip_imagine.binary.loader.the_loader_name')->getArgument(2)); + $locatorReference = $container->getDefinition('liip_imagine.binary.loader.the_loader_name')->getArgument(2); + $this->assertEquals($expected, $container->getDefinition((string) $locatorReference)->getArgument(0)); } /** diff --git a/UPGRADE.md b/UPGRADE.md index ea6737044..6b75fe6e0 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,17 @@ # Upgrade +## 1.8.1 + + - __[Data Loader]__ The arguments for the `FileSystemLoader` class constructor have changed. Instead of passing data + roots as third parameter and optionally a `LocatorInterace` as fourth parameter, a `LocatorInterface` + should now be passed as third parameter. The data roots no longer need to be passed to the `FileSystemLoader` class + constructor but need to be passed to `LocatorInterface` class constructor instead. + Passing data roots as a third parameter to the `FileSystemLoader` class constructor is still allowed, but deprecated + and will be removed in `2.0`. + Passing both data roots as well as a `LocatorInterface` instance to the `FileSystemLoader` class constructor is still + supported but deprecated and will be removed in `2.0` as well. Note that when you this, any data root that was already + set in the `LocatorInteface` will be overwritten by de data roots passed to the `FileSystemLoader`. + ## 1.8.0 - __[Routing]__ The `Resources/config/routing.xml` file has been deprecated and will be removed in `2.0`. Use the new From 2dffd96faffe4080576b306a149bf7a84102b66b Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Fri, 21 Jul 2017 02:41:25 -0400 Subject: [PATCH 13/27] cleanup of pr --- Binary/Loader/FileSystemLoader.php | 58 ++++++++----------- Binary/Locator/FileSystemLocator.php | 11 +++- .../Loader/FileSystemLoaderFactory.php | 47 ++++++++++----- Tests/Binary/Loader/FileSystemLoaderTest.php | 18 +++--- .../Locator/AbstractFileSystemLocatorTest.php | 4 +- .../Locator/FileSystemInsecureLocatorTest.php | 18 +++--- .../Binary/Locator/FileSystemLocatorTest.php | 18 +++--- UPGRADE.md | 19 +++--- 8 files changed, 106 insertions(+), 87 deletions(-) diff --git a/Binary/Loader/FileSystemLoader.php b/Binary/Loader/FileSystemLoader.php index aaba2bbe7..a5ccc8161 100644 --- a/Binary/Loader/FileSystemLoader.php +++ b/Binary/Loader/FileSystemLoader.php @@ -38,55 +38,47 @@ class FileSystemLoader implements LoaderInterface /** * @param MimeTypeGuesserInterface $mimeGuesser * @param ExtensionGuesserInterface $extensionGuesser - * @param string[] $locatorOrDataRoots - * @param LocatorInterface $locator + * @param string[]|LocatorInterface $locator */ - public function __construct( - MimeTypeGuesserInterface $mimeGuesser, - ExtensionGuesserInterface $extensionGuesser, - $locatorOrDataRoots - /* LocatorInterface $locator */ - ) { + public function __construct(MimeTypeGuesserInterface $mimeGuesser, ExtensionGuesserInterface $extensionGuesser, $locator) + { $this->mimeTypeGuesser = $mimeGuesser; $this->extensionGuesser = $extensionGuesser; - if (is_array($locatorOrDataRoots) || is_string($locatorOrDataRoots)) { // pre-1.9.0 behaviour - if (count((array) $locatorOrDataRoots) === 0) { + if ($locator instanceof LocatorInterface) { // post-1.9.0 behavior + $this->locator = $locator; + } elseif (is_array($locator) || is_string($locator)) { // pre-1.9.0 behaviour + if (count((array) $locator) === 0) { throw new InvalidArgumentException('One or more data root paths must be specified.'); } if (func_num_args() >= 4) { if (func_get_arg(3) instanceof LocatorInterface) { - @trigger_error( - sprintf( - 'Passing a LocatorInterface as fourth parameter to %s() is deprecated. It needs to be the third parameter. ' . - 'The previous third parameter (data roots) is removed and the data roots must now be passed as a constructor argument ' . - 'to the LocatorInterface passed to this method.', - __METHOD__ - ), - E_USER_DEPRECATED - ); + @trigger_error(sprintf( + 'Passing a LocatorInterface as fourth parameter to %s() is deprecated. It needs to be the '. + 'third parameter. The previous third parameter (data roots) is removed and the data roots must '. + 'now be passed as a constructor argument to the LocatorInterface passed to this method.', __METHOD__ + ), E_USER_DEPRECATED); $this->locator = func_get_arg(3); - $this->locator->setOptions(array('roots' => (array) $locatorOrDataRoots)); + $this->locator->setOptions(array('roots' => (array) $locator)); } else { - throw new \InvalidArgumentException(sprintf('Unknown call to %s(). Please check the method signature.', __METHOD__)); + throw new \InvalidArgumentException(sprintf( + 'Unknown call to %s(). Please check the method signature.', __METHOD__ + )); } } else { - @trigger_error( - sprintf( - 'Method %s() will expect the third parameter to be a LocatorInterface in version 2.0. Defining dataroots instead is deprecated since version 1.9.0', - __METHOD__ - ), - E_USER_DEPRECATED - ); + @trigger_error(sprintf( + 'Method %s() will expect the third parameter to be a LocatorInterface in version 2.0. Defining '. + 'data roots instead is deprecated since version 1.9.0', __METHOD__ + ), E_USER_DEPRECATED); - $this->locator = new FileSystemLocator((array) $locatorOrDataRoots); + $this->locator = new FileSystemLocator((array) $locator); } - } elseif ($locatorOrDataRoots instanceof LocatorInterface) { - $this->locator = $locatorOrDataRoots; - } else { - throw new \InvalidArgumentException(sprintf('Method %s() expects a LocatorInterface for the third argument.', __METHOD__)); + } else { // invalid behavior + throw new \InvalidArgumentException(sprintf( + 'Method %s() expects a LocatorInterface for the third argument.', __METHOD__ + )); } } diff --git a/Binary/Locator/FileSystemLocator.php b/Binary/Locator/FileSystemLocator.php index 177190943..7ba6c295b 100644 --- a/Binary/Locator/FileSystemLocator.php +++ b/Binary/Locator/FileSystemLocator.php @@ -23,12 +23,17 @@ class FileSystemLocator implements LocatorInterface */ private $roots = array(); - public function __construct(array $dataRoots = array()) + /** + * @param string[] $roots + */ + public function __construct(array $roots = array()) { - $this->roots = array_map(array($this, 'sanitizeRootPath'), (array) $dataRoots); + $this->roots = array_map(array($this, 'sanitizeRootPath'), $roots); } /** + * @deprecated Since version 0.9.0, use __construct(array $roots) instead + * * @param array[] $options */ public function setOptions(array $options = array()) @@ -43,7 +48,7 @@ public function setOptions(array $options = array()) } @trigger_error( - sprintf('%s() is deprecated. Pass the dataroots to the constuctor instead.', __METHOD__), + sprintf('%s() is deprecated. Pass the data roots to the constructor instead.', __METHOD__), E_USER_DEPRECATED ); diff --git a/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php b/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php index 2c399d0cb..03fd37a52 100644 --- a/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php +++ b/DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php @@ -12,11 +12,10 @@ namespace Liip\ImagineBundle\DependencyInjection\Factory\Loader; use Liip\ImagineBundle\Exception\InvalidArgumentException; -use Liip\ImagineBundle\Utility\Framework\SymfonyFramework; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Reference; @@ -27,18 +26,8 @@ class FileSystemLoaderFactory extends AbstractLoaderFactory */ public function create(ContainerBuilder $container, $loaderName, array $config) { - $parentLocatorServiceId = sprintf('liip_imagine.binary.locator.%s', $config['locator']); - - $locatorDefinition = class_exists('\Symfony\Component\DependencyInjection\ChildDefinition') ? - new ChildDefinition($parentLocatorServiceId) : new DefinitionDecorator($parentLocatorServiceId); - - $locatorServiceId = sprintf('liip_imagine.binary.locator.%s.%s', $config['locator'], $loaderName); - $container->setDefinition($locatorServiceId, $locatorDefinition); - - $locatorDefinition->replaceArgument(0, $this->resolveDataRoots($config['data_root'], $config['bundle_resources'], $container)); - $definition = $this->getChildLoaderDefinition(); - $definition->replaceArgument(2, new Reference($locatorServiceId)); + $definition->replaceArgument(2, new Reference($this->setupLocator($loaderName, $config, $container))); return $this->setTaggedLoaderDefinition($loaderName, $definition, $container); } @@ -174,4 +163,36 @@ private function getBundlePathsUsingNamedObj(array $classes) return $paths; } + + /** + * @param string $loaderName + * @param array $config + * @param ContainerBuilder $container + * + * @return string + */ + private function setupLocator($loaderName, array $config, ContainerBuilder $container) + { + $locator = $this->getLocatorDefinition($config); + $locator->replaceArgument(0, $this->resolveDataRoots($config['data_root'], $config['bundle_resources'], $container)); + + $locatorId = sprintf('liip_imagine.binary.locator.%s.%s', $config['locator'], $loaderName); + $container->setDefinition($locatorId, $locator); + + return $locatorId; + } + + /** + * @param array $config + * + * @return Definition + */ + private function getLocatorDefinition(array $config) + { + $key = sprintf('liip_imagine.binary.locator.%s', $config['locator']); + $def = class_exists('\Symfony\Component\DependencyInjection\ChildDefinition') ? + new ChildDefinition($key) : new DefinitionDecorator($key); + + return $def; + } } diff --git a/Tests/Binary/Loader/FileSystemLoaderTest.php b/Tests/Binary/Loader/FileSystemLoaderTest.php index 2a0909f5a..5e7508bf9 100644 --- a/Tests/Binary/Loader/FileSystemLoaderTest.php +++ b/Tests/Binary/Loader/FileSystemLoaderTest.php @@ -203,14 +203,6 @@ public function testThrowsIfFileDoesNotExist() $this->getFileSystemLoader()->find('fileNotExist'); } - /** - * @return FileSystemLocator - */ - private function getFileSystemLocator($dataRoots) - { - return new FileSystemLocator((array) $dataRoots); - } - /** * @return string[] */ @@ -234,6 +226,16 @@ private function getFileSystemLoader($root = null, LocatorInterface $locator = n ); } + /** + * @param string|string[] $roots + * + * @return FileSystemLocator + */ + private function getFileSystemLocator($roots) + { + return new FileSystemLocator((array) $roots); + } + /** * @param FileBinary|mixed $return * @param string|null $message diff --git a/Tests/Binary/Locator/AbstractFileSystemLocatorTest.php b/Tests/Binary/Locator/AbstractFileSystemLocatorTest.php index 20583495d..81c0759c2 100644 --- a/Tests/Binary/Locator/AbstractFileSystemLocatorTest.php +++ b/Tests/Binary/Locator/AbstractFileSystemLocatorTest.php @@ -17,11 +17,11 @@ abstract class AbstractFileSystemLocatorTest extends \PHPUnit_Framework_TestCase { /** - * @param string[]|string $paths + * @param string[]|string $roots * * @return LocatorInterface */ - abstract protected function getFileSystemLocator($paths); + abstract protected function getFileSystemLocator($roots); public function testImplementsLocatorInterface() { diff --git a/Tests/Binary/Locator/FileSystemInsecureLocatorTest.php b/Tests/Binary/Locator/FileSystemInsecureLocatorTest.php index 058ee7626..1d4d6080b 100644 --- a/Tests/Binary/Locator/FileSystemInsecureLocatorTest.php +++ b/Tests/Binary/Locator/FileSystemInsecureLocatorTest.php @@ -19,14 +19,6 @@ */ class FileSystemInsecureLocatorTest extends AbstractFileSystemLocatorTest { - /** - * @return LocatorInterface - */ - protected function getFileSystemLocator($paths) - { - return new FileSystemInsecureLocator((array) $paths); - } - public function testLoadsOnSymbolicLinks() { $loader = $this->getFileSystemLocator($root = realpath(__DIR__.'/../../Fixtures/FileSystemLocator/root-02')); @@ -87,4 +79,14 @@ public static function provideMultipleRootLoadCases() return array(array($prepend[mt_rand(0, count($prepend) - 1)], $params[0]), $params[1]); }, static::provideLoadCases()); } + + /** + * @param string|string[] $roots + * + * @return LocatorInterface + */ + protected function getFileSystemLocator($roots) + { + return new FileSystemInsecureLocator((array) $roots); + } } diff --git a/Tests/Binary/Locator/FileSystemLocatorTest.php b/Tests/Binary/Locator/FileSystemLocatorTest.php index 8dc08c6a0..42bc25c0d 100644 --- a/Tests/Binary/Locator/FileSystemLocatorTest.php +++ b/Tests/Binary/Locator/FileSystemLocatorTest.php @@ -19,14 +19,6 @@ */ class FileSystemLocatorTest extends AbstractFileSystemLocatorTest { - /** - * @return LocatorInterface - */ - protected function getFileSystemLocator($paths) - { - return new FileSystemLocator((array) $paths); - } - /** * @expectedException \Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException * @expectedExceptionMessage Source image invalid @@ -82,4 +74,14 @@ public static function provideMultipleRootLoadCases() return array(array($prepend[mt_rand(0, count($prepend) - 1)], $params[0]), $params[1]); }, static::provideLoadCases()); } + + /** + * @param string|string[] $roots + * + * @return LocatorInterface + */ + protected function getFileSystemLocator($roots) + { + return new FileSystemLocator((array) $roots); + } } diff --git a/UPGRADE.md b/UPGRADE.md index 6b75fe6e0..8cbea6cea 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,17 +1,12 @@ # Upgrade -## 1.8.1 - - - __[Data Loader]__ The arguments for the `FileSystemLoader` class constructor have changed. Instead of passing data - roots as third parameter and optionally a `LocatorInterace` as fourth parameter, a `LocatorInterface` - should now be passed as third parameter. The data roots no longer need to be passed to the `FileSystemLoader` class - constructor but need to be passed to `LocatorInterface` class constructor instead. - Passing data roots as a third parameter to the `FileSystemLoader` class constructor is still allowed, but deprecated - and will be removed in `2.0`. - Passing both data roots as well as a `LocatorInterface` instance to the `FileSystemLoader` class constructor is still - supported but deprecated and will be removed in `2.0` as well. Note that when you this, any data root that was already - set in the `LocatorInteface` will be overwritten by de data roots passed to the `FileSystemLoader`. - +## 1.9.0 + + - __[Data Loader]__ The arguments for the `FileSystemLoader` class constructor have changed. Passing an array of roots + as the third parameter and an (optional) `LocatorInterace` as the fourth parameter is deprecated. A `LocatorInterface` + should now be passed as third parameter, and the array of data roots to the `LocatorInterface::__construct()` method + directly. All prior signatures will continue to work until `2.0` is release. + ## 1.8.0 - __[Routing]__ The `Resources/config/routing.xml` file has been deprecated and will be removed in `2.0`. Use the new From 5e321d2c3bf46ea414c18a51307a80bbbf4e3e8a Mon Sep 17 00:00:00 2001 From: Imanalopher Date: Thu, 27 Jul 2017 10:04:17 +0600 Subject: [PATCH 14/27] Modify some PHP Annotations --- Binary/Loader/FileSystemLoader.php | 1 - Model/FileBinary.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Binary/Loader/FileSystemLoader.php b/Binary/Loader/FileSystemLoader.php index 7b454c5cd..16cbbdfda 100644 --- a/Binary/Loader/FileSystemLoader.php +++ b/Binary/Loader/FileSystemLoader.php @@ -39,7 +39,6 @@ class FileSystemLoader implements LoaderInterface * @param MimeTypeGuesserInterface $mimeGuesser * @param ExtensionGuesserInterface $extensionGuesser * @param string[] $dataRoots - * @param LocatorInterface $locator */ public function __construct( MimeTypeGuesserInterface $mimeGuesser, diff --git a/Model/FileBinary.php b/Model/FileBinary.php index ecbdfa66e..458e2dcb4 100644 --- a/Model/FileBinary.php +++ b/Model/FileBinary.php @@ -31,7 +31,7 @@ class FileBinary implements FileBinaryInterface protected $format; /** - * @param string $content + * @param string $path * @param string $mimeType * @param string $format */ From 22d874c93fe09b7ab97a3b8e0afe7f1040b15824 Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Thu, 3 Aug 2017 23:06:37 -0400 Subject: [PATCH 15/27] enable functional testing of resample filter with both imagick and gmagick --- .../Loader/ResampleFilterLoaderTest.php | 108 +++++++++++++++--- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php b/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php index efc5c88ad..81d6833c0 100644 --- a/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php +++ b/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php @@ -11,7 +11,8 @@ namespace Liip\ImagineBundle\Tests\Filter; -use Imagine\Imagick\Imagine; +use Imagine\Imagick\Imagine as ImagickImagine; +use Imagine\Gmagick\Imagine as GmagickImagine; use Imagine\Image\ImagineInterface; use Liip\ImagineBundle\Imagine\Filter\Loader\ResampleFilterLoader; use Liip\ImagineBundle\Tests\AbstractTest; @@ -21,29 +22,55 @@ */ class ResampleFilterLoaderTest extends AbstractTest { - public function testResample() + /** + * @dataProvider provideResampleData + * + * @param string $imgPath + * @param float $resolution + */ + public function testResample($imgPath, $resolution) { - if (!class_exists('\Imagick')) { - $this->markTestSkipped('Requires \Imagick class (`imagick` extension) to be available.'); - } - - $imgPath = realpath(__DIR__.'/../../../Fixtures/assets/cats.png'); - $tmpPath = tempnam(sys_get_temp_dir(), 'liip-imagine-bundle-test'); - $imagine = new Imagine(); - $ppc = 240.0; + $imgType = static::getSupportedDriver(); + $tmpPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.sprintf('liip-imagine-bundle-test-%s-%d.%s', md5($imgPath), time(), pathinfo($imgPath, PATHINFO_EXTENSION)); + $imagine = $this->getImagineInstance($imgType); $image = $imagine->open($imgPath); $image = $this->createResampleFilterLoaderInstance($imagine)->load($image, array( - 'x' => $ppc, - 'y' => $ppc, + 'x' => $resolution, + 'y' => $resolution, 'unit' => 'ppc', )); $image->save($tmpPath); - $imagick = new \Imagick($tmpPath); - $this->assertSame(array('x' => $ppc, 'y' => $ppc), $imagick->getImageResolution()); - + $tmpSize = $this->getImageResolution($imgType, $tmpPath); @unlink($tmpPath); + $this->assertSame(array('x' => $resolution, 'y' => $resolution), $tmpSize); + } + + /** + * @return array[] + */ + public static function provideResampleData() + { + $paths = array( + realpath(__DIR__.'/../../../Fixtures/assets/cats.png'), + realpath(__DIR__.'/../../../Fixtures/assets/cats.jpeg'), + ); + + $resolutions = array( + 72.0, + 120.0, + 240.0 + ); + + $data = array(); + foreach ($paths as $path) { + foreach ($resolutions as $resolution) { + $data[] = array($path, $resolution); + } + } + + return $data; } /** @@ -170,4 +197,55 @@ private function createResampleFilterLoaderInstance(ImagineInterface $imagine = { return new ResampleFilterLoader($imagine ?: $this->createImagineInterfaceMock()); } + + /** + * @return string + */ + private static function getSupportedDriver() + { + if (class_exists('\Imagick')) { + return 'imagick'; + } elseif (class_exists('\Gmagick')) { + return 'gmagick'; + } + + static::markTestSkipped('Data set requires "imagick" or "gmagick" extension to be installed and loaded.'); + } + + /** + * @return ImagickImagine|GmagickImagine + */ + private function getImagineInstance($driver) + { + switch ($driver) { + case 'imagick': + return new ImagickImagine(); + + case 'gmagick': + default: + return new GmagickImagine(); + } + } + + /** + * @param string $driver + * @param string $file + * + * @return float[] + */ + private function getImageResolution($driver, $file) + { + switch ($driver) { + case 'imagick': + $driver = new \Imagick($file); + break; + + case 'gmagick': + default: + $driver = new \Gmagick($file); + break; + } + + return $driver->getImageResolution(); + } } From 23e79a7f41321010a2f8a38a00f49916ca64704a Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Fri, 4 Aug 2017 05:38:37 -0400 Subject: [PATCH 16/27] add comment about supported method signitures for FileSystemLoader::__construct() --- Binary/Loader/FileSystemLoader.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Binary/Loader/FileSystemLoader.php b/Binary/Loader/FileSystemLoader.php index a5ccc8161..529c26f47 100644 --- a/Binary/Loader/FileSystemLoader.php +++ b/Binary/Loader/FileSystemLoader.php @@ -36,9 +36,13 @@ class FileSystemLoader implements LoaderInterface protected $locator; /** + * This method will continue to support two prior, deprecated signitures for the duration of the 1.x + * release. The currently documented signiture will be the only valid usage once 2.0 is release. You + * can reference PR-963 {@see https://github.com/liip/LiipImagineBundle/pull/963} for more information. + * * @param MimeTypeGuesserInterface $mimeGuesser * @param ExtensionGuesserInterface $extensionGuesser - * @param string[]|LocatorInterface $locator + * @param LocatorInterface $locator */ public function __construct(MimeTypeGuesserInterface $mimeGuesser, ExtensionGuesserInterface $extensionGuesser, $locator) { From 39a3c14f59a8da8038674942ee7e02b3142ce279 Mon Sep 17 00:00:00 2001 From: Lars Strojny Date: Fri, 4 Aug 2017 22:54:09 +0200 Subject: [PATCH 17/27] Allow to configure the HTTP response code for redirects --- Controller/ImagineController.php | 23 +++++--- DependencyInjection/Configuration.php | 1 + DependencyInjection/LiipImagineExtension.php | 1 + Resources/config/imagine.xml | 1 + Tests/Controller/ImagineControllerTest.php | 55 ++++++++++++++++++- .../LiipImagineExtensionTest.php | 1 + .../Controller/ImagineControllerTest.php | 10 ++-- Tests/Functional/app/config/config.yml | 4 +- 8 files changed, 82 insertions(+), 14 deletions(-) diff --git a/Controller/ImagineController.php b/Controller/ImagineController.php index 6fc81fe06..8cbb6251d 100644 --- a/Controller/ImagineController.php +++ b/Controller/ImagineController.php @@ -52,23 +52,32 @@ class ImagineController protected $logger; /** - * @param DataManager $dataManager - * @param FilterManager $filterManager - * @param CacheManager $cacheManager - * @param SignerInterface $signer + * @var int + */ + protected $redirectResponseCode; + + /** + * @param DataManager $dataManager + * @param FilterManager $filterManager + * @param CacheManager $cacheManager + * @param SignerInterface $signer + * @param LoggerInterface|null $logger + * @param int $redirectResponseCode */ public function __construct( DataManager $dataManager, FilterManager $filterManager, CacheManager $cacheManager, SignerInterface $signer, - LoggerInterface $logger = null + LoggerInterface $logger = null, + $redirectResponseCode = 301 ) { $this->dataManager = $dataManager; $this->filterManager = $filterManager; $this->cacheManager = $cacheManager; $this->signer = $signer; $this->logger = $logger; + $this->redirectResponseCode = $redirectResponseCode; } /** @@ -109,7 +118,7 @@ public function filterAction(Request $request, $path, $filter) ); } - return new RedirectResponse($this->cacheManager->resolve($path, $filter, $resolver), 301); + return new RedirectResponse($this->cacheManager->resolve($path, $filter, $resolver), $this->redirectResponseCode); } catch (NonExistingFilterException $e) { $message = sprintf('Could not locate filter "%s" for path "%s". Message was "%s"', $filter, $path, $e->getMessage()); @@ -177,7 +186,7 @@ public function filterRuntimeAction(Request $request, $hash, $path, $filter) $resolver ); - return new RedirectResponse($this->cacheManager->resolve($rcPath, $filter, $resolver), 301); + return new RedirectResponse($this->cacheManager->resolve($rcPath, $filter, $resolver), $this->redirectResponseCode); } catch (NonExistingFilterException $e) { $message = sprintf('Could not locate filter "%s" for path "%s". Message was "%s"', $filter, $hash.'/'.$path, $e->getMessage()); diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index e5577dda4..e9e27f9b3 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -124,6 +124,7 @@ public function getConfigTreeBuilder() ->children() ->scalarNode('filter_action')->defaultValue('liip_imagine.controller:filterAction')->end() ->scalarNode('filter_runtime_action')->defaultValue('liip_imagine.controller:filterRuntimeAction')->end() + ->scalarNode('redirect_response_code')->defaultValue(301)->end() ->end() ->end() ->arrayNode('filter_sets') diff --git a/DependencyInjection/LiipImagineExtension.php b/DependencyInjection/LiipImagineExtension.php index 7f406c253..0e3e22f08 100644 --- a/DependencyInjection/LiipImagineExtension.php +++ b/DependencyInjection/LiipImagineExtension.php @@ -95,6 +95,7 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter('liip_imagine.controller.filter_action', $config['controller']['filter_action']); $container->setParameter('liip_imagine.controller.filter_runtime_action', $config['controller']['filter_runtime_action']); + $container->setParameter('liip_imagine.controller.redirect_response_code', $config['controller']['redirect_response_code']); $resources = $container->hasParameter('twig.form.resources') ? $container->getParameter('twig.form.resources') : array(); $resources[] = 'LiipImagineBundle:Form:form_div_layout.html.twig'; diff --git a/Resources/config/imagine.xml b/Resources/config/imagine.xml index 060dc6bb3..ad0ab774e 100644 --- a/Resources/config/imagine.xml +++ b/Resources/config/imagine.xml @@ -134,6 +134,7 @@ + %liip_imagine.controller.redirect_response_code% diff --git a/Tests/Controller/ImagineControllerTest.php b/Tests/Controller/ImagineControllerTest.php index d93da8486..3057bcb24 100644 --- a/Tests/Controller/ImagineControllerTest.php +++ b/Tests/Controller/ImagineControllerTest.php @@ -13,6 +13,7 @@ use Liip\ImagineBundle\Controller\ImagineController; use Liip\ImagineBundle\Tests\AbstractTest; +use Symfony\Component\HttpFoundation\Request; /** * @covers \Liip\ImagineBundle\Controller\ImagineController @@ -26,7 +27,59 @@ public function testConstruction() $this->createFilterManagerMock(), $this->createCacheManagerMock(), $this->createSignerInterfaceMock(), - $this->createLoggerInterfaceMock() + $this->createLoggerInterfaceMock(), + 301 ); } + + public function testRedirectCodeIsConfigurable() + { + $redirectResponseCode = 307; + $path = '/foo'; + $filter = 'filter'; + $binary = $this->createObjectMock('\Liip\ImagineBundle\Model\Binary'); + $hash = 'hash'; + + $dataManager = $this->createDataManagerMock(); + $dataManager + ->method('find') + ->with($filter, $path) + ->willReturn($binary); + + $filterManager = $this->createFilterManagerMock(); + $filterManager + ->method('applyFilter') + ->with($binary, $filter) + ->willReturn($binary); + + $cacheManager = $this->createCacheManagerMock(); + $cacheManager + ->method('resolve') + ->willReturn($path, $filter) + ->willReturn('/target'); + + $signer = $this->createSignerInterfaceMock(); + $signer + ->expects($this->once()) + ->method('check') + ->with($hash, $path, array()) + ->willReturn(true); + + $controller = new ImagineController( + $dataManager, + $filterManager, + $cacheManager, + $signer, + $this->createLoggerInterfaceMock(), + $redirectResponseCode + ); + + $response = $controller->filterAction(new Request(), $path, $filter); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); + $this->assertSame($redirectResponseCode, $response->getStatusCode()); + + $response = $controller->filterRuntimeAction(new Request(), $hash, $path, $filter); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); + $this->assertSame($redirectResponseCode, $response->getStatusCode()); + } } diff --git a/Tests/DependencyInjection/LiipImagineExtensionTest.php b/Tests/DependencyInjection/LiipImagineExtensionTest.php index 5b8492ffc..9d7034d1c 100644 --- a/Tests/DependencyInjection/LiipImagineExtensionTest.php +++ b/Tests/DependencyInjection/LiipImagineExtensionTest.php @@ -57,6 +57,7 @@ public function testLoadWithDefaults() new Reference('liip_imagine.cache.manager'), new Reference('liip_imagine.cache.signer'), new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), + '%liip_imagine.controller.redirect_response_code%' ) ); } diff --git a/Tests/Functional/Controller/ImagineControllerTest.php b/Tests/Functional/Controller/ImagineControllerTest.php index 85d1d9c4e..14301a439 100644 --- a/Tests/Functional/Controller/ImagineControllerTest.php +++ b/Tests/Functional/Controller/ImagineControllerTest.php @@ -37,7 +37,7 @@ public function testShouldResolvePopulatingCacheFirst() $response = $this->client->getResponse(); $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $response); - $this->assertEquals(301, $response->getStatusCode()); + $this->assertEquals(302, $response->getStatusCode()); $this->assertEquals('http://localhost/media/cache/thumbnail_web_path/images/cats.jpeg', $response->getTargetUrl()); $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); @@ -55,7 +55,7 @@ public function testShouldResolveFromCache() $response = $this->client->getResponse(); $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $response); - $this->assertEquals(301, $response->getStatusCode()); + $this->assertEquals(302, $response->getStatusCode()); $this->assertEquals('http://localhost/media/cache/thumbnail_web_path/images/cats.jpeg', $response->getTargetUrl()); $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); @@ -131,7 +131,7 @@ public function testShouldResolveWithCustomFiltersPopulatingCacheFirst() $response = $this->client->getResponse(); $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $response); - $this->assertEquals(301, $response->getStatusCode()); + $this->assertEquals(302, $response->getStatusCode()); $this->assertEquals('http://localhost/media/cache/'.$expectedCachePath, $response->getTargetUrl()); $this->assertFileExists($this->cacheRoot.'/'.$expectedCachePath); @@ -166,7 +166,7 @@ public function testShouldResolveWithCustomFiltersFromCache() $response = $this->client->getResponse(); $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $response); - $this->assertEquals(301, $response->getStatusCode()); + $this->assertEquals(302, $response->getStatusCode()); $this->assertEquals('http://localhost/media/cache'.'/'.$expectedCachePath, $response->getTargetUrl()); $this->assertFileExists($this->cacheRoot.'/'.$expectedCachePath); @@ -186,7 +186,7 @@ public function testShouldResolvePathWithSpecialCharactersAndWhiteSpaces() $response = $this->client->getResponse(); $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $response); - $this->assertEquals(301, $response->getStatusCode()); + $this->assertEquals(302, $response->getStatusCode()); $this->assertEquals('http://localhost/media/cache/thumbnail_web_path/images/foo bar.jpeg', $response->getTargetUrl()); $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/foo bar.jpeg'); diff --git a/Tests/Functional/app/config/config.yml b/Tests/Functional/app/config/config.yml index 2c7ebbfb7..41b99282a 100644 --- a/Tests/Functional/app/config/config.yml +++ b/Tests/Functional/app/config/config.yml @@ -84,4 +84,6 @@ liip_imagine: filters: thumbnail: { size: [223, 223], mode: inset } -... + + controller: + redirect_response_code: 302 From 3413e7c1ae2d3f2243a166bd9e0712fa5a85d9b9 Mon Sep 17 00:00:00 2001 From: Cliff Odijk Date: Fri, 11 Aug 2017 15:42:05 +0200 Subject: [PATCH 18/27] Added support for centerright and centerleft position to the BackgroundFilterLoader --- Imagine/Filter/Loader/BackgroundFilterLoader.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Imagine/Filter/Loader/BackgroundFilterLoader.php b/Imagine/Filter/Loader/BackgroundFilterLoader.php index 7f8d8a503..75a4ec8d6 100644 --- a/Imagine/Filter/Loader/BackgroundFilterLoader.php +++ b/Imagine/Filter/Loader/BackgroundFilterLoader.php @@ -62,10 +62,18 @@ public function load(ImageInterface $image, array $options = array()) $x = 0; $y = ($height - $image->getSize()->getHeight()) / 2; break; + case 'centerright': + $x = $width - $image->getSize()->getWidth(); + $y = ($height - $image->getSize()->getHeight()) / 2; + break; case 'center': $x = ($width - $image->getSize()->getWidth()) / 2; $y = ($height - $image->getSize()->getHeight()) / 2; break; + case 'centerleft': + $x = 0; + $y = ($height - $image->getSize()->getHeight()) / 2; + break; case 'right': $x = $width - $image->getSize()->getWidth(); $y = ($height - $image->getSize()->getHeight()) / 2; From 145133a9cda0a33cd23148f94c4589f6108c6ad7 Mon Sep 17 00:00:00 2001 From: Cliff Odijk Date: Mon, 28 Aug 2017 13:19:51 +0200 Subject: [PATCH 19/27] Updated the docs for #974 --- Resources/doc/filters/general.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/doc/filters/general.rst b/Resources/doc/filters/general.rst index 138dca36f..c6815858e 100644 --- a/Resources/doc/filters/general.rst +++ b/Resources/doc/filters/general.rst @@ -203,7 +203,7 @@ Watermark Options :strong:`position:` ``string`` Sets the position of the watermark on the input image. Valid values: ``topleft``, - ``top``, ``topright``, ``left``, ``center``, ``right``, ``bottomleft``, ``bottom``, and + ``top``, ``topright``, ``left``, ``centerright``, ``center``, ``centerleft``, ``right``, ``bottomleft``, ``bottom``, and ``bottomright``. .. caution:: From 627b7bd5d77ab4c54e434ca1f072ea3bec6d39d1 Mon Sep 17 00:00:00 2001 From: Cedric Ziel Date: Mon, 28 Aug 2017 13:24:01 +0200 Subject: [PATCH 20/27] Use more intuitive order of positions --- Resources/doc/filters/general.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/doc/filters/general.rst b/Resources/doc/filters/general.rst index c6815858e..e1a87d7e2 100644 --- a/Resources/doc/filters/general.rst +++ b/Resources/doc/filters/general.rst @@ -203,7 +203,7 @@ Watermark Options :strong:`position:` ``string`` Sets the position of the watermark on the input image. Valid values: ``topleft``, - ``top``, ``topright``, ``left``, ``centerright``, ``center``, ``centerleft``, ``right``, ``bottomleft``, ``bottom``, and + ``top``, ``topright``, ``left``, ``centerleft``, ``center``, ``centerright``, ``right``, ``bottomleft``, ``bottom``, and ``bottomright``. .. caution:: From 1c9811ef99c4096d36ec6e0b23e2d7020078ff9f Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Thu, 27 Jul 2017 07:31:46 -0400 Subject: [PATCH 21/27] cleanup cache resolve command, add forced resolve, prittify and detail output --- Command/RemoveCacheCommand.php | 44 +- Command/ResolveCacheCommand.php | 200 ++++++--- .../Command/AbstractCommandTestCase.php | 7 +- Tests/Functional/Command/RemoveCacheTest.php | 26 +- Tests/Functional/Command/ResolveCacheTest.php | 380 +++++++++++++++--- 5 files changed, 526 insertions(+), 131 deletions(-) diff --git a/Command/RemoveCacheCommand.php b/Command/RemoveCacheCommand.php index c08824067..aa8d53514 100644 --- a/Command/RemoveCacheCommand.php +++ b/Command/RemoveCacheCommand.php @@ -26,12 +26,10 @@ protected function configure() ->setName('liip:imagine:cache:remove') ->setDescription('Remove cache for given paths and set of filters.') ->addArgument('paths', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Image paths') - ->addOption( - 'filters', - 'f', - InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'Filters list' - ) + ->addOption('filters', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'List of filters to remove for passed images (Deprecated, use "filter").') + ->addOption('filter', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'List of filters to remove for passed images.') ->setHelp(<<<'EOF' The %command.name% command removes cache by specified parameters. @@ -39,12 +37,12 @@ protected function configure() php app/console %command.name% path1 path2 All cache for a given `paths` will be lost. -If you use --filters parameter: -php app/console %command.name% --filters=thumb1 --filters=thumb2 +If you use --filter parameter: +php app/console %command.name% --filter=thumb1 --filter=thumb2 All cache for a given filters will be lost. You can combine these parameters: -php app/console %command.name% path1 path2 --filters=thumb1 --filters=thumb2 +php app/console %command.name% path1 path2 --filter=thumb1 --filter=thumb2 php app/console %command.name% Cache for all paths and filters will be lost when executing this command without parameters. @@ -52,10 +50,16 @@ protected function configure() ); } + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return int + */ protected function execute(InputInterface $input, OutputInterface $output) { $paths = $input->getArgument('paths'); - $filters = $input->getOption('filters'); + $filters = $this->resolveInputFilters($input); if (empty($filters)) { $filters = null; @@ -63,7 +67,25 @@ protected function execute(InputInterface $input, OutputInterface $output) /* @var CacheManager cacheManager */ $cacheManager = $this->getContainer()->get('liip_imagine.cache.manager'); - $cacheManager->remove($paths, $filters); + + return 0; + } + + /** + * @param InputInterface $input + * + * @return array|mixed + */ + private function resolveInputFilters(InputInterface $input) + { + $filters = $input->getOption('filter'); + + if (count($filtersDeprecated = $input->getOption('filters'))) { + $filters = array_merge($filters, $filtersDeprecated); + @trigger_error('As of 1.9, use of the "--filters" option has been deprecated in favor of "--filter" and will be removed in 2.0.', E_USER_DEPRECATED); + } + + return $filters; } } diff --git a/Command/ResolveCacheCommand.php b/Command/ResolveCacheCommand.php index 8aa69a8ba..0b3209cb3 100644 --- a/Command/ResolveCacheCommand.php +++ b/Command/ResolveCacheCommand.php @@ -27,67 +27,177 @@ protected function configure() $this ->setName('liip:imagine:cache:resolve') ->setDescription('Resolve cache for given path and set of filters.') - ->addArgument('paths', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Image paths') - ->addOption( - 'filters', - 'f', - InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'Filters list' - )->setHelp(<<<'EOF' -The %command.name% command resolves cache by specified parameters. -It returns list of urls. - -php app/console %command.name% path1 path2 --filters=thumb1 -Cache for this two paths will be resolved with passed filter. -As a result you will get - http://localhost/media/cache/thumb1/path1 - http://localhost/media/cache/thumb1/path2 - -You can pass few filters: -php app/console %command.name% path1 --filters=thumb1 --filters=thumb2 -As a result you will get - http://localhost/media/cache/thumb1/path1 - http://localhost/media/cache/thumb2/path1 - -If you omit --filters parameter then to resolve given paths will be used all configured and available filters in application: -php app/console %command.name% path1 -As a result you will get - http://localhost/media/cache/thumb1/path1 - http://localhost/media/cache/thumb2/path1 + ->addArgument('paths', InputArgument::REQUIRED | InputArgument::IS_ARRAY, + 'Any number of image paths to act on.') + ->addOption('filters', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'List of filters to apply to passed images (Deprecated, use "filter").') + ->addOption('filter', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'List of filters to apply to passed images.') + ->addOption('force', 'F', InputOption::VALUE_NONE, + 'Force image resolution regardless of cache.') + ->addOption('as-script', 's', InputOption::VALUE_NONE, + 'Only print machine-parseable results.') + ->setHelp(<<<'EOF' +The %command.name% command resolves the passed image(s) for the resolved +filter(s), outputting results using the following basic format: + - "image.ext[filter]" (resolved|cached|failed) as "path/to/cached/image.ext" + +# bin/console %command.name% --filter=thumb1 foo.ext bar.ext +Resolve both foo.ext and bar.ext using thumb1 filter, outputting: + - "foo.ext[thumb1]" resolved as "http://localhost/media/cache/thumb1/foo.ext" + - "bar.ext[thumb1]" resolved as "http://localhost/media/cache/thumb1/bar.ext" + +# bin/console %command.name% --filter=thumb1 --filter=thumb2 foo.ext +Resolve foo.ext using both thumb1 and thumb2 filters, outputting: + - "foo.ext[thumb1]" resolved as "http://localhost/media/cache/thumb1/foo.ext" + - "foo.ext[thumb2]" resolved as "http://localhost/media/cache/thumb2/foo.ext" + +# bin/console %command.name% foo.ext +Resolve foo.ext using all configured filters (as none are specified), outputting: + - "foo.ext[thumb1]" resolved as "http://localhost/media/cache/thumb1/foo.ext" + - "foo.ext[thumb2]" resolved as "http://localhost/media/cache/thumb2/foo.ext" + +# bin/console %command.name% --force --filter=thumb1 foo.ext +Resolve foo.ext using thumb1 and force creation regardless of cache, outputting: + - "foo.ext[thumb1]" resolved as "http://localhost/media/cache/thumb1/foo.ext" + EOF ); } protected function execute(InputInterface $input, OutputInterface $output) { + $force = $input->getOption('force'); $paths = $input->getArgument('paths'); - $filters = $input->getOption('filters'); + $filters = $this->resolveInputFilters($input); + $machine = $input->getOption('as-script'); + $failed = 0; - /* @var FilterManager filterManager */ - $filterManager = $this->getContainer()->get('liip_imagine.filter.manager'); - /* @var CacheManager cacheManager */ - $cacheManager = $this->getContainer()->get('liip_imagine.cache.manager'); - /* @var DataManager dataManager */ - $dataManager = $this->getContainer()->get('liip_imagine.data.manager'); + $filterManager = $this->getFilterManager(); + $dataManager = $this->getDataManager(); + $cacheManager = $this->getCacheManager(); - if (empty($filters)) { + if (0 === count($filters)) { $filters = array_keys($filterManager->getFilterConfiguration()->all()); } + $this->outputTitle($output, $machine); + foreach ($paths as $path) { foreach ($filters as $filter) { - if (!$cacheManager->isStored($path, $filter)) { - $binary = $dataManager->find($filter, $path); - - $cacheManager->store( - $filterManager->applyFilter($binary, $filter), - $path, - $filter - ); - } + $output->write(sprintf('- %s[%s] ', $path, $filter)); + + try { + if ($force || !$cacheManager->isStored($path, $filter)) { + $cacheManager->store($filterManager->applyFilter($dataManager->find($filter, $path), $filter), $path, $filter); + $output->write('resolved: '); + } else { + $output->write('cached: '); + } - $output->writeln($cacheManager->resolve($path, $filter)); + $output->writeln($cacheManager->resolve($path, $filter)); + } catch (\Exception $e) { + $output->writeln(sprintf('failed: %s', $e->getMessage())); + ++$failed; + } } } + + $this->outputSummary($output, $machine, count($filters), count($paths), $failed); + + return 0 === $failed ? 0 : 255; + } + + /** + * @param OutputInterface $output + * @param bool $machine + */ + private function outputTitle(OutputInterface $output, $machine) + { + if (!$machine) { + $title = '[liip/imagine-bundle] Image Resolver'; + + $output->writeln(sprintf('%s', $title)); + $output->writeln(str_repeat('=', strlen($title))); + $output->writeln(''); + } + } + + /** + * @param OutputInterface $output + * @param bool $machine + * @param int $filters + * @param int $paths + * @param int $failed + */ + private function outputSummary(OutputInterface $output, $machine, $filters, $paths, $failed) + { + if (!$machine) { + $operations = ($filters * $paths) - $failed; + + $output->writeln(''); + $output->writeln(vsprintf('Completed %d %s (%d %s on %d %s) %s', array( + $operations, + $this->pluralizeWord($operations, 'operation'), + $filters, + $this->pluralizeWord($filters, 'filter'), + $paths, + $this->pluralizeWord($paths, 'image'), + 0 === $failed ? '' : sprintf('[encountered %d %s]', $failed, $this->pluralizeWord($failed, 'failure')), + ))); + } + } + + /** + * @param int $count + * @param string $singular + * @param string $pluralEnding + * + * @return string + */ + private function pluralizeWord($count, $singular, $pluralEnding = 's') + { + return 1 === $count ? $singular : $singular.$pluralEnding; + } + + /** + * @param InputInterface $input + * + * @return array|mixed + */ + private function resolveInputFilters(InputInterface $input) + { + $filters = $input->getOption('filter'); + + if (count($filtersDeprecated = $input->getOption('filters'))) { + $filters = array_merge($filters, $filtersDeprecated); + @trigger_error('As of 1.9, use of the "--filters" option has been deprecated in favor of "--filter" and will be removed in 2.0.', E_USER_DEPRECATED); + } + + return $filters; + } + + /** + * @return FilterManager + */ + private function getFilterManager() + { + return $this->getContainer()->get('liip_imagine.filter.manager'); + } + + /** + * @return DataManager + */ + private function getDataManager() + { + return $this->getContainer()->get('liip_imagine.data.manager'); + } + + /** + * @return CacheManager + */ + private function getCacheManager() + { + return $this->getContainer()->get('liip_imagine.cache.manager'); } } diff --git a/Tests/Functional/Command/AbstractCommandTestCase.php b/Tests/Functional/Command/AbstractCommandTestCase.php index c620f9df5..35e56d8a6 100644 --- a/Tests/Functional/Command/AbstractCommandTestCase.php +++ b/Tests/Functional/Command/AbstractCommandTestCase.php @@ -22,11 +22,11 @@ class AbstractCommandTestCase extends AbstractSetupWebTestCase /** * @param Command $command * @param array $arguments - * @param array $options + * @param int $return * * @return string */ - protected function executeConsole(Command $command, array $arguments = array(), array $options = array()) + protected function executeConsole(Command $command, array $arguments = array(), &$return = null) { $command->setApplication(new Application($this->createClient()->getKernel())); if ($command instanceof ContainerAwareCommand) { @@ -34,10 +34,9 @@ protected function executeConsole(Command $command, array $arguments = array(), } $arguments = array_replace(array('command' => $command->getName()), $arguments); - $options = array_replace(array('--env' => 'test'), $options); $commandTester = new CommandTester($command); - $commandTester->execute($arguments, $options); + $return = $commandTester->execute($arguments, array('--env' => 'test')); return $commandTester->getDisplay(); } diff --git a/Tests/Functional/Command/RemoveCacheTest.php b/Tests/Functional/Command/RemoveCacheTest.php index ccf00bbf0..bd98777f2 100644 --- a/Tests/Functional/Command/RemoveCacheTest.php +++ b/Tests/Functional/Command/RemoveCacheTest.php @@ -33,7 +33,7 @@ public function testExecuteSuccessfullyWithEmptyCacheAndOnePathAndOneFilter() new RemoveCacheCommand(), array( 'paths' => array('images/cats.jpeg'), - '--filters' => array('thumbnail_web_path'), + '--filter' => array('thumbnail_web_path'), )); } @@ -53,7 +53,7 @@ public function testExecuteSuccessfullyWithEmptyCacheAndMultipleFilters() $this->executeConsole( new RemoveCacheCommand(), - array('--filters' => array('thumbnail_web_path', 'thumbnail_default')) + array('--filter' => array('thumbnail_web_path', 'thumbnail_default')) ); } @@ -160,7 +160,7 @@ public function testShouldRemoveCacheBySingleFilter() $this->executeConsole( new RemoveCacheCommand(), - array('--filters' => array('thumbnail_default')) + array('--filter' => array('thumbnail_default')) ); $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); @@ -190,7 +190,7 @@ public function testShouldRemoveCacheByMultipleFilters() $this->executeConsole( new RemoveCacheCommand(), - array('--filters' => array('thumbnail_default', 'thumbnail_web_path')) + array('--filter' => array('thumbnail_default', 'thumbnail_web_path')) ); $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); @@ -218,7 +218,7 @@ public function testShouldRemoveCacheByOnePathAndMultipleFilters() new RemoveCacheCommand(), array( 'paths' => array('images/cats.jpeg'), - '--filters' => array('thumbnail_default', 'thumbnail_web_path'), ) + '--filter' => array('thumbnail_default', 'thumbnail_web_path'), ) ); $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); @@ -245,11 +245,25 @@ public function testShouldRemoveCacheByMultiplePathsAndSingleFilter() new RemoveCacheCommand(), array( 'paths' => array('images/cats.jpeg', 'images/cats2.jpeg'), - '--filters' => array('thumbnail_web_path'), ) + '--filter' => array('thumbnail_web_path'), ) ); $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg'); $this->assertFileExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); } + + /** + * @group legacy + * @expectedDeprecation As of 1.9, use of the "--filters" option has been deprecated in favor of "--filter" and will be removed in 2.0. + */ + public function testDeprecatedFiltersOption() + { + $this->executeConsole( + new RemoveCacheCommand(), + array( + 'paths' => array('images/cats.jpeg', 'images/cats2.jpeg'), + '--filters' => array('thumbnail_web_path'), ) + ); + } } diff --git a/Tests/Functional/Command/ResolveCacheTest.php b/Tests/Functional/Command/ResolveCacheTest.php index b0437e002..822944812 100644 --- a/Tests/Functional/Command/ResolveCacheTest.php +++ b/Tests/Functional/Command/ResolveCacheTest.php @@ -20,100 +20,350 @@ class ResolveCacheTest extends AbstractCommandTestCase { public function testShouldResolveWithEmptyCache() { - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); + $images = array('images/cats.jpeg', 'images/cats2.jpeg'); + $filters = array('thumbnail_web_path'); - $output = $this->executeConsole( - new ResolveCacheCommand(), - array( - 'paths' => array('images/cats.jpeg'), - '--filters' => array('thumbnail_web_path'), ) - ); + $this->assertImagesNotExist($images, $filters); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); - $this->assertContains('http://localhost/media/cache/thumbnail_web_path/images/cats.jpeg', $output); + $return = null; + $output = $this->executeResolveCacheCommand($images, $filters, array(), $return); + + $this->assertSame(0, $return); + $this->assertImagesExist($images, $filters); + $this->assertImagesNotExist($images, array('thumbnail_default')); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); } public function testShouldResolveWithCacheExists() { - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg', - 'anImageContent' - ); + $images = array('images/cats.jpeg'); + $filters = array('thumbnail_web_path'); + + $this->putResolvedImages($images, $filters); - $output = $this->executeConsole( - new ResolveCacheCommand(), - array( - 'paths' => array('images/cats.jpeg'), - '--filters' => array('thumbnail_web_path'), ) - ); + $output = $this->executeResolveCacheCommand($images, $filters); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); - $this->assertContains('http://localhost/media/cache/thumbnail_web_path/images/cats.jpeg', $output); + $this->assertImagesExist($images, $filters); + $this->assertImagesNotExist($images, array('thumbnail_default')); + $this->assertOutputContainsCachedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); } public function testShouldResolveWithFewPathsAndSingleFilter() { - $output = $this->executeConsole( - new ResolveCacheCommand(), - array( - 'paths' => array('images/cats.jpeg', 'images/cats2.jpeg'), - '--filters' => array('thumbnail_web_path'), ) - ); + $images = array('images/cats.jpeg', 'images/cats2.jpeg'); + $filters = array('thumbnail_web_path'); + + $output = $this->executeResolveCacheCommand($images, $filters); - $this->assertContains('http://localhost/media/cache/thumbnail_web_path/images/cats.jpeg', $output); - $this->assertContains('http://localhost/media/cache/thumbnail_web_path/images/cats2.jpeg', $output); + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + + $output = $this->executeResolveCacheCommand($images, $filters); + + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsCachedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); } public function testShouldResolveWithFewPathsSingleFilterAndPartiallyFullCache() { - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); + $imagesResolved = array('images/cats.jpeg'); + $imagesCached = array('images/cats2.jpeg'); + $images = array_merge($imagesResolved, $imagesCached); + $filters = array('thumbnail_web_path'); + + $this->putResolvedImages($imagesCached, $filters); + + $this->assertImagesNotExist($imagesResolved, $filters); + $this->assertImagesExist($imagesCached, $filters); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg', - 'anImageContent' - ); + $output = $this->executeResolveCacheCommand($images, $filters); - $output = $this->executeConsole( - new ResolveCacheCommand(), - array( - 'paths' => array('images/cats.jpeg', 'images/cats2.jpeg'), - '--filters' => array('thumbnail_web_path'), ) - ); + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsResolvedImages($output, $imagesResolved, $filters); + $this->assertOutputContainsCachedImages($output, $imagesCached, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg'); - $this->assertContains('http://localhost/media/cache/thumbnail_web_path/images/cats.jpeg', $output); - $this->assertContains('http://localhost/media/cache/thumbnail_web_path/images/cats2.jpeg', $output); + $this->delResolvedImages($images, $filters); } public function testShouldResolveWithFewPathsAndFewFilters() { - $output = $this->executeConsole( - new ResolveCacheCommand(), - array( - 'paths' => array('images/cats.jpeg', 'images/cats2.jpeg'), - '--filters' => array('thumbnail_web_path', 'thumbnail_default'), ) - ); + $images = array('images/cats.jpeg', 'images/cats2.jpeg'); + $filters = array('thumbnail_web_path', 'thumbnail_default'); - $this->assertContains('http://localhost/media/cache/thumbnail_web_path/images/cats.jpeg', $output); - $this->assertContains('http://localhost/media/cache/thumbnail_web_path/images/cats2.jpeg', $output); - $this->assertContains('http://localhost/media/cache/thumbnail_default/images/cats.jpeg', $output); - $this->assertContains('http://localhost/media/cache/thumbnail_default/images/cats2.jpeg', $output); + $output = $this->executeResolveCacheCommand($images, $filters); + + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); } public function testShouldResolveWithFewPathsAndWithoutFilters() { - $output = $this->executeConsole( - new ResolveCacheCommand(), - array('paths' => array('images/cats.jpeg', 'images/cats2.jpeg')) - ); + $images = array('images/cats.jpeg', 'images/cats2.jpeg'); + $filters = array('thumbnail_web_path', 'thumbnail_default'); + + $output = $this->executeResolveCacheCommand($images); + + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); + } + + public function testCachedAndForceResolve() + { + $images = array('images/cats.jpeg', 'images/cats2.jpeg'); + $filters = array('thumbnail_web_path', 'thumbnail_default'); + + $this->assertImagesNotExist($images, $filters); + $this->putResolvedImages($images, $filters); + $this->assertImagesExist($images, $filters); + + $output = $this->executeResolveCacheCommand($images, $filters); + + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsCachedImages($output, $images, $filters); + + $output = $this->executeResolveCacheCommand($images, $filters, array('--force' => true)); + + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); + } + + public function testFailedResolve() + { + $images = array('images/cats.jpeg', 'images/cats2.jpeg'); + $filters = array('does_not_exist'); + + $this->assertImagesNotExist($images, $filters); + + $return = null; + $output = $this->executeResolveCacheCommand($images, $filters, array(), $return); + + $this->assertSame(255, $return); + $this->assertImagesNotExist($images, $filters); + $this->assertOutputContainsFailedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters, 2); + + $this->delResolvedImages($images, $filters); + } + + public function testMachineOption() + { + $images = array('images/cats.jpeg', 'images/cats2.jpeg'); + $filters = array('does_not_exist'); + + $this->assertImagesNotExist($images, $filters); - $this->assertContains('http://localhost/media/cache/thumbnail_web_path/images/cats.jpeg', $output); - $this->assertContains('http://localhost/media/cache/thumbnail_web_path/images/cats2.jpeg', $output); - $this->assertContains('http://localhost/media/cache/thumbnail_default/images/cats.jpeg', $output); - $this->assertContains('http://localhost/media/cache/thumbnail_default/images/cats2.jpeg', $output); + $output = $this->executeResolveCacheCommand($images, $filters, array('--as-script' => true)); + + $this->assertImagesNotExist($images, $filters); + $this->assertNotContains('liip/imagine-bundle (cache resolver)', $output); + $this->assertNotContains('====================================', $output); + $this->assertOutputContainsFailedImages($output, $images, $filters); + $this->assertOutputNotContainsSummary($output, $images, $filters, 2); + + $this->delResolvedImages($images, $filters); + } + + /** + * @group legacy + * @expectedDeprecation As of 1.9, use of the "--filters" option has been deprecated in favor of "--filter" and will be removed in 2.0. + */ + public function testDeprecatedFiltersOption() + { + $images = array('images/cats.jpeg'); + $filters = array('thumbnail_web_path'); + + $this->assertImagesNotExist($images, $filters); + + $output = $this->executeConsole(new ResolveCacheCommand(), array('paths' => $images, '--filters' => $filters)); + + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + + $this->delResolvedImages($images, $filters); + } + + /** + * @param string[] $paths + * @param string[] $filters + * @param string[] $additionalOptions + * @param int $return + * + * @return string + */ + private function executeResolveCacheCommand(array $paths, array $filters = array(), array $additionalOptions = array(), &$return = null) + { + $options = array_merge(array('paths' => $paths), $additionalOptions); + + if (0 < count($filters)) { + $options['--filter'] = $filters; + } + + return $this->executeConsole(new ResolveCacheCommand(), $options, $return); + } + + /** + * @param string[] $images + * @param string[] $filters + */ + private function assertImagesNotExist($images, $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertFileNotExists(sprintf('%s/%s/%s', $this->cacheRoot, $f, $i)); + } + } + } + + /** + * @param string[] $images + * @param string[] $filters + */ + private function assertImagesExist($images, $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertFileExists(sprintf('%s/%s/%s', $this->cacheRoot, $f, $i)); + } + } + } + + /** + * @param string $output + * @param array $images + * @param array $filters + */ + private function assertOutputContainsResolvedImages($output, array $images, array $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertOutputContainsImage($output, $i, $f, 'resolved'); + } + } + } + + /** + * @param string $output + * @param array $images + * @param array $filters + */ + private function assertOutputContainsCachedImages($output, array $images, array $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertOutputContainsImage($output, $i, $f, 'cached'); + } + } + } + + /** + * @param string $output + * @param array $images + * @param array $filters + */ + private function assertOutputContainsFailedImages($output, array $images, array $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertContains(sprintf('%s[%s] failed: ', $i, $f), $output); + } + } + } + + /** + * @param string $output + * @param string $image + * @param string $filter + * @param string $type + */ + private function assertOutputContainsImage($output, $image, $filter, $type) + { + $expected = vsprintf('%s[%s] %s: http://localhost/media/cache/%s/%s', array( + $image, + $filter, + $type, + $filter, + $image, + )); + $this->assertContains($expected, $output); + } + + /** + * @param string $output + * @param string[] $images + * @param string[] $filters + * @param int $failures + */ + private function assertOutputContainsSummary($output, array $images, array $filters, $failures = 0) + { + $this->assertContains(sprintf('Completed %d operation', (count($images) * count($filters)) - $failures), $output); + $this->assertContains(sprintf('%d image', count($images)), $output); + $this->assertContains(sprintf('%d filter', count($filters)), $output); + if (0 !== $failures) { + $this->assertContains(sprintf('%d failure', $failures), $output); + } + } + + /** + * @param string $output + * @param string[] $images + * @param string[] $filters + * @param int $failures + */ + private function assertOutputNotContainsSummary($output, array $images, array $filters, $failures = 0) + { + $this->assertNotContains(sprintf('Completed %d operation', (count($images) * count($filters)) - $failures), $output); + $this->assertNotContains(sprintf('%d image', count($images)), $output); + $this->assertNotContains(sprintf('%d filter', count($filters)), $output); + if (0 !== $failures) { + $this->assertNotContains(sprintf('%d failure', $failures), $output); + } + } + + /** + * @param string[] $images + * @param string[] $filters + */ + private function delResolvedImages(array $images, array $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + if (file_exists($f = sprintf('%s/%s/%s', $this->cacheRoot, $f, $i))) { + @unlink($f); + } + } + } + } + + /** + * @param string[] $images + * @param string[] $filters + * @param string $content + */ + private function putResolvedImages(array $images, array $filters, $content = 'anImageContent') + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->filesystem->dumpFile(sprintf('%s/%s/%s', $this->cacheRoot, $f, $i), $content); + } + } } } From 78c0dd39d0594262dba8665b2d85cd09b927e3e4 Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Wed, 30 Aug 2017 21:26:18 -0400 Subject: [PATCH 22/27] fix filesystem loader deprecation message --- Tests/Binary/Loader/FileSystemLoaderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Binary/Loader/FileSystemLoaderTest.php b/Tests/Binary/Loader/FileSystemLoaderTest.php index 5e7508bf9..67ff44087 100644 --- a/Tests/Binary/Loader/FileSystemLoaderTest.php +++ b/Tests/Binary/Loader/FileSystemLoaderTest.php @@ -83,7 +83,7 @@ public function testLoad($root, $path) * @dataProvider provideLoadCases * * @group legacy - * @expectedDeprecation Method %s() will have a forth `LocatorInterface $locator` argument in version 2.0. Not defining it is deprecated since version 1.7.2 + * @expectedDeprecation Method %s::__construct() will expect the third parameter to be a LocatorInterface in version 2.0. Defining data roots instead is deprecated since version 1.9.0 * * @param string $root * @param string $path From a3bde59e1de60b6bd7ce0ed5f9ccca1da1d7d7ef Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Wed, 30 Aug 2017 22:06:34 -0400 Subject: [PATCH 23/27] ready 1.9.0 release --- CHANGELOG.md | 23 ++++++++++++++++++++++- UPGRADE.md | 3 +++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 999714d48..4c1f75133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,28 @@ # Change Log ## [Unreleased](https://github.com/liip/LiipImagineBundle/tree/HEAD) (2017-xx-xx) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.8.0...HEAD) +[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.9.0...HEAD) + +## [1.9.0](https://github.com/liip/LiipImagineBundle/tree/1.9.0) (2017-08-30) +[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.8.0...1.9.0) + +- \[Data Loader\] \[Tests\] Fix filesystem loader deprecation message in tests [\#982](https://github.com/liip/LiipImagineBundle/pull/982) ([robfrawley](https://github.com/robfrawley)) +- \[Data Loader\] Add "centerright" and "centerleft" positions to background filter loader [\#974](https://github.com/liip/LiipImagineBundle/pull/974) ([cmodijk](https://github.com/cmodijk)) +- \[Controller\] Allow to configure the HTTP response code for redirects [\#970](https://github.com/liip/LiipImagineBundle/pull/970) ([lstrojny](https://github.com/lstrojny)) +- \[Console\] Add forced resolve to command and prittify and detail output [\#967](https://github.com/liip/LiipImagineBundle/pull/967) ([robfrawley](https://github.com/robfrawley)) +- \[CS\] Fix two docblock annotations [\#965](https://github.com/liip/LiipImagineBundle/pull/965) ([imanalopher](https://github.com/imanalopher)) +- \[Data Loader\] Pass root paths to file system locator constructor (instead of loader) [\#963](https://github.com/liip/LiipImagineBundle/pull/963/) ([robfrawley](https://github.com/robfrawley), [rpkamp](https://github.com/rpkamp)) +- \[Composer\] Allow imagine-library version 0.7.0 [\#958](https://github.com/liip/LiipImagineBundle/pull/958) ([robfrawley](https://github.com/robfrawley)) +- \[Data Loader\] \[Documentation\] Add chain loader documentation [\#957](https://github.com/liip/LiipImagineBundle/pull/957) ([robfrawley](https://github.com/robfrawley)) +- \[Data Loader\] Add chain loader implementation [\#953](https://github.com/liip/LiipImagineBundle/pull/953) ([robfrawley](https://github.com/robfrawley)) +- \[CS\] Fix templating extension "filter" return type [\#951](https://github.com/liip/LiipImagineBundle/pull/951) ([imanalopher](https://github.com/imanalopher)) +- \[Dependency Injection\] Fix compiler pass log message typo [\#947](https://github.com/liip/LiipImagineBundle/pull/947) ([you-ser](https://github.com/you-ser)) +- \[Travis\] Default to trusty container image (with precise image for php 5.3) [\#945](https://github.com/liip/LiipImagineBundle/pull/945) ([robfrawley](https://github.com/robfrawley)) +- \[Enqueue\] Use simplified transport configuration [\#942](https://github.com/liip/LiipImagineBundle/pull/942) ([makasim](https://github.com/makasim)) +- \[Filter\] Add resolution loader implementation [\#941](https://github.com/liip/LiipImagineBundle/pull/941) ([robfrawley](https://github.com/robfrawley)) +- \[Travis\] Remove Symfony 3.3 from allowed failures [\#940](https://github.com/liip/LiipImagineBundle/pull/940) ([robfrawley](https://github.com/robfrawley)) +- \[Utility\] \[CS\] Use simplified symfony version comparison and minor cs fixes [\#939](https://github.com/liip/LiipImagineBundle/pull/939) ([robfrawley](https://github.com/robfrawley)) + ## [1.8.0](https://github.com/liip/LiipImagineBundle/tree/1.8.0) (2017-05-08) [Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.4...1.8.0) diff --git a/UPGRADE.md b/UPGRADE.md index 8cbea6cea..11df0b01a 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -6,6 +6,9 @@ as the third parameter and an (optional) `LocatorInterace` as the fourth parameter is deprecated. A `LocatorInterface` should now be passed as third parameter, and the array of data roots to the `LocatorInterface::__construct()` method directly. All prior signatures will continue to work until `2.0` is release. + - __[Console]__ Added the `--force` parameter to resolve console command to force image resolution regardless of cache. + Added the `--as-script` parameter to resolve console command to disable verbose, "prettified" output. + - __[Composer]__ Imagine library upgraded to version 0.7.x. ## 1.8.0 From f0d636dc6fe95914bb10849d1a1739eb39e5707b Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Thu, 31 Aug 2017 01:48:34 -0400 Subject: [PATCH 24/27] reformat changelog and add lead/description to upgrade and changelog --- CHANGELOG.md | 1275 ++++++++++++++++++++++++++++---------------------- UPGRADE.md | 176 ++++--- 2 files changed, 818 insertions(+), 633 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c1f75133..529132f9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,635 +1,788 @@ -# Change Log - -## [Unreleased](https://github.com/liip/LiipImagineBundle/tree/HEAD) (2017-xx-xx) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.9.0...HEAD) - -## [1.9.0](https://github.com/liip/LiipImagineBundle/tree/1.9.0) (2017-08-30) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.8.0...1.9.0) - -- \[Data Loader\] \[Tests\] Fix filesystem loader deprecation message in tests [\#982](https://github.com/liip/LiipImagineBundle/pull/982) ([robfrawley](https://github.com/robfrawley)) -- \[Data Loader\] Add "centerright" and "centerleft" positions to background filter loader [\#974](https://github.com/liip/LiipImagineBundle/pull/974) ([cmodijk](https://github.com/cmodijk)) -- \[Controller\] Allow to configure the HTTP response code for redirects [\#970](https://github.com/liip/LiipImagineBundle/pull/970) ([lstrojny](https://github.com/lstrojny)) -- \[Console\] Add forced resolve to command and prittify and detail output [\#967](https://github.com/liip/LiipImagineBundle/pull/967) ([robfrawley](https://github.com/robfrawley)) -- \[CS\] Fix two docblock annotations [\#965](https://github.com/liip/LiipImagineBundle/pull/965) ([imanalopher](https://github.com/imanalopher)) -- \[Data Loader\] Pass root paths to file system locator constructor (instead of loader) [\#963](https://github.com/liip/LiipImagineBundle/pull/963/) ([robfrawley](https://github.com/robfrawley), [rpkamp](https://github.com/rpkamp)) -- \[Composer\] Allow imagine-library version 0.7.0 [\#958](https://github.com/liip/LiipImagineBundle/pull/958) ([robfrawley](https://github.com/robfrawley)) -- \[Data Loader\] \[Documentation\] Add chain loader documentation [\#957](https://github.com/liip/LiipImagineBundle/pull/957) ([robfrawley](https://github.com/robfrawley)) -- \[Data Loader\] Add chain loader implementation [\#953](https://github.com/liip/LiipImagineBundle/pull/953) ([robfrawley](https://github.com/robfrawley)) -- \[CS\] Fix templating extension "filter" return type [\#951](https://github.com/liip/LiipImagineBundle/pull/951) ([imanalopher](https://github.com/imanalopher)) -- \[Dependency Injection\] Fix compiler pass log message typo [\#947](https://github.com/liip/LiipImagineBundle/pull/947) ([you-ser](https://github.com/you-ser)) -- \[Travis\] Default to trusty container image (with precise image for php 5.3) [\#945](https://github.com/liip/LiipImagineBundle/pull/945) ([robfrawley](https://github.com/robfrawley)) -- \[Enqueue\] Use simplified transport configuration [\#942](https://github.com/liip/LiipImagineBundle/pull/942) ([makasim](https://github.com/makasim)) -- \[Filter\] Add resolution loader implementation [\#941](https://github.com/liip/LiipImagineBundle/pull/941) ([robfrawley](https://github.com/robfrawley)) -- \[Travis\] Remove Symfony 3.3 from allowed failures [\#940](https://github.com/liip/LiipImagineBundle/pull/940) ([robfrawley](https://github.com/robfrawley)) -- \[Utility\] \[CS\] Use simplified symfony version comparison and minor cs fixes [\#939](https://github.com/liip/LiipImagineBundle/pull/939) ([robfrawley](https://github.com/robfrawley)) - - -## [1.8.0](https://github.com/liip/LiipImagineBundle/tree/1.8.0) (2017-05-08) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.4...1.8.0) - -- \[Minor\] \[Bug\] Revert to php-cs-fixer 1.x and run fixer [\#927](https://github.com/liip/LiipImagineBundle/pull/927) ([robfrawley](https://github.com/robfrawley)) -- \[Routing\] Deprecate XML routing file in favor of YAML [\#925](https://github.com/liip/LiipImagineBundle/pull/925) ([robfrawley](https://github.com/robfrawley)) -- \[Filter\] Add flip filter implementation to core [\#920](https://github.com/liip/LiipImagineBundle/pull/920) ([robfrawley](https://github.com/robfrawley)) -- \[Queue\] Resolve image caches in background using message queue. [\#919](https://github.com/liip/LiipImagineBundle/pull/919) ([makasim](https://github.com/makasim)) - -## [1.7.4](https://github.com/liip/LiipImagineBundle/tree/1.7.4) (2017-03-01) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.3...1.7.4) - -- \[Bug\] Revert adding leading slash to S3 class names [\#893](https://github.com/liip/LiipImagineBundle/pull/893) ([cedricziel](https://github.com/cedricziel)) - -## [1.7.3](https://github.com/liip/LiipImagineBundle/tree/1.7.3) (2017-03-01) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.2...1.7.3) - -- \[Tests\] Support PHPUnit 5.x (and remove depredations) [\#887](https://github.com/liip/LiipImagineBundle/pull/887) ([robfrawley](https://github.com/robfrawley)) -- \[Tests\] Assert expected deprecation using symfony/phpunit-bridge [\#886](https://github.com/liip/LiipImagineBundle/pull/886) ([robfrawley](https://github.com/robfrawley)) -- \[Minor\] \[Docs\] Fix typo in general filters documentation [\#888](https://github.com/liip/LiipImagineBundle/pull/888) ([svenluijten](https://github.com/svenluijten)) -- \[Loader\] Add bundle resources to safe path when requested [\#883](https://github.com/liip/LiipImagineBundle/pull/883) ([bobvandevijver](https://github.com/bobvandevijver), [robfrawley](https://github.com/robfrawley)) -- \[Tests\] Enable mongo unit tests on PHP7 using "mongo" => "mongodb" extension adapter [\#882](https://github.com/liip/LiipImagineBundle/pull/882) ([robfrawley](https://github.com/robfrawley)) -- \[Loader\] \[Locator\] FileSystemLocator service must not be shared [\#875](https://github.com/liip/LiipImagineBundle/pull/875) ([robfrawley](https://github.com/liip/LiipImagineBundle/pull/875)) - -## [1.7.2](https://github.com/liip/LiipImagineBundle/tree/1.7.2) (2017-02-07) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.1...1.7.2) - -- \[Loader\] Abstract filesystem resource locator and legacy insecure locator implementation [\#866](https://github.com/liip/LiipImagineBundle/pull/866) ([robfrawley](https://github.com/robfrawley)) -- \[Minor\] \[Loader\] Fix for FileSystemLoader annotation [\#868](https://github.com/liip/LiipImagineBundle/pull/868) ([tgabi333](https://github.com/tgabi333)) -- \[DependencyInjection\] Container logging for compiler passes [\#867](https://github.com/liip/LiipImagineBundle/pull/867) ([robfrawley](https://github.com/robfrawley)) -- \[CI\] Use Prestissimo package for Travis build [\#864](https://github.com/liip/LiipImagineBundle/pull/864) ([robfrawley](https://github.com/robfrawley)) -- \[GitHub\] Add Hithub templates for issues and PRs [\#863](https://github.com/liip/LiipImagineBundle/pull/863) ([robfrawley](https://github.com/robfrawley)) -- \[Symfony\] Bug fixes and deprecation cleanup for Symfony 3.3 [\#860](https://github.com/liip/LiipImagineBundle/pull/860) ([robfrawley](https://github.com/robfrawley)) -- \[Filter\] Upscale filter should use the highest dimension to calculate ratio [\#856](https://github.com/liip/LiipImagineBundle/pull/856) ([Rattler3](https://github.com/Rattler3)) - -## [1.7.1](https://github.com/liip/LiipImagineBundle/tree/1.7.1) (2017-01-19) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.0...1.7.1) - -- Allow multiple root paths for FileSystemLoader [\#851](https://github.com/liip/LiipImagineBundle/pull/851) ([robfrawley](https://github.com/robfrawley)) -- Fix strange wording in readme [\#847](https://github.com/liip/LiipImagineBundle/pull/847) ([svenluijten](https://github.com/svenluijten)) - -## [1.7.0](https://github.com/liip/LiipImagineBundle/tree/1.7.0) (2017-01-08) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.6.0...1.7.0) - -- Set DefaultMetadataReader when ext-exif is not present [\#841](https://github.com/liip/LiipImagineBundle/pull/841) ([cedricziel](https://github.com/cedricziel)) -- Updating twig call to utilise asset\(\), to match README.md \(closes \#830\) [\#836](https://github.com/liip/LiipImagineBundle/pull/836) ([antoligy](https://github.com/antoligy)) -- Exclude "Tests" directory from classmap [\#835](https://github.com/liip/LiipImagineBundle/pull/835) ([pamil](https://github.com/pamil)) -- Require components that no longer ship with Symfony FrameworkBundle 3.2 [\#832](https://github.com/liip/LiipImagineBundle/pull/832) ([rpkamp](https://github.com/rpkamp)) -- Document how web paths are built [\#829](https://github.com/liip/LiipImagineBundle/pull/829) ([greg0ire](https://github.com/greg0ire)) -- Wrap relative path with asset\(\) Twig function [\#825](https://github.com/liip/LiipImagineBundle/pull/825) ([bocharsky-bw](https://github.com/bocharsky-bw)) -- Update data-loaders.rst [\#821](https://github.com/liip/LiipImagineBundle/pull/821) ([IllesAprod](https://github.com/IllesAprod)) -- Updating 2.0 with corrections from 1.0 [\#820](https://github.com/liip/LiipImagineBundle/pull/820) ([antoligy](https://github.com/antoligy)) -- Typo fix [\#819](https://github.com/liip/LiipImagineBundle/pull/819) ([redjanym](https://github.com/redjanym)) -- Fix RST indentation error in AWS S3 cache resolver documentation [\#809](https://github.com/liip/LiipImagineBundle/pull/809) ([GeoffreyHervet](https://github.com/GeoffreyHervet)) -- Update basic-usage.rst [\#805](https://github.com/liip/LiipImagineBundle/pull/805) ([you-ser](https://github.com/you-ser)) -- Add data\_loader config to doc [\#803](https://github.com/liip/LiipImagineBundle/pull/803) ([davidfuhr](https://github.com/davidfuhr)) -- RST Typo Fix and Clarification for Watermark Docs [\#802](https://github.com/liip/LiipImagineBundle/pull/802) ([robfrawley](https://github.com/robfrawley)) -- Update Source to Newly Merged Style Rule Additions [\#800](https://github.com/liip/LiipImagineBundle/pull/800) ([robfrawley](https://github.com/robfrawley)) -- Bugfix: Remove Short Array Syntax and Fix \(Minor\) Recent Merge Issues [\#799](https://github.com/liip/LiipImagineBundle/pull/799) ([robfrawley](https://github.com/robfrawley)) -- Add Symfony Framework 3.1.x and 3.2-dev to build matrix [\#796](https://github.com/liip/LiipImagineBundle/pull/796) ([cedricziel](https://github.com/cedricziel)) -- Add visibility argument to service definition [\#795](https://github.com/liip/LiipImagineBundle/pull/795) ([cedricziel](https://github.com/cedricziel)) -- Bugfix for Failing Tests Introduced in \#777 [\#793](https://github.com/liip/LiipImagineBundle/pull/793) ([robfrawley](https://github.com/robfrawley)) -- Added php\_cs.dist / Updated .styleci.yml / Fixed and updated .travis.yml [\#792](https://github.com/liip/LiipImagineBundle/pull/792) ([robfrawley](https://github.com/robfrawley)) -- Add LICENSE.md [\#790](https://github.com/liip/LiipImagineBundle/pull/790) ([robfrawley](https://github.com/robfrawley)) -- Updated README.md and RST Documentation [\#789](https://github.com/liip/LiipImagineBundle/pull/789) ([robfrawley](https://github.com/robfrawley)) -- Updated CHANGELOG.md [\#788](https://github.com/liip/LiipImagineBundle/pull/788) ([robfrawley](https://github.com/robfrawley)) -- List deprecation - closes \#731 [\#787](https://github.com/liip/LiipImagineBundle/pull/787) ([antoligy](https://github.com/antoligy)) -- Cleanup FileSystemLoader \(Followup to \#775\) [\#785](https://github.com/liip/LiipImagineBundle/pull/785) ([robfrawley](https://github.com/robfrawley)) -- Tempdir for postprocessors [\#779](https://github.com/liip/LiipImagineBundle/pull/779) ([jehaby](https://github.com/jehaby)) -- Add visibility argument to flysystem resolver [\#777](https://github.com/liip/LiipImagineBundle/pull/777) ([cedricziel](https://github.com/cedricziel)) -- Amend path resolution handlers and outside root check conditional in FileSystemLoader [\#775](https://github.com/liip/LiipImagineBundle/pull/775) ([robfrawley](https://github.com/robfrawley)) -- Scale filter and Downscale and Upscale as derivatives, with a new feature [\#773](https://github.com/liip/LiipImagineBundle/pull/773) ([deviprsd21](https://github.com/deviprsd21)) -- Applied fixes from StyleCI [\#768](https://github.com/liip/LiipImagineBundle/pull/768) ([lsmith77](https://github.com/lsmith77)) -- Replaced deprecated factory\_class and factory\_method [\#767](https://github.com/liip/LiipImagineBundle/pull/767) ([rvanlaarhoven](https://github.com/rvanlaarhoven)) -- Update basic-usage.rst [\#766](https://github.com/liip/LiipImagineBundle/pull/766) ([nochecksum](https://github.com/nochecksum)) -- Implemented ConfigurablePostProcessorInterface in OptiPngPostProcessor [\#764](https://github.com/liip/LiipImagineBundle/pull/764) ([jehaby](https://github.com/jehaby)) - -## [1.6.0](https://github.com/liip/LiipImagineBundle/tree/1.6.0) (2016-07-22) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.5.3...1.6.0) - -- Input is added twice in the OptiPngProcessor. [\#762](https://github.com/liip/LiipImagineBundle/pull/762) ([antoligy](https://github.com/antoligy)) -- Enable configuration of post processors using parameters \(closes \#720\) [\#759](https://github.com/liip/LiipImagineBundle/pull/759) ([antoligy](https://github.com/antoligy)) -- Applied fixes from StyleCI [\#758](https://github.com/liip/LiipImagineBundle/pull/758) ([lsmith77](https://github.com/lsmith77)) -- Applied fixes from StyleCI [\#757](https://github.com/liip/LiipImagineBundle/pull/757) ([lsmith77](https://github.com/lsmith77)) -- Add configuration options for jpegoptim post-processor [\#756](https://github.com/liip/LiipImagineBundle/pull/756) ([dylanschoenmakers](https://github.com/dylanschoenmakers)) -- Ignore invalid exif orientations [\#751](https://github.com/liip/LiipImagineBundle/pull/751) ([lstrojny](https://github.com/lstrojny)) -- Quote strings starting '%' in YAML [\#745](https://github.com/liip/LiipImagineBundle/pull/745) ([jaikdean](https://github.com/jaikdean)) -- Fix tempnam usages [\#723](https://github.com/liip/LiipImagineBundle/pull/723) ([1ed](https://github.com/1ed)) -- background filter: allow image positioning [\#721](https://github.com/liip/LiipImagineBundle/pull/721) ([uvoelkel](https://github.com/uvoelkel)) -- Add Flysystem resolver [\#715](https://github.com/liip/LiipImagineBundle/pull/715) ([cedricziel](https://github.com/cedricziel)) -- Downscale filter scales an image to fit bounding box [\#696](https://github.com/liip/LiipImagineBundle/pull/696) ([aminin](https://github.com/aminin)) -- Implement Imagine Grayscale filter [\#638](https://github.com/liip/LiipImagineBundle/pull/638) ([gregumo](https://github.com/gregumo)) - -## [1.5.3](https://github.com/liip/LiipImagineBundle/tree/1.5.3) (2016-05-06) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.5.2...1.5.3) - -- add @Event annotation to let IDEs known event names and class instance [\#732](https://github.com/liip/LiipImagineBundle/pull/732) ([Haehnchen](https://github.com/Haehnchen)) -- Introduce mozjpeg and pngquant post-processors, add transform options. [\#717](https://github.com/liip/LiipImagineBundle/pull/717) ([antoligy](https://github.com/antoligy)) -- StreamLoader-exception-arguments [\#714](https://github.com/liip/LiipImagineBundle/pull/714) ([antonsmolin](https://github.com/antonsmolin)) - -## [1.5.2](https://github.com/liip/LiipImagineBundle/tree/1.5.2) (2016-02-16) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.5.1...1.5.2) - -- Revert "Merge pull request \#699 from jockri/fix-background-filter" [\#709](https://github.com/liip/LiipImagineBundle/pull/709) ([mangelsnc](https://github.com/mangelsnc)) - -## [1.5.1](https://github.com/liip/LiipImagineBundle/tree/1.5.1) (2016-02-13) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.5.0...1.5.1) - -- Fix regression introduced in bb8e4109672902e37931e0a491ff55ebac93d8e9 [\#707](https://github.com/liip/LiipImagineBundle/pull/707) ([Seldaek](https://github.com/Seldaek)) - -## [1.5.0](https://github.com/liip/LiipImagineBundle/tree/1.5.0) (2016-02-12) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.4.3...1.5.0) - -- Applied fixes from StyleCI [\#706](https://github.com/liip/LiipImagineBundle/pull/706) ([lsmith77](https://github.com/lsmith77)) -- Add FileBinary\[Interface\] to support large files without loading them in memory unnecessarily [\#705](https://github.com/liip/LiipImagineBundle/pull/705) ([Seldaek](https://github.com/Seldaek)) -- Fix background filter [\#699](https://github.com/liip/LiipImagineBundle/pull/699) ([jockri](https://github.com/jockri)) -- Fix undeclared variable [\#697](https://github.com/liip/LiipImagineBundle/pull/697) ([tifabien](https://github.com/tifabien)) -- Update WebPathResolver.php [\#695](https://github.com/liip/LiipImagineBundle/pull/695) ([gonzalovilaseca](https://github.com/gonzalovilaseca)) -- Add missing link to the filters doc [\#694](https://github.com/liip/LiipImagineBundle/pull/694) ([bocharsky-bw](https://github.com/bocharsky-bw)) -- Adding optipng post transformer [\#692](https://github.com/liip/LiipImagineBundle/pull/692) ([gouaille](https://github.com/gouaille)) - -## [1.4.3](https://github.com/liip/LiipImagineBundle/tree/1.4.3) (2016-01-14) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.4.2...1.4.3) - -- Fixed build issues [\#691](https://github.com/liip/LiipImagineBundle/pull/691) ([yceruto](https://github.com/yceruto)) -- Fixed doc errors reported by docs build tool [\#690](https://github.com/liip/LiipImagineBundle/pull/690) ([javiereguiluz](https://github.com/javiereguiluz)) -- Explicit attr definition was added [\#688](https://github.com/liip/LiipImagineBundle/pull/688) ([ostretsov](https://github.com/ostretsov)) -- Flysystem support added. [\#674](https://github.com/liip/LiipImagineBundle/pull/674) ([graundas](https://github.com/graundas)) - -## [1.4.2](https://github.com/liip/LiipImagineBundle/tree/1.4.2) (2015-12-29) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.4.1...1.4.2) - -- Proxy resolver allow find and replace and regexp strategies [\#687](https://github.com/liip/LiipImagineBundle/pull/687) ([makasim](https://github.com/makasim)) -- added contributing docs [\#681](https://github.com/liip/LiipImagineBundle/pull/681) ([helios-ag](https://github.com/helios-ag)) -- rebased commands document patch, see \#533 [\#680](https://github.com/liip/LiipImagineBundle/pull/680) ([helios-ag](https://github.com/helios-ag)) - -## [1.4.1](https://github.com/liip/LiipImagineBundle/tree/1.4.1) (2015-12-27) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.4.0...1.4.1) - -- Aws sdk v3 [\#685](https://github.com/liip/LiipImagineBundle/pull/685) ([makasim](https://github.com/makasim)) - -## [1.4.0](https://github.com/liip/LiipImagineBundle/tree/1.4.0) (2015-12-27) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.3.3...1.4.0) - -- \[resolver\] Add ability to force resolver. [\#684](https://github.com/liip/LiipImagineBundle/pull/684) ([makasim](https://github.com/makasim)) - -## [1.3.3](https://github.com/liip/LiipImagineBundle/tree/1.3.3) (2015-12-27) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.3.2...1.3.3) - -- Destruct image to cleanup memory [\#682](https://github.com/liip/LiipImagineBundle/pull/682) ([cmodijk](https://github.com/cmodijk)) - -## [1.3.2](https://github.com/liip/LiipImagineBundle/tree/1.3.2) (2015-12-10) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.3.1...1.3.2) - -- Removed UrlGenerator deprecations from symfony 2.8 [\#673](https://github.com/liip/LiipImagineBundle/pull/673) ([sebastianblum](https://github.com/sebastianblum)) -- Typo [\#668](https://github.com/liip/LiipImagineBundle/pull/668) ([benoitMariaux](https://github.com/benoitMariaux)) -- Misc. fixes and improvements to the docs [\#667](https://github.com/liip/LiipImagineBundle/pull/667) ([javiereguiluz](https://github.com/javiereguiluz)) -- skip MongoDB ODM related tests on PHP7 and HHVM [\#659](https://github.com/liip/LiipImagineBundle/pull/659) ([lsmith77](https://github.com/lsmith77)) -- Fix all test fails in master \(just to check\) [\#658](https://github.com/liip/LiipImagineBundle/pull/658) ([kamazee](https://github.com/kamazee)) -- Fix handling invalid orientation in AutoRotateFilterLoader & test exceptions [\#657](https://github.com/liip/LiipImagineBundle/pull/657) ([kamazee](https://github.com/kamazee)) -- Fix broken CacheResolver tests \(\#650\) [\#655](https://github.com/liip/LiipImagineBundle/pull/655) ([kamazee](https://github.com/kamazee)) -- - Task: correctly handles all rotations, even those involving flippin… [\#654](https://github.com/liip/LiipImagineBundle/pull/654) ([Heshyo](https://github.com/Heshyo)) -- Incorporate feedback from @WouterJ for PR 651 [\#653](https://github.com/liip/LiipImagineBundle/pull/653) ([kix](https://github.com/kix)) -- Applied fixes from StyleCI [\#652](https://github.com/liip/LiipImagineBundle/pull/652) ([lsmith77](https://github.com/lsmith77)) -- Add notes on basic usage [\#651](https://github.com/liip/LiipImagineBundle/pull/651) ([kix](https://github.com/kix)) -- Fix travis php version [\#649](https://github.com/liip/LiipImagineBundle/pull/649) ([Koc](https://github.com/Koc)) -- Update StreamLoader.php [\#648](https://github.com/liip/LiipImagineBundle/pull/648) ([kix](https://github.com/kix)) -- Applied fixes from StyleCI [\#646](https://github.com/liip/LiipImagineBundle/pull/646) ([lsmith77](https://github.com/lsmith77)) -- updated build matrix [\#645](https://github.com/liip/LiipImagineBundle/pull/645) ([lsmith77](https://github.com/lsmith77)) -- Fix typo [\#634](https://github.com/liip/LiipImagineBundle/pull/634) ([trsteel88](https://github.com/trsteel88)) -- Added support for special characters and white spaces in image name [\#629](https://github.com/liip/LiipImagineBundle/pull/629) ([ivanbarlog](https://github.com/ivanbarlog)) -- Updated docs for features introduced in Symfony 2.4 [\#621](https://github.com/liip/LiipImagineBundle/pull/621) ([foaly-nr1](https://github.com/foaly-nr1)) -- Use identity instead equality [\#619](https://github.com/liip/LiipImagineBundle/pull/619) ([piotrantosik](https://github.com/piotrantosik)) -- context parameter cannot be an empty string [\#618](https://github.com/liip/LiipImagineBundle/pull/618) ([aistis-](https://github.com/aistis-)) -- introduced DownscaleFilterLoader [\#610](https://github.com/liip/LiipImagineBundle/pull/610) ([sascha-meissner](https://github.com/sascha-meissner)) -## [1.3.1](https://github.com/liip/LiipImagineBundle/tree/1.3.1) (2015-08-27) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.3.0...1.3.1) +# Changelog -- Fix deprecated twig filter syntax [\#631](https://github.com/liip/LiipImagineBundle/pull/631) ([Rattler3](https://github.com/Rattler3)) -- fix invalid yaml [\#623](https://github.com/liip/LiipImagineBundle/pull/623) ([carlcraig](https://github.com/carlcraig)) -- switch to docker based travis infrastructure [\#622](https://github.com/liip/LiipImagineBundle/pull/622) ([lsmith77](https://github.com/lsmith77)) -- Return string, not Twig\_Markup object in Twig extension [\#615](https://github.com/liip/LiipImagineBundle/pull/615) ([lstrojny](https://github.com/lstrojny)) -- Use is\_file\(\) instead of Filesystem::exists\(\) [\#614](https://github.com/liip/LiipImagineBundle/pull/614) ([lstrojny](https://github.com/lstrojny)) -- Make it easier to get a dev environment up and running [\#613](https://github.com/liip/LiipImagineBundle/pull/613) ([lstrojny](https://github.com/lstrojny)) -- Fix code block into README [\#608](https://github.com/liip/LiipImagineBundle/pull/608) ([PedroTroller](https://github.com/PedroTroller)) -- fix upscale size not being calculated correctly [\#561](https://github.com/liip/LiipImagineBundle/pull/561) ([scuben](https://github.com/scuben)) +All notable alterations to this project will be documented in this +[`CHANGELOG.md`](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md) file and all important upgrade +requirements will be enumerated in the [`UPGRADE.md`](https://github.com/liip/LiipImagineBundle/blob/1.0/UPGRADE.md) file. +This project adheres to [semantic versioning](http://semver.org/spec/v2.0.0.html). -## [1.3.0](https://github.com/liip/LiipImagineBundle/tree/1.3.0) (2015-06-04) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.7...1.3.0) -- use setFactory service definition method for symfony \>= 2.6 \(when possible\) [\#566](https://github.com/liip/LiipImagineBundle/pull/566) ([adam187](https://github.com/adam187)) +## [Unreleased](https://github.com/liip/LiipImagineBundle/tree/HEAD) -## [1.2.7](https://github.com/liip/LiipImagineBundle/tree/1.2.7) (2015-06-02) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.6...1.2.7) +*Note: Recent developments can be tracked via the +[latest changelog](https://github.com/liip/LiipImagineBundle/compare/1.9.0...HEAD), the +[active milestone](https://github.com/liip/LiipImagineBundle/milestone/15), as well as all +[open milestones](https://github.com/liip/LiipImagineBundle/milestones).* -- Make AwsS3Resolver compatible with SDK v3 [\#605](https://github.com/liip/LiipImagineBundle/pull/605) ([cdaguerre](https://github.com/cdaguerre)) -- \[Doc\] Add missing coma and fix indentation in README.md [\#604](https://github.com/liip/LiipImagineBundle/pull/604) ([grena](https://github.com/grena)) -- Removed TransformerInterface [\#603](https://github.com/liip/LiipImagineBundle/pull/603) ([rvanlaarhoven](https://github.com/rvanlaarhoven)) -- remove duplicate parameter [\#601](https://github.com/liip/LiipImagineBundle/pull/601) ([ip512](https://github.com/ip512)) -- Fix typo [\#600](https://github.com/liip/LiipImagineBundle/pull/600) ([hpatoio](https://github.com/hpatoio)) -- Adding details to use the bundle with remote images [\#569](https://github.com/liip/LiipImagineBundle/pull/569) ([flug](https://github.com/flug)) -## [1.2.6](https://github.com/liip/LiipImagineBundle/tree/1.2.6) (2015-04-24) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.5...1.2.6) +## [v1.9.0](https://github.com/liip/LiipImagineBundle/tree/1.9.0) -- Check $filters is an array [\#596](https://github.com/liip/LiipImagineBundle/pull/596) ([trsteel88](https://github.com/trsteel88)) +*Released on* 2017-08-30 *and assigned* [`1.9.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.9.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.8.0...1.9.0)\).* -## [1.2.5](https://github.com/liip/LiipImagineBundle/tree/1.2.5) (2015-04-08) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.4...1.2.5) +- __\[Tests\]__ Fix filesystem loader deprecation message in tests. [\#982](https://github.com/liip/LiipImagineBundle/pull/982) *([robfrawley](https://github.com/robfrawley))* +- __\[Filter\]__ Add "centerright" and "centerleft" positions to background filter. [\#974](https://github.com/liip/LiipImagineBundle/pull/974) *([cmodijk](https://github.com/cmodijk))* +- __\[Config\]__ Allow to configure the HTTP response code for redirects. [\#970](https://github.com/liip/LiipImagineBundle/pull/970) *([lstrojny](https://github.com/lstrojny))* +- __\[Console\]__ Added --force option, renamed --filters to --filter, and made resolve command output pretty. [\#967](https://github.com/liip/LiipImagineBundle/pull/967) *([robfrawley](https://github.com/robfrawley))* +- __\[CS\]__ Fix two docblock annotations. [\#965](https://github.com/liip/LiipImagineBundle/pull/965) *([imanalopher](https://github.com/imanalopher))* +- __\[Data Loader\]__ __\[Deprecation\]__ The FileSystemLoader no longer accepts an array of data root paths; instead pass a FileSystemLocator, which should instead be passed said paths. [\#963](https://github.com/liip/LiipImagineBundle/pull/963/) *([robfrawley](https://github.com/robfrawley), [rpkamp](https://github.com/rpkamp))* +- __\[Composer\]__ Allow [avalanche123/Imagine](https://github.com/avalanche123/Imagine) version 0.7.0. [\#958](https://github.com/liip/LiipImagineBundle/pull/958) *([robfrawley](https://github.com/robfrawley))* +- __\[Data Loader\]__ __\[Documentation\]__ Add chain loader documentation. [\#957](https://github.com/liip/LiipImagineBundle/pull/957) *([robfrawley](https://github.com/robfrawley))* +- __\[Data Loader\]__ Add chain loader implementation. [\#953](https://github.com/liip/LiipImagineBundle/pull/953) *([robfrawley](https://github.com/robfrawley))* +- __\[CS\]__ Fix templating extension method return type. [\#951](https://github.com/liip/LiipImagineBundle/pull/951) *([imanalopher](https://github.com/imanalopher))* +- __\[Dependency Injection\]__ Fix compiler pass log message typo. [\#947](https://github.com/liip/LiipImagineBundle/pull/947) *([you-ser](https://github.com/you-ser))* +- __\[Travis\]__ Default to trusty container image \(with precise image for php 5.3\). [\#945](https://github.com/liip/LiipImagineBundle/pull/945) *([robfrawley](https://github.com/robfrawley))* +- __\[Enqueue\]__ Use simplified transport configuration. [\#942](https://github.com/liip/LiipImagineBundle/pull/942) *([makasim](https://github.com/makasim))* +- __\[Filter\]__ Add resolution loader implementation. [\#941](https://github.com/liip/LiipImagineBundle/pull/941) *([robfrawley](https://github.com/robfrawley))* +- __\[Travis\]__ Remove Symfony 3.3 from allowed failures. [\#940](https://github.com/liip/LiipImagineBundle/pull/940) *([robfrawley](https://github.com/robfrawley))* +- __\[Utility\]__ Use simplified Symfony kernel version comparison operation. [\#939](https://github.com/liip/LiipImagineBundle/pull/939) *([robfrawley](https://github.com/robfrawley))* -- Add image rotate filter [\#588](https://github.com/liip/LiipImagineBundle/pull/588) ([bocharsky-bw](https://github.com/bocharsky-bw)) -- run php-cs-fixer on bundle [\#583](https://github.com/liip/LiipImagineBundle/pull/583) ([trsteel88](https://github.com/trsteel88)) -- Fix typo [\#582](https://github.com/liip/LiipImagineBundle/pull/582) ([bicpi](https://github.com/bicpi)) -- Fix typos [\#581](https://github.com/liip/LiipImagineBundle/pull/581) ([bicpi](https://github.com/bicpi)) -- Fix typos [\#580](https://github.com/liip/LiipImagineBundle/pull/580) ([bicpi](https://github.com/bicpi)) -## [1.2.4](https://github.com/liip/LiipImagineBundle/tree/1.2.4) (2015-03-27) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.3...1.2.4) +## [v1.8.0](https://github.com/liip/LiipImagineBundle/tree/1.8.0) -- Update how missing filters are logged [\#579](https://github.com/liip/LiipImagineBundle/pull/579) ([trsteel88](https://github.com/trsteel88)) -- use isDefined method for OptionsResolver instead of isKnown \(when possible\) [\#567](https://github.com/liip/LiipImagineBundle/pull/567) ([adam187](https://github.com/adam187)) +*Released on* 2017-05-08 *and assigned* [`1.8.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.8.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.4...1.8.0)\).* -## [1.2.3](https://github.com/liip/LiipImagineBundle/tree/1.2.3) (2015-02-22) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.2...1.2.3) +- __\[Minor\]__ __\[Bug\]__ Revert to php-cs-fixer 1.x and run fixer. [\#927](https://github.com/liip/LiipImagineBundle/pull/927) *([robfrawley](https://github.com/robfrawley))* +- __\[Routing\]__ Deprecate XML routing file in favor of YAML. [\#925](https://github.com/liip/LiipImagineBundle/pull/925) *([robfrawley](https://github.com/robfrawley))* +- __\[Filter\]__ Add flip filter implementation to core. [\#920](https://github.com/liip/LiipImagineBundle/pull/920) *([robfrawley](https://github.com/robfrawley))* +- __\[Queue\]__ Resolve image caches in background using message queue. [\#919](https://github.com/liip/LiipImagineBundle/pull/919) *([makasim](https://github.com/makasim))* -- fix invalid in\_array [\#565](https://github.com/liip/LiipImagineBundle/pull/565) ([digitalkaoz](https://github.com/digitalkaoz)) -- Add a short introductory paragraph about the bundle [\#559](https://github.com/liip/LiipImagineBundle/pull/559) ([javiereguiluz](https://github.com/javiereguiluz)) -- Update Filters.rst [\#556](https://github.com/liip/LiipImagineBundle/pull/556) ([Spawnrad](https://github.com/Spawnrad)) -- Fixed the syntax of the internal doc links [\#554](https://github.com/liip/LiipImagineBundle/pull/554) ([javiereguiluz](https://github.com/javiereguiluz)) -- Updated README.md to point to new .rst doc files [\#551](https://github.com/liip/LiipImagineBundle/pull/551) ([Khez](https://github.com/Khez)) -- fix typo on readme file [\#550](https://github.com/liip/LiipImagineBundle/pull/550) ([erivello](https://github.com/erivello)) -- Switched the documentation from Markdown to ReStructuredText [\#545](https://github.com/liip/LiipImagineBundle/pull/545) ([javiereguiluz](https://github.com/javiereguiluz)) -- Fix Filter Documentation [\#544](https://github.com/liip/LiipImagineBundle/pull/544) ([wodka](https://github.com/wodka)) -- Add support for the new quality options [\#473](https://github.com/liip/LiipImagineBundle/pull/473) ([patrickli](https://github.com/patrickli)) -## [1.2.2](https://github.com/liip/LiipImagineBundle/tree/1.2.2) (2015-01-08) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.1...1.2.2) +## [v1.7.4](https://github.com/liip/LiipImagineBundle/tree/1.7.4) -- Update the filter\_sets Documentation about removed configurations [\#543](https://github.com/liip/LiipImagineBundle/pull/543) ([mbiagetti](https://github.com/mbiagetti)) -- implement interlace filter [\#503](https://github.com/liip/LiipImagineBundle/pull/503) ([wodka](https://github.com/wodka)) +*Released on* 2017-03-01 *and assigned* [`1.7.4`](https://github.com/liip/LiipImagineBundle/releases/tag/1.7.4) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.3...1.7.4)\).* -## [1.2.1](https://github.com/liip/LiipImagineBundle/tree/1.2.1) (2014-12-10) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.0...1.2.1) +- __\[Bug\]__ Revert adding leading slash to S3 class names. [\#893](https://github.com/liip/LiipImagineBundle/pull/893) *([cedricziel](https://github.com/cedricziel))* -- argument to s3 resolver prototype definition has been added [\#536](https://github.com/liip/LiipImagineBundle/pull/536) ([ruslan-polutsygan](https://github.com/ruslan-polutsygan)) -## [1.2.0](https://github.com/liip/LiipImagineBundle/tree/1.2.0) (2014-12-10) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.1.1...1.2.0) +## [v1.7.3](https://github.com/liip/LiipImagineBundle/tree/1.7.3) -- S3 resolver put options [\#535](https://github.com/liip/LiipImagineBundle/pull/535) ([ruslan-polutsygan](https://github.com/ruslan-polutsygan)) -- Fixed minor PHPDoc [\#528](https://github.com/liip/LiipImagineBundle/pull/528) ([sdaoudi](https://github.com/sdaoudi)) +*Released on* 2017-03-01 *and assigned* [`1.7.3`](https://github.com/liip/LiipImagineBundle/releases/tag/1.7.3) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.2...1.7.3)\).* -## [1.1.1](https://github.com/liip/LiipImagineBundle/tree/1.1.1) (2014-11-12) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.1.0...1.1.1) +- __\[Tests\]__ Support PHPUnit 5.x \(and remove depredations\). [\#887](https://github.com/liip/LiipImagineBundle/pull/887) *([robfrawley](https://github.com/robfrawley))* +- __\[Tests\]__ Assert expected deprecation using symfony/phpunit-bridge. [\#886](https://github.com/liip/LiipImagineBundle/pull/886) *([robfrawley](https://github.com/robfrawley))* +- __\[Minor\]__ __\[Documentation\]__ Fix typo in general filters documentation. [\#888](https://github.com/liip/LiipImagineBundle/pull/888) *([svenluijten](https://github.com/svenluijten))* +- __\[Data Loader\]__ Add bundle resources to safe path when requested. [\#883](https://github.com/liip/LiipImagineBundle/pull/883) *([bobvandevijver](https://github.com/bobvandevijver), [robfrawley](https://github.com/robfrawley))* +- __\[Tests\]__ Enable mongo unit tests on PHP7 using "mongo" => "mongodb" extension adapter. [\#882](https://github.com/liip/LiipImagineBundle/pull/882) *([robfrawley](https://github.com/robfrawley))* +- __\[Data Loader\]__ __\[Data Locator\]__ FileSystemLocator service must not be shared. [\#875](https://github.com/liip/LiipImagineBundle/pull/875) *([robfrawley](https://github.com/liip/LiipImagineBundle/pull/875))* -- Fix crash when no post processor is defined [\#526](https://github.com/liip/LiipImagineBundle/pull/526) ([lolautruche](https://github.com/lolautruche)) -- WebPathResolver - sanitize URL to directory name [\#480](https://github.com/liip/LiipImagineBundle/pull/480) ([teohhanhui](https://github.com/teohhanhui)) -## [1.1.0](https://github.com/liip/LiipImagineBundle/tree/1.1.0) (2014-10-29) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.8...1.1.0) +## [v1.7.2](https://github.com/liip/LiipImagineBundle/tree/1.7.2) -- Post-processors - handlers to be applied on filtered image binary [\#519](https://github.com/liip/LiipImagineBundle/pull/519) ([kostiklv](https://github.com/kostiklv)) +*Released on* 2017-02-07 *and assigned* [`1.7.2`](https://github.com/liip/LiipImagineBundle/releases/tag/1.7.2) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.1...1.7.2)\).* -## [1.0.8](https://github.com/liip/LiipImagineBundle/tree/1.0.8) (2014-10-22) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.7...1.0.8) +- __\[Data Loader\]__ Abstract filesystem resource locator and legacy insecure locator implementation. [\#866](https://github.com/liip/LiipImagineBundle/pull/866) *([robfrawley](https://github.com/robfrawley))* +- __\[Minor\]__ __\[Data Loader\]__ Fix for FileSystemLoader annotation. [\#868](https://github.com/liip/LiipImagineBundle/pull/868) *([tgabi333](https://github.com/tgabi333))* +- __\[Dependency Injection\]__ Container logging for compiler passes. [\#867](https://github.com/liip/LiipImagineBundle/pull/867) *([robfrawley](https://github.com/robfrawley))* +- __\[CI\]__ Use Prestissimo package for Travis build. [\#864](https://github.com/liip/LiipImagineBundle/pull/864) *([robfrawley](https://github.com/robfrawley))* +- __\[GitHub\]__ Add Github templates for issues and PRs. [\#863](https://github.com/liip/LiipImagineBundle/pull/863) *([robfrawley](https://github.com/robfrawley))* +- __\[Symfony\]__ Bug fixes and deprecation cleanup for Symfony 3.3. [\#860](https://github.com/liip/LiipImagineBundle/pull/860) *([robfrawley](https://github.com/robfrawley))* +- __\[Filter\]__ Upscale filter should use the highest dimension to calculate ratio. [\#856](https://github.com/liip/LiipImagineBundle/pull/856) *([Rattler3](https://github.com/Rattler3))* -- Delete АГГЗ.jpeg [\#515](https://github.com/liip/LiipImagineBundle/pull/515) ([crash21](https://github.com/crash21)) -- Update configuration.md [\#513](https://github.com/liip/LiipImagineBundle/pull/513) ([hugohenrique](https://github.com/hugohenrique)) -## [1.0.7](https://github.com/liip/LiipImagineBundle/tree/1.0.7) (2014-10-18) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.6...1.0.7) +## [v1.7.1](https://github.com/liip/LiipImagineBundle/tree/1.7.1) -- fix tests, upgrade phpunit up to 4.3 [\#511](https://github.com/liip/LiipImagineBundle/pull/511) ([makasim](https://github.com/makasim)) -- Image default when notloadable exception [\#510](https://github.com/liip/LiipImagineBundle/pull/510) ([Neime](https://github.com/Neime)) -- Explain how to change the default resolver [\#508](https://github.com/liip/LiipImagineBundle/pull/508) ([dbu](https://github.com/dbu)) -- Updated DI configuration to the current implementation of the loader [\#500](https://github.com/liip/LiipImagineBundle/pull/500) ([peterrehm](https://github.com/peterrehm)) -- Support custom output format for each filter set [\#477](https://github.com/liip/LiipImagineBundle/pull/477) ([teohhanhui](https://github.com/teohhanhui)) +*Released on* 2017-01-19 *and assigned* [`1.7.1`](https://github.com/liip/LiipImagineBundle/releases/tag/1.7.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.7.0...1.7.1)\).* -## [1.0.6](https://github.com/liip/LiipImagineBundle/tree/1.0.6) (2014-09-17) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.5...1.0.6) +- __\[Data Loader\]__ Allow multiple root paths for FileSystemLoader. [\#851](https://github.com/liip/LiipImagineBundle/pull/851) *([robfrawley](https://github.com/robfrawley))* +- __\[Documentation\]__ Fix strange wording in readme. [\#847](https://github.com/liip/LiipImagineBundle/pull/847) *([svenluijten](https://github.com/svenluijten))* -- Fix GridFSLoader [\#461](https://github.com/liip/LiipImagineBundle/pull/461) ([aldeck](https://github.com/aldeck)) -## [1.0.5](https://github.com/liip/LiipImagineBundle/tree/1.0.5) (2014-09-15) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.4...1.0.5) +## [v1.7.0](https://github.com/liip/LiipImagineBundle/tree/1.7.0) -- check if runtimeconfig path is stored [\#498](https://github.com/liip/LiipImagineBundle/pull/498) ([trsteel88](https://github.com/trsteel88)) -- Update README.md [\#490](https://github.com/liip/LiipImagineBundle/pull/490) ([JellyBellyDev](https://github.com/JellyBellyDev)) -- Update README.md [\#488](https://github.com/liip/LiipImagineBundle/pull/488) ([JellyBellyDev](https://github.com/JellyBellyDev)) -- fix auto rotate [\#476](https://github.com/liip/LiipImagineBundle/pull/476) ([scuben](https://github.com/scuben)) -- support animated gif [\#466](https://github.com/liip/LiipImagineBundle/pull/466) ([scuben](https://github.com/scuben)) +*Released on* 2017-01-08 *and assigned* [`1.7.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.7.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.6.0...1.7.0)\).* -## [1.0.4](https://github.com/liip/LiipImagineBundle/tree/1.0.4) (2014-07-30) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.3...1.0.4) +- __\[Dependency Injection\]__ Use DefaultMetadataReader instead of ExifMetadataReader when "exif" extension is not present. [\#841](https://github.com/liip/LiipImagineBundle/pull/841) *([cedricziel](https://github.com/cedricziel))* +- __\[Documentation\]__ Updating twig call to utilise asset\(\) \(to match README.md\) \(closes [\#830](https://github.com/liip/LiipImagineBundle/issues/830)\). [\#836](https://github.com/liip/LiipImagineBundle/pull/836) *([antoligy](https://github.com/antoligy))* +- __\[Composer\]__ Exclude "Tests" directory from classmap. [\#835](https://github.com/liip/LiipImagineBundle/pull/835) *([pamil](https://github.com/pamil))* +- __\[Composer\]__ Require components that no longer ship with Symfony FrameworkBundle 3.2. [\#832](https://github.com/liip/LiipImagineBundle/pull/832) *([rpkamp](https://github.com/rpkamp))* +- __\[Documentation\]__ Document how web paths are built. [\#829](https://github.com/liip/LiipImagineBundle/pull/829) *([greg0ire](https://github.com/greg0ire))* +- __\[Documentation\]__ Wrap relative path with asset\(\) Twig function. [\#825](https://github.com/liip/LiipImagineBundle/pull/825) *([bocharsky-bw](https://github.com/bocharsky-bw))* +- __\[Documentation\]__ __\[Data Loader\]__ Update custom data loader tag. [\#821](https://github.com/liip/LiipImagineBundle/pull/821) *([IllesAprod](https://github.com/IllesAprod))* +- __\[Documentation\]__ Fix typo in README.md example code. [\#819](https://github.com/liip/LiipImagineBundle/pull/819) *([redjanym](https://github.com/redjanym))* +- __\[Documentation\]__ Fix RST indentation error in AWS S3 cache resolver documentation. [\#809](https://github.com/liip/LiipImagineBundle/pull/809) *([GeoffreyHervet](https://github.com/GeoffreyHervet))* +- __\[Documentation\]__ Fix typo in basic-usage.rst example code. [\#805](https://github.com/liip/LiipImagineBundle/pull/805) *([you-ser](https://github.com/you-ser))* +- __\[Documentation\]__ Fix missing "data_loader" option in Flyststem code example. [\#803](https://github.com/liip/LiipImagineBundle/pull/803) *([davidfuhr](https://github.com/davidfuhr))* +- __\[Documentation\]__ Typo/Fix/Clarification for Watermark RST Docs. [\#802](https://github.com/liip/LiipImagineBundle/pull/802) *([robfrawley](https://github.com/robfrawley))* +- __\[CI\]__ Apply latest style rules to entire codebase. [\#800](https://github.com/liip/LiipImagineBundle/pull/800) *([robfrawley](https://github.com/robfrawley))* +- __\[Minor\]__ __\[CI\]__ __\[Travis\]__ Bugfix: remove short array syntax and fix merge error. [\#799](https://github.com/liip/LiipImagineBundle/pull/799) *([robfrawley](https://github.com/robfrawley))* +- __\[Travis\]__ Add Symfony Framework 3.1.x and 3.2-dev to build matrix. [\#796](https://github.com/liip/LiipImagineBundle/pull/796) *([cedricziel](https://github.com/cedricziel))* +- __\[Config\]__ Add visibility argument to service definition. [\#795](https://github.com/liip/LiipImagineBundle/pull/795) *([cedricziel](https://github.com/cedricziel))* +- __\[Tests\]__ Bugfix for failing tests \(introduced in [\#777](https://github.com/liip/LiipImagineBundle/issues/777)\). [\#793](https://github.com/liip/LiipImagineBundle/pull/793) *([robfrawley](https://github.com/robfrawley))* +- __\[CI\]__ Added php\_cs.dist and updated .styleci.yml / Fixed and updated .travis.yml. [\#792](https://github.com/liip/LiipImagineBundle/pull/792) *([robfrawley](https://github.com/robfrawley))* +- __\[Documentation\]__ Add LICENSE.md. [\#790](https://github.com/liip/LiipImagineBundle/pull/790) *([robfrawley](https://github.com/robfrawley))* +- __\[Documentation\]__ Major update/refactoring and additions to README.md and RST documentation. [\#789](https://github.com/liip/LiipImagineBundle/pull/789) *([robfrawley](https://github.com/robfrawley))* +- __\[Documentation\]__ Updated CHANGELOG.md. [\#788](https://github.com/liip/LiipImagineBundle/pull/788) *([robfrawley](https://github.com/robfrawley))* +- __\[Filter\]__ __\[Tests\]__ Fix "list" usages and use "getMockBuilder()" \(closes [\#731](https://github.com/liip/LiipImagineBundle/issues/731)\). [\#787](https://github.com/liip/LiipImagineBundle/pull/787) *([antoligy](https://github.com/antoligy))* +- __\[Data Loader\]__ Cleanup FileSystemLoader implementation and add tests \(followup to [\#775](https://github.com/liip/LiipImagineBundle/issues/775)\). [\#785](https://github.com/liip/LiipImagineBundle/pull/785) *([robfrawley](https://github.com/robfrawley))* +- __\[Post Processor\]__ Add "temp_dir" option for post-processors. [\#779](https://github.com/liip/LiipImagineBundle/pull/779) *([jehaby](https://github.com/jehaby))* +- __\[Cache Resolver\]__ Add visibility argument to flysystem resolver. [\#777](https://github.com/liip/LiipImagineBundle/pull/777) *([cedricziel](https://github.com/cedricziel))* +- __\[Data Loader\]__ Fix FileSystemLoader path resolution handlers and outside root check. [\#775](https://github.com/liip/LiipImagineBundle/pull/775) *([robfrawley](https://github.com/robfrawley))* +- __\[Filter\]__ Make Downscale and Upscale derivatives of Scale. [\#773](https://github.com/liip/LiipImagineBundle/pull/773) *([deviprsd21](https://github.com/deviprsd21))* +- __\[CI\]__ Applied fixes from StyleCI. [\#768](https://github.com/liip/LiipImagineBundle/pull/768) *([lsmith77](https://github.com/lsmith77))* +- __\[DI\]__ Replaced deprecated factory\_class and factory\_method. [\#767](https://github.com/liip/LiipImagineBundle/pull/767) *([rvanlaarhoven](https://github.com/rvanlaarhoven))* +- __\[Documentation\]__ Update basic-usage.rst. [\#766](https://github.com/liip/LiipImagineBundle/pull/766) *([nochecksum](https://github.com/nochecksum))* +- __\[Post Processor\]__ Implemented ConfigurablePostProcessorInterface in OptiPngPostProcessor. [\#764](https://github.com/liip/LiipImagineBundle/pull/764) *([jehaby](https://github.com/jehaby))* -- Update WebPathResolverFactory.php [\#467](https://github.com/liip/LiipImagineBundle/pull/467) ([JJK801](https://github.com/JJK801)) -## [1.0.3](https://github.com/liip/LiipImagineBundle/tree/1.0.3) (2014-07-30) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.2...1.0.3) +## [v1.6.0](https://github.com/liip/LiipImagineBundle/tree/1.6.0) -- Fixing issue with removed class Color [\#458](https://github.com/liip/LiipImagineBundle/pull/458) ([lstrojny](https://github.com/lstrojny)) -- Added PHP 5.6 and HHVM to travis.yml [\#454](https://github.com/liip/LiipImagineBundle/pull/454) ([Nyholm](https://github.com/Nyholm)) -- make the Bundle compatible with config:dump-reference command [\#452](https://github.com/liip/LiipImagineBundle/pull/452) ([lsmith77](https://github.com/lsmith77)) +*Released on* 2016-07-22 *and assigned* [`1.6.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.6.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.5.3...1.6.0)\).* -## [1.0.2](https://github.com/liip/LiipImagineBundle/tree/1.0.2) (2014-06-24) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.1...1.0.2) +- Input is added twice in the OptiPngProcessor. [\#762](https://github.com/liip/LiipImagineBundle/pull/762) *([antoligy](https://github.com/antoligy))* +- Enable configuration of post processors using parameters \(closes [\#720](https://github.com/liip/LiipImagineBundle/issues/720)\). [\#759](https://github.com/liip/LiipImagineBundle/pull/759) *([antoligy](https://github.com/antoligy))* +- Applied fixes from StyleCI. [\#758](https://github.com/liip/LiipImagineBundle/pull/758) *([lsmith77](https://github.com/lsmith77))* +- Applied fixes from StyleCI. [\#757](https://github.com/liip/LiipImagineBundle/pull/757) *([lsmith77](https://github.com/lsmith77))* +- Add configuration options for jpegoptim post-processor. [\#756](https://github.com/liip/LiipImagineBundle/pull/756) *([dylanschoenmakers](https://github.com/dylanschoenmakers))* +- Ignore invalid exif orientations. [\#751](https://github.com/liip/LiipImagineBundle/pull/751) *([lstrojny](https://github.com/lstrojny))* +- Quote strings starting '%' in YAML. [\#745](https://github.com/liip/LiipImagineBundle/pull/745) *([jaikdean](https://github.com/jaikdean))* +- Fix tempnam usages. [\#723](https://github.com/liip/LiipImagineBundle/pull/723) *([1ed](https://github.com/1ed))* +- Background filter: allow image positioning. [\#721](https://github.com/liip/LiipImagineBundle/pull/721) *([uvoelkel](https://github.com/uvoelkel))* +- Add Flysystem resolver. [\#715](https://github.com/liip/LiipImagineBundle/pull/715) *([cedricziel](https://github.com/cedricziel))* +- Downscale filter scales an image to fit bounding box. [\#696](https://github.com/liip/LiipImagineBundle/pull/696) *([aminin](https://github.com/aminin))* +- Implement Imagine Grayscale filter. [\#638](https://github.com/liip/LiipImagineBundle/pull/638) *([gregumo](https://github.com/gregumo))* -- Update README.md [\#447](https://github.com/liip/LiipImagineBundle/pull/447) ([sgaze](https://github.com/sgaze)) -- Update configuration.md [\#446](https://github.com/liip/LiipImagineBundle/pull/446) ([sgaze](https://github.com/sgaze)) -## [1.0.1](https://github.com/liip/LiipImagineBundle/tree/1.0.1) (2014-06-06) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0...1.0.1) +## [v1.5.3](https://github.com/liip/LiipImagineBundle/tree/1.5.3) -- \[stream\] throws exception when content cannot be read. [\#444](https://github.com/liip/LiipImagineBundle/pull/444) ([makasim](https://github.com/makasim)) -- remove unused use-statement and fix phpdoc [\#441](https://github.com/liip/LiipImagineBundle/pull/441) ([UFOMelkor](https://github.com/UFOMelkor)) +*Released on* 2016-05-06 *and assigned* [`1.5.3`](https://github.com/liip/LiipImagineBundle/releases/tag/1.5.3) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.5.2...1.5.3)\).* -## [1.0.0](https://github.com/liip/LiipImagineBundle/tree/1.0.0) (2014-05-22) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha7...1.0.0) +- Add @Event annotation to let IDEs known event names and class instance. [\#732](https://github.com/liip/LiipImagineBundle/pull/732) *([Haehnchen](https://github.com/Haehnchen))* +- Introduce mozjpeg and pngquant post-processors, add transform options. [\#717](https://github.com/liip/LiipImagineBundle/pull/717) *([antoligy](https://github.com/antoligy))* +- StreamLoader-exception-arguments. [\#714](https://github.com/liip/LiipImagineBundle/pull/714) *([antonsmolin](https://github.com/antonsmolin))* -- added possibility to use imagine new metadata api [\#413](https://github.com/liip/LiipImagineBundle/pull/413) ([digitalkaoz](https://github.com/digitalkaoz)) -## [1.0.0-alpha7](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha7) (2014-05-22) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha6...1.0.0-alpha7) +## [v1.5.2](https://github.com/liip/LiipImagineBundle/tree/1.5.2) -- Add a Signer Utility to sign filters, run php-cs-fixer on bundle [\#405](https://github.com/liip/LiipImagineBundle/pull/405) ([trsteel88](https://github.com/trsteel88)) +*Released on* 2016-02-16 *and assigned* [`1.5.2`](https://github.com/liip/LiipImagineBundle/releases/tag/1.5.2) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.5.1...1.5.2)\).* -## [1.0.0-alpha6](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha6) (2014-05-05) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha5...1.0.0-alpha6) +- Revert "Merge pull request [\#699](https://github.com/liip/LiipImagineBundle/issues/699) from jockri/fix-background-filter". [\#709](https://github.com/liip/LiipImagineBundle/pull/709) *([mangelsnc](https://github.com/mangelsnc))* -- \[router\] remove custom route loader. [\#425](https://github.com/liip/LiipImagineBundle/pull/425) ([makasim](https://github.com/makasim)) -## [1.0.0-alpha5](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha5) (2014-04-29) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha4...1.0.0-alpha5) +## [v1.5.1](https://github.com/liip/LiipImagineBundle/tree/1.5.1) -- added scrutinizer config [\#420](https://github.com/liip/LiipImagineBundle/pull/420) ([digitalkaoz](https://github.com/digitalkaoz)) -- Fixed testsuite \#417 in \#403 [\#419](https://github.com/liip/LiipImagineBundle/pull/419) ([ama3ing](https://github.com/ama3ing)) -- increase test coverage report [\#417](https://github.com/liip/LiipImagineBundle/pull/417) ([digitalkaoz](https://github.com/digitalkaoz)) -- enabled symfony 2.4 on travis [\#416](https://github.com/liip/LiipImagineBundle/pull/416) ([digitalkaoz](https://github.com/digitalkaoz)) -- Update configuration.md [\#410](https://github.com/liip/LiipImagineBundle/pull/410) ([ama3ing](https://github.com/ama3ing)) -- \[ci\] run tests only on 2.3 version. [\#407](https://github.com/liip/LiipImagineBundle/pull/407) ([makasim](https://github.com/makasim)) -- Watermark filter documentation update. Fixes \#404 [\#406](https://github.com/liip/LiipImagineBundle/pull/406) ([ama3ing](https://github.com/ama3ing)) -- Fixes \#373. Replace NotFoundHttpException with SourceNotFoundException [\#403](https://github.com/liip/LiipImagineBundle/pull/403) ([ama3ing](https://github.com/ama3ing)) -- Removed unreachable statement [\#402](https://github.com/liip/LiipImagineBundle/pull/402) ([ama3ing](https://github.com/ama3ing)) -- Fix of \#369 \(Trim of forwarding slash in path\) [\#401](https://github.com/liip/LiipImagineBundle/pull/401) ([ama3ing](https://github.com/ama3ing)) +*Released on* 2016-02-13 *and assigned* [`1.5.1`](https://github.com/liip/LiipImagineBundle/releases/tag/1.5.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.5.0...1.5.1)\).* -## [1.0.0-alpha4](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha4) (2014-04-14) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha3...1.0.0-alpha4) +- Fix regression introduced in [\#bb8e410](https://github.com/liip/LiipImagineBundle/commit/bb8e4109672902e37931e0a491ff55ebac93d8e9). [\#707](https://github.com/liip/LiipImagineBundle/pull/707) *([Seldaek](https://github.com/Seldaek))* -- \[config\] correctly process resolvers\loaders section if not array or null [\#396](https://github.com/liip/LiipImagineBundle/pull/396) ([makasim](https://github.com/makasim)) -- Issue\#368 wrong image path [\#395](https://github.com/liip/LiipImagineBundle/pull/395) ([serdyuka](https://github.com/serdyuka)) -## [1.0.0-alpha3](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha3) (2014-04-14) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha2...1.0.0-alpha3) +## [v1.5.0](https://github.com/liip/LiipImagineBundle/tree/1.5.0) -- Added proxy to aws s3 resolver factory [\#392](https://github.com/liip/LiipImagineBundle/pull/392) ([serdyuka](https://github.com/serdyuka)) +*Released on* 2016-02-12 *and assigned* [`1.5.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.5.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.4.3...1.5.0)\).* -## [1.0.0-alpha2](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha2) (2014-04-10) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha1...1.0.0-alpha2) +- Applied fixes from StyleCI. [\#706](https://github.com/liip/LiipImagineBundle/pull/706) *([lsmith77](https://github.com/lsmith77))* +- Add FileBinaryInterface to support large files without loading them in memory unnecessarily. [\#705](https://github.com/liip/LiipImagineBundle/pull/705) *([Seldaek](https://github.com/Seldaek))* +- Fix background filter. [\#699](https://github.com/liip/LiipImagineBundle/pull/699) *([jockri](https://github.com/jockri))* +- Fix undeclared variable. [\#697](https://github.com/liip/LiipImagineBundle/pull/697) *([tifabien](https://github.com/tifabien))* +- Update WebPathResolver.php. [\#695](https://github.com/liip/LiipImagineBundle/pull/695) *([gonzalovilaseca](https://github.com/gonzalovilaseca))* +- Add missing link to the filters doc. [\#694](https://github.com/liip/LiipImagineBundle/pull/694) *([bocharsky-bw](https://github.com/bocharsky-bw))* +- Adding optipng post transformer. [\#692](https://github.com/liip/LiipImagineBundle/pull/692) *([gouaille](https://github.com/gouaille))* -- Documentation update fixes \#389 [\#390](https://github.com/liip/LiipImagineBundle/pull/390) ([ama3ing](https://github.com/ama3ing)) -- \[WIP\] Added resolve events to cache manager [\#388](https://github.com/liip/LiipImagineBundle/pull/388) ([serdyuka](https://github.com/serdyuka)) -## [1.0.0-alpha1](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha1) (2014-04-07) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.21.1...1.0.0-alpha1) +## [v1.4.3](https://github.com/liip/LiipImagineBundle/tree/1.4.3) -- Remove cli command [\#387](https://github.com/liip/LiipImagineBundle/pull/387) ([serdyuka](https://github.com/serdyuka)) -- fixed and improved tests for resolve cache command [\#386](https://github.com/liip/LiipImagineBundle/pull/386) ([serdyuka](https://github.com/serdyuka)) -- \[1.0\]\[config\] Fix default loader not found bug. [\#385](https://github.com/liip/LiipImagineBundle/pull/385) ([makasim](https://github.com/makasim)) -- Resolve command few paths [\#383](https://github.com/liip/LiipImagineBundle/pull/383) ([serdyuka](https://github.com/serdyuka)) -- Move data loaders to binary folder [\#382](https://github.com/liip/LiipImagineBundle/pull/382) ([serdyuka](https://github.com/serdyuka)) -- Documentation for cli command [\#380](https://github.com/liip/LiipImagineBundle/pull/380) ([serdyuka](https://github.com/serdyuka)) -- Cli command to resolve cache [\#379](https://github.com/liip/LiipImagineBundle/pull/379) ([serdyuka](https://github.com/serdyuka)) -- Update README.md [\#374](https://github.com/liip/LiipImagineBundle/pull/374) ([daslicht](https://github.com/daslicht)) -- \[1.0\]\[loader\] cleanup filesystem loader, simplify logic, add factory. [\#371](https://github.com/liip/LiipImagineBundle/pull/371) ([makasim](https://github.com/makasim)) -- \[1.0\]\[aws-resolver\] allow configure cache\_prefix via factory. [\#370](https://github.com/liip/LiipImagineBundle/pull/370) ([makasim](https://github.com/makasim)) -- \[1.0\] set web\_path resolver as default if not configured. [\#367](https://github.com/liip/LiipImagineBundle/pull/367) ([makasim](https://github.com/makasim)) -- \[1.0\]\[Config\] remove path option. [\#366](https://github.com/liip/LiipImagineBundle/pull/366) ([makasim](https://github.com/makasim)) -- Fixed yaml code block on stream loader documentation [\#363](https://github.com/liip/LiipImagineBundle/pull/363) ([rvanlaarhoven](https://github.com/rvanlaarhoven)) -- \[1.0\]\[WebResolver\] Use baseUrl and port while generating image path. [\#362](https://github.com/liip/LiipImagineBundle/pull/362) ([makasim](https://github.com/makasim)) -- Removed cache\_clearer documentation [\#359](https://github.com/liip/LiipImagineBundle/pull/359) ([rvanlaarhoven](https://github.com/rvanlaarhoven)) -- CacheManager updated [\#355](https://github.com/liip/LiipImagineBundle/pull/355) ([ossinkine](https://github.com/ossinkine)) -- FilesystemLoader updated [\#354](https://github.com/liip/LiipImagineBundle/pull/354) ([ossinkine](https://github.com/ossinkine)) -- Update filters.md [\#346](https://github.com/liip/LiipImagineBundle/pull/346) ([zazoomauro](https://github.com/zazoomauro)) +*Released on* 2016-01-14 *and assigned* [`1.4.3`](https://github.com/liip/LiipImagineBundle/releases/tag/1.4.3) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.4.2...1.4.3)\).* -## [v0.21.1](https://github.com/liip/LiipImagineBundle/tree/v0.21.1) (2014-03-14) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.21.0...v0.21.1) +- Fixed build issues. [\#691](https://github.com/liip/LiipImagineBundle/pull/691) *([yceruto](https://github.com/yceruto))* +- Fixed doc errors reported by docs build tool. [\#690](https://github.com/liip/LiipImagineBundle/pull/690) *([javiereguiluz](https://github.com/javiereguiluz))* +- Explicit attr definition was added. [\#688](https://github.com/liip/LiipImagineBundle/pull/688) *([ostretsov](https://github.com/ostretsov))* +- Flysystem support added. [\#674](https://github.com/liip/LiipImagineBundle/pull/674) *([graundas](https://github.com/graundas))* -## [v0.21.0](https://github.com/liip/LiipImagineBundle/tree/v0.21.0) (2014-03-14) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.20.2...v0.21.0) -- Added reference on how to get image path inside a controller [\#340](https://github.com/liip/LiipImagineBundle/pull/340) ([ama3ing](https://github.com/ama3ing)) -- \[1.0\] add phpunit as require-dev [\#339](https://github.com/liip/LiipImagineBundle/pull/339) ([makasim](https://github.com/makasim)) -- \[1.0\] Twig helper not escape filter url [\#337](https://github.com/liip/LiipImagineBundle/pull/337) ([makasim](https://github.com/makasim)) -- Added cache clearing & setting cachePrefix for Aws S3 [\#336](https://github.com/liip/LiipImagineBundle/pull/336) ([rvanlaarhoven](https://github.com/rvanlaarhoven)) -- Merge latest changes in master to develop branch [\#334](https://github.com/liip/LiipImagineBundle/pull/334) ([makasim](https://github.com/makasim)) -- Update to Imagine 0.6 [\#330](https://github.com/liip/LiipImagineBundle/pull/330) ([vlastv](https://github.com/vlastv)) -- \[1.0\]\[Configuration\] Cleanup bundle configuration. [\#325](https://github.com/liip/LiipImagineBundle/pull/325) ([makasim](https://github.com/makasim)) -- \[1.0\]\[filter\] Dynamic filters [\#313](https://github.com/liip/LiipImagineBundle/pull/313) ([makasim](https://github.com/makasim)) +## [v1.4.2](https://github.com/liip/LiipImagineBundle/tree/1.4.2) -## [v0.20.2](https://github.com/liip/LiipImagineBundle/tree/v0.20.2) (2014-02-20) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.20.1...v0.20.2) +*Released on* 2015-12-29 *and assigned* [`1.4.2`](https://github.com/liip/LiipImagineBundle/releases/tag/1.4.2) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.4.1...1.4.2)\).* -- GridFSLoader Bug [\#331](https://github.com/liip/LiipImagineBundle/pull/331) ([peterrehm](https://github.com/peterrehm)) -- Update filters.md [\#327](https://github.com/liip/LiipImagineBundle/pull/327) ([herb123456](https://github.com/herb123456)) +- Proxy resolver allow find and replace and regexp strategies. [\#687](https://github.com/liip/LiipImagineBundle/pull/687) *([makasim](https://github.com/makasim))* +- Added contributing docs. [\#681](https://github.com/liip/LiipImagineBundle/pull/681) *([helios-ag](https://github.com/helios-ag))* +- Rebased commands document patch \(see [\#533](https://github.com/liip/LiipImagineBundle/issues/533)\). [\#680](https://github.com/liip/LiipImagineBundle/pull/680) *([helios-ag](https://github.com/helios-ag))* -## [v0.20.1](https://github.com/liip/LiipImagineBundle/tree/v0.20.1) (2014-02-10) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.20.0...v0.20.1) -- fixed ProxyResolver-\>getBrowserPath [\#323](https://github.com/liip/LiipImagineBundle/pull/323) ([digitalkaoz](https://github.com/digitalkaoz)) +## [v1.4.1](https://github.com/liip/LiipImagineBundle/tree/1.4.1) -## [v0.20.0](https://github.com/liip/LiipImagineBundle/tree/v0.20.0) (2014-02-07) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.19.0...v0.20.0) +*Released on* 2015-12-27 *and assigned* [`1.4.1`](https://github.com/liip/LiipImagineBundle/releases/tag/1.4.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.4.0...1.4.1)\).* -- \[1.0\]\[resolver\] Decouple WebPathResolver from http request. Simplify its logic. [\#320](https://github.com/liip/LiipImagineBundle/pull/320) ([makasim](https://github.com/makasim)) -- added proxy cache resolver [\#318](https://github.com/liip/LiipImagineBundle/pull/318) ([digitalkaoz](https://github.com/digitalkaoz)) +- Aws sdk v3. [\#685](https://github.com/liip/LiipImagineBundle/pull/685) *([makasim](https://github.com/makasim))* -## [v0.19.0](https://github.com/liip/LiipImagineBundle/tree/v0.19.0) (2014-02-07) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.18.0...v0.19.0) -- improved exception on generation failure [\#321](https://github.com/liip/LiipImagineBundle/pull/321) ([digitalkaoz](https://github.com/digitalkaoz)) -- added background\_image filter [\#319](https://github.com/liip/LiipImagineBundle/pull/319) ([digitalkaoz](https://github.com/digitalkaoz)) -- \[1.0\] Fix tests on current develop branch [\#316](https://github.com/liip/LiipImagineBundle/pull/316) ([makasim](https://github.com/makasim)) -- \[1.0\]\[cache\] CacheResolver has to cache isStored method too. [\#308](https://github.com/liip/LiipImagineBundle/pull/308) ([makasim](https://github.com/makasim)) -- \[1.0\]\[cache\]\[resolver\] Improve caches invalidation. [\#304](https://github.com/liip/LiipImagineBundle/pull/304) ([makasim](https://github.com/makasim)) +## [v1.4.0](https://github.com/liip/LiipImagineBundle/tree/1.4.0) -## [v0.18.0](https://github.com/liip/LiipImagineBundle/tree/v0.18.0) (2014-01-29) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.17.1...v0.18.0) +*Released on* 2015-12-27 *and assigned* [`1.4.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.4.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.3.3...1.4.0)\).* -- added an "auto\_rotate" filter based on exif data [\#254](https://github.com/liip/LiipImagineBundle/pull/254) ([digitalkaoz](https://github.com/digitalkaoz)) +- __\[Resolver\]__ Add ability to force resolver. [\#684](https://github.com/liip/LiipImagineBundle/pull/684) *([makasim](https://github.com/makasim))* -## [v0.17.1](https://github.com/liip/LiipImagineBundle/tree/v0.17.1) (2014-01-24) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.17.0...v0.17.1) -- fixed missing namespace [\#306](https://github.com/liip/LiipImagineBundle/pull/306) ([digitalkaoz](https://github.com/digitalkaoz)) -- \[1.0\]\[cache\] cache manager has to use isStored inside getBrowserPath method [\#303](https://github.com/liip/LiipImagineBundle/pull/303) ([makasim](https://github.com/makasim)) -- \[1.0\]\[CacheResolver\] Use binary on store method call. [\#301](https://github.com/liip/LiipImagineBundle/pull/301) ([makasim](https://github.com/makasim)) -- \[1.0\]\[filter-manager\] make use of binary object. [\#297](https://github.com/liip/LiipImagineBundle/pull/297) ([makasim](https://github.com/makasim)) -- \[1.0\]\[loader\] remove deprecated phpcr loader [\#292](https://github.com/liip/LiipImagineBundle/pull/292) ([makasim](https://github.com/makasim)) -- \[1.0\] Rework data loaders. Introduce mime type guesser. [\#291](https://github.com/liip/LiipImagineBundle/pull/291) ([makasim](https://github.com/makasim)) -- \[1.0\]\[tests\] increase code coverage by tests. [\#290](https://github.com/liip/LiipImagineBundle/pull/290) ([makasim](https://github.com/makasim)) -- \[1.0\]\[Logger\] use PSR one logger [\#286](https://github.com/liip/LiipImagineBundle/pull/286) ([makasim](https://github.com/makasim)) -- \[1.0\]\[CacheResolver\] Resolver get rid of get browser path [\#284](https://github.com/liip/LiipImagineBundle/pull/284) ([makasim](https://github.com/makasim)) -- \[tests\] use real amazon libs in tests. [\#283](https://github.com/liip/LiipImagineBundle/pull/283) ([makasim](https://github.com/makasim)) -- \[1.0\]\[resolver\] do not expose `targetPath` [\#282](https://github.com/liip/LiipImagineBundle/pull/282) ([makasim](https://github.com/makasim)) -- \[1.0\]\[resolver\] remove request parameter [\#281](https://github.com/liip/LiipImagineBundle/pull/281) ([makasim](https://github.com/makasim)) +## [v1.3.3](https://github.com/liip/LiipImagineBundle/tree/1.3.3) -## [v0.17.0](https://github.com/liip/LiipImagineBundle/tree/v0.17.0) (2013-12-04) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.16.0...v0.17.0) +*Released on* 2015-12-27 *and assigned* [`1.3.3`](https://github.com/liip/LiipImagineBundle/releases/tag/1.3.3) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.3.2...1.3.3)\).* -- handle image extensions in doctrine loader [\#276](https://github.com/liip/LiipImagineBundle/pull/276) ([dbu](https://github.com/dbu)) -- Exclude Tests directory on composer archive [\#274](https://github.com/liip/LiipImagineBundle/pull/274) ([oziks](https://github.com/oziks)) -- fix composer require-dev [\#272](https://github.com/liip/LiipImagineBundle/pull/272) ([havvg](https://github.com/havvg)) -- Update filters.md [\#267](https://github.com/liip/LiipImagineBundle/pull/267) ([uwej711](https://github.com/uwej711)) -- Add comment for image parameter in watermark filter configuration exampl... [\#263](https://github.com/liip/LiipImagineBundle/pull/263) ([USvER](https://github.com/USvER)) +- Destruct image to cleanup memory. [\#682](https://github.com/liip/LiipImagineBundle/pull/682) *([cmodijk](https://github.com/cmodijk))* -## [v0.16.0](https://github.com/liip/LiipImagineBundle/tree/v0.16.0) (2013-09-30) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.15.1...v0.16.0) - -- Add Upscale filter [\#248](https://github.com/liip/LiipImagineBundle/pull/248) ([maximecolin](https://github.com/maximecolin)) - -## [v0.15.1](https://github.com/liip/LiipImagineBundle/tree/v0.15.1) (2013-09-20) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.15.0...v0.15.1) - -- Set ContentType of AWS cache object [\#246](https://github.com/liip/LiipImagineBundle/pull/246) ([eXtreme](https://github.com/eXtreme)) - -## [v0.15.0](https://github.com/liip/LiipImagineBundle/tree/v0.15.0) (2013-09-18) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.14.0...v0.15.0) - -- deprecate the phpcr loader as CmfMediaBundle provides a better one now. [\#243](https://github.com/liip/LiipImagineBundle/pull/243) ([dbu](https://github.com/dbu)) -- fix missing filename in exception [\#240](https://github.com/liip/LiipImagineBundle/pull/240) ([havvg](https://github.com/havvg)) -- Corrected aws-sdk-php link [\#233](https://github.com/liip/LiipImagineBundle/pull/233) ([javiacei](https://github.com/javiacei)) - -## [v0.14.0](https://github.com/liip/LiipImagineBundle/tree/v0.14.0) (2013-08-21) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.13.0...v0.14.0) - -- add AwsS3Resolver for new SDK version [\#227](https://github.com/liip/LiipImagineBundle/pull/227) ([havvg](https://github.com/havvg)) - -## [v0.13.0](https://github.com/liip/LiipImagineBundle/tree/v0.13.0) (2013-08-19) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.12.0...v0.13.0) - -- Watermark loader [\#222](https://github.com/liip/LiipImagineBundle/pull/222) ([KingCrunch](https://github.com/KingCrunch)) - -## [v0.12.0](https://github.com/liip/LiipImagineBundle/tree/v0.12.0) (2013-08-19) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.11.1...v0.12.0) - -- Update dependency 'imagine/imagine' to 0.5.\* [\#221](https://github.com/liip/LiipImagineBundle/pull/221) ([KingCrunch](https://github.com/KingCrunch)) - -## [v0.11.1](https://github.com/liip/LiipImagineBundle/tree/v0.11.1) (2013-08-05) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.11.0...v0.11.1) - -- added documentation on inset and outbound modes of thumbnail filter Documentation \(issue \#207\) [\#210](https://github.com/liip/LiipImagineBundle/pull/210) ([rjbijl](https://github.com/rjbijl)) - -## [v0.11.0](https://github.com/liip/LiipImagineBundle/tree/v0.11.0) (2013-06-21) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.10.1...v0.11.0) - -- Add link filter [\#201](https://github.com/liip/LiipImagineBundle/pull/201) ([EmmanuelVella](https://github.com/EmmanuelVella)) -- Thumbnail filter was not applied when allow\_upscale=true and one dimensi... [\#200](https://github.com/liip/LiipImagineBundle/pull/200) ([teohhanhui](https://github.com/teohhanhui)) -- Add badge poser in README [\#199](https://github.com/liip/LiipImagineBundle/pull/199) ([agiuliano](https://github.com/agiuliano)) -- add docs about allow\_scale of thumbnail filter [\#198](https://github.com/liip/LiipImagineBundle/pull/198) ([havvg](https://github.com/havvg)) -- add documentation on S3 object URL options [\#197](https://github.com/liip/LiipImagineBundle/pull/197) ([havvg](https://github.com/havvg)) - -## [v0.10.1](https://github.com/liip/LiipImagineBundle/tree/v0.10.1) (2013-05-29) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.10.0...v0.10.1) - -- mkdir\(\) doesn't take care about the umask [\#189](https://github.com/liip/LiipImagineBundle/pull/189) ([KingCrunch](https://github.com/KingCrunch)) -- The quickest PR to review I guess. [\#188](https://github.com/liip/LiipImagineBundle/pull/188) ([Sydney-o9](https://github.com/Sydney-o9)) - -## [v0.10.0](https://github.com/liip/LiipImagineBundle/tree/v0.10.0) (2013-05-17) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.9.4...v0.10.0) - -- CacheResolver [\#184](https://github.com/liip/LiipImagineBundle/pull/184) ([havvg](https://github.com/havvg)) -- fix broken tests on windows [\#179](https://github.com/liip/LiipImagineBundle/pull/179) ([kevinarcher](https://github.com/kevinarcher)) - -## [v0.9.4](https://github.com/liip/LiipImagineBundle/tree/v0.9.4) (2013-05-14) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.9.3...v0.9.4) - -- fix doc of CacheManager::resolve to not lie [\#186](https://github.com/liip/LiipImagineBundle/pull/186) ([dbu](https://github.com/dbu)) -- Small documentation fix for getting browserPath for a thumb from controller [\#178](https://github.com/liip/LiipImagineBundle/pull/178) ([leberknecht](https://github.com/leberknecht)) -- improve phpcr loader doc [\#177](https://github.com/liip/LiipImagineBundle/pull/177) ([dbu](https://github.com/dbu)) -- Allow symfony 2.3 and greater [\#176](https://github.com/liip/LiipImagineBundle/pull/176) ([tommygnr](https://github.com/tommygnr)) - -## [v0.9.3](https://github.com/liip/LiipImagineBundle/tree/v0.9.3) (2013-04-17) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.9.2...v0.9.3) - -- add CacheManagerAwareTrait [\#173](https://github.com/liip/LiipImagineBundle/pull/173) ([havvg](https://github.com/havvg)) - -## [v0.9.2](https://github.com/liip/LiipImagineBundle/tree/v0.9.2) (2013-04-08) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.9.1...v0.9.2) - -- Add background filter [\#171](https://github.com/liip/LiipImagineBundle/pull/171) ([maxbeutel](https://github.com/maxbeutel)) -- made the phpcr loader search for the requested path with or without a file extension [\#169](https://github.com/liip/LiipImagineBundle/pull/169) ([lsmith77](https://github.com/lsmith77)) -- use composer require command [\#160](https://github.com/liip/LiipImagineBundle/pull/160) ([gimler](https://github.com/gimler)) -- Update installation.md [\#159](https://github.com/liip/LiipImagineBundle/pull/159) ([dlondero](https://github.com/dlondero)) -- Update README.md [\#158](https://github.com/liip/LiipImagineBundle/pull/158) ([dlondero](https://github.com/dlondero)) - -## [v0.9.1](https://github.com/liip/LiipImagineBundle/tree/v0.9.1) (2013-02-20) -[Full Changelog](https://github.com/liip/LiipImagineBundle/compare/v0.9.0...v0.9.1) - -- added the 'strip' filter [\#152](https://github.com/liip/LiipImagineBundle/pull/152) ([uwej711](https://github.com/uwej711)) - -## [v0.9.0](https://github.com/liip/LiipImagineBundle/tree/v0.9.0) (2013-02-13) -- add FilterManager::applyFilter [\#150](https://github.com/liip/LiipImagineBundle/pull/150) ([havvg](https://github.com/havvg)) -- add "Introduction" chapter to documentation [\#149](https://github.com/liip/LiipImagineBundle/pull/149) ([havvg](https://github.com/havvg)) -- split documentation and README into chapters [\#148](https://github.com/liip/LiipImagineBundle/pull/148) ([havvg](https://github.com/havvg)) -- Add route options to routing loader [\#138](https://github.com/liip/LiipImagineBundle/pull/138) ([sveriger](https://github.com/sveriger)) -- Added a data loader for PHPCR [\#134](https://github.com/liip/LiipImagineBundle/pull/134) ([Burgov](https://github.com/Burgov)) -- minor cleanup [\#133](https://github.com/liip/LiipImagineBundle/pull/133) ([havvg](https://github.com/havvg)) -- Add image form type [\#130](https://github.com/liip/LiipImagineBundle/pull/130) ([EmmanuelVella](https://github.com/EmmanuelVella)) -- New minor Imagine version [\#129](https://github.com/liip/LiipImagineBundle/pull/129) ([jcrombez](https://github.com/jcrombez)) -- Pathinfo-related notices in generateUrl\(\) [\#128](https://github.com/liip/LiipImagineBundle/pull/128) ([thanosp](https://github.com/thanosp)) -- Updated the Imagine library to version 0.4.0 [\#127](https://github.com/liip/LiipImagineBundle/pull/127) ([ubick](https://github.com/ubick)) -- Added some documentation to Outside the web root chapter [\#122](https://github.com/liip/LiipImagineBundle/pull/122) ([nass600](https://github.com/nass600)) -- Added PasteFilterLoader [\#118](https://github.com/liip/LiipImagineBundle/pull/118) ([lmcd](https://github.com/lmcd)) -- add info on the StreamWrapper of GaufretteBundle [\#115](https://github.com/liip/LiipImagineBundle/pull/115) ([havvg](https://github.com/havvg)) -- Properly set config parameter in the container [\#113](https://github.com/liip/LiipImagineBundle/pull/113) ([kevinarcher](https://github.com/kevinarcher)) -- Adding cache directory permissions configuration parameter [\#112](https://github.com/liip/LiipImagineBundle/pull/112) ([kevinarcher](https://github.com/kevinarcher)) -- Renamed "auto\_clear\_cache" to "cache\_clearer" [\#102](https://github.com/liip/LiipImagineBundle/pull/102) ([Spea](https://github.com/Spea)) -- Added option to disable cache\_clearer [\#101](https://github.com/liip/LiipImagineBundle/pull/101) ([Spea](https://github.com/Spea)) -- Cache resolver service argument order in readme [\#100](https://github.com/liip/LiipImagineBundle/pull/100) ([johnnypeck](https://github.com/johnnypeck)) -- Added GridFS Loader [\#99](https://github.com/liip/LiipImagineBundle/pull/99) ([jdewit](https://github.com/jdewit)) -- Update composer.json [\#95](https://github.com/liip/LiipImagineBundle/pull/95) ([krispypen](https://github.com/krispypen)) -- Use the basePath in the file path resolver \(useful in "\_dev" or "\_\*" env... [\#92](https://github.com/liip/LiipImagineBundle/pull/92) ([khepin](https://github.com/khepin)) -- add basePath injection to filesystem resolver [\#91](https://github.com/liip/LiipImagineBundle/pull/91) ([havvg](https://github.com/havvg)) -- add "using the controller as a service" to the documentation [\#88](https://github.com/liip/LiipImagineBundle/pull/88) ([inmarelibero](https://github.com/inmarelibero)) -- minor fix in readme [\#87](https://github.com/liip/LiipImagineBundle/pull/87) ([stefax](https://github.com/stefax)) -- ensure that hardcoded filter formats are applied [\#86](https://github.com/liip/LiipImagineBundle/pull/86) ([lsmith77](https://github.com/lsmith77)) -- fixed \#81 cache clearer only registered for sf2.1 [\#82](https://github.com/liip/LiipImagineBundle/pull/82) ([digitalkaoz](https://github.com/digitalkaoz)) -- Issue 43 - Added a cache clearer for generated images [\#80](https://github.com/liip/LiipImagineBundle/pull/80) ([sixty-nine](https://github.com/sixty-nine)) -- added NoCacheResolver [\#76](https://github.com/liip/LiipImagineBundle/pull/76) ([ghost](https://github.com/ghost)) -- Fixed errors in README.md [\#75](https://github.com/liip/LiipImagineBundle/pull/75) ([iamdto](https://github.com/iamdto)) -- add LoggerInterface to AmazonS3Resolver [\#70](https://github.com/liip/LiipImagineBundle/pull/70) ([havvg](https://github.com/havvg)) -- fix AmazonS3Resolver [\#69](https://github.com/liip/LiipImagineBundle/pull/69) ([havvg](https://github.com/havvg)) -- several fixes to the AmazonS3Resolver based on feedback [\#68](https://github.com/liip/LiipImagineBundle/pull/68) ([havvg](https://github.com/havvg)) -- move getFilePath to AbstractFilesystemResolver [\#67](https://github.com/liip/LiipImagineBundle/pull/67) ([havvg](https://github.com/havvg)) -- add AmazonS3Resolver and ResolverInterface::remove [\#66](https://github.com/liip/LiipImagineBundle/pull/66) ([havvg](https://github.com/havvg)) -- Throwing an error if source image doesn't exist [\#65](https://github.com/liip/LiipImagineBundle/pull/65) ([fixe](https://github.com/fixe)) -- add GaufretteFilesystemLoader [\#63](https://github.com/liip/LiipImagineBundle/pull/63) ([havvg](https://github.com/havvg)) -- Mark image services as non public [\#62](https://github.com/liip/LiipImagineBundle/pull/62) ([lstrojny](https://github.com/lstrojny)) -- Updates PdfTransformer so that imagick is injected [\#61](https://github.com/liip/LiipImagineBundle/pull/61) ([lucasaba](https://github.com/lucasaba)) -- add crop filter; add missing option for thumbnail filter [\#58](https://github.com/liip/LiipImagineBundle/pull/58) ([gimler](https://github.com/gimler)) -- Add file transformers to the file loader [\#57](https://github.com/liip/LiipImagineBundle/pull/57) ([lucasaba](https://github.com/lucasaba)) -- Use of protected class properties in FilesystemLoader [\#54](https://github.com/liip/LiipImagineBundle/pull/54) ([petrjaros](https://github.com/petrjaros)) -- 'cache\_resolver' property name change [\#53](https://github.com/liip/LiipImagineBundle/pull/53) ([petrjaros](https://github.com/petrjaros)) -- add composer.json [\#51](https://github.com/liip/LiipImagineBundle/pull/51) ([iampersistent](https://github.com/iampersistent)) -- Fix for last version of symfony [\#50](https://github.com/liip/LiipImagineBundle/pull/50) ([benji07](https://github.com/benji07)) -- Allowed a file extension to be inferred for source files without one [\#47](https://github.com/liip/LiipImagineBundle/pull/47) ([web-dev](https://github.com/web-dev)) -- Added a configuration option for the data root. [\#46](https://github.com/liip/LiipImagineBundle/pull/46) ([web-dev](https://github.com/web-dev)) -- README update: source img outside web root [\#45](https://github.com/liip/LiipImagineBundle/pull/45) ([scoolen](https://github.com/scoolen)) -- Fixing typo in README.md [\#44](https://github.com/liip/LiipImagineBundle/pull/44) ([stefanosala](https://github.com/stefanosala)) -- update template extension and helper names [\#41](https://github.com/liip/LiipImagineBundle/pull/41) ([iampersistent](https://github.com/iampersistent)) -- Refactor RelativeResize code and add documentation [\#39](https://github.com/liip/LiipImagineBundle/pull/39) ([jmikola](https://github.com/jmikola)) -- Add Resize and RelativeResize filters [\#37](https://github.com/liip/LiipImagineBundle/pull/37) ([jmikola](https://github.com/jmikola)) -- Extracted the abstract class Resolver from WebPathResolver [\#35](https://github.com/liip/LiipImagineBundle/pull/35) ([sixty-nine](https://github.com/sixty-nine)) -- fix service name [\#34](https://github.com/liip/LiipImagineBundle/pull/34) ([lenar](https://github.com/lenar)) -- Removed webRoot logic outside controller [\#28](https://github.com/liip/LiipImagineBundle/pull/28) ([LouTerrailloune](https://github.com/LouTerrailloune)) -- Fixed redirect using wrong variable [\#27](https://github.com/liip/LiipImagineBundle/pull/27) ([Spea](https://github.com/Spea)) -- Tweak response creation [\#26](https://github.com/liip/LiipImagineBundle/pull/26) ([lsmith77](https://github.com/lsmith77)) -- fixed unit tests, fixes GH-22 [\#24](https://github.com/liip/LiipImagineBundle/pull/24) ([ghost](https://github.com/ghost)) -- added missing docblock [\#20](https://github.com/liip/LiipImagineBundle/pull/20) ([LouTerrailloune](https://github.com/LouTerrailloune)) -- allow-all default setting for liip\_imagine.formats [\#14](https://github.com/liip/LiipImagineBundle/pull/14) ([ghost](https://github.com/ghost)) -- added support for many filter transformations in one filter set \(style\), fixes GH-1 [\#11](https://github.com/liip/LiipImagineBundle/pull/11) ([ghost](https://github.com/ghost)) -- fixed ImagineLoader - cache prefix was not used in urls [\#6](https://github.com/liip/LiipImagineBundle/pull/6) ([ghost](https://github.com/ghost)) -- fixed CachePathResolver\#getBrowserPath [\#5](https://github.com/liip/LiipImagineBundle/pull/5) ([ghost](https://github.com/ghost)) -- Added check for the existence of extension info [\#147](https://github.com/liip/LiipImagineBundle/pull/147) ([thanosp](https://github.com/thanosp)) -- add Tests for bundle features [\#140](https://github.com/liip/LiipImagineBundle/pull/140) ([havvg](https://github.com/havvg)) - - - -\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* + +## [v1.3.2](https://github.com/liip/LiipImagineBundle/tree/1.3.2) + +*Released on* 2015-12-10 *and assigned* [`1.3.2`](https://github.com/liip/LiipImagineBundle/releases/tag/1.3.2) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.3.1...1.3.2)\).* + +- Removed UrlGenerator deprecations from Symfony 2.8. [\#673](https://github.com/liip/LiipImagineBundle/pull/673) *([sebastianblum](https://github.com/sebastianblum))* +- Typo. [\#668](https://github.com/liip/LiipImagineBundle/pull/668) *([benoitMariaux](https://github.com/benoitMariaux))* +- Misc. fixes and improvements to the docs. [\#667](https://github.com/liip/LiipImagineBundle/pull/667) *([javiereguiluz](https://github.com/javiereguiluz))* +- Skip MongoDB ODM related tests on PHP7 and HHVM. [\#659](https://github.com/liip/LiipImagineBundle/pull/659) *([lsmith77](https://github.com/lsmith77))* +- Fix all test fails in master \(just to check\). [\#658](https://github.com/liip/LiipImagineBundle/pull/658) *([kamazee](https://github.com/kamazee))* +- Fix handling invalid orientation in AutoRotateFilterLoader & test exceptions. [\#657](https://github.com/liip/LiipImagineBundle/pull/657) *([kamazee](https://github.com/kamazee))* +- Fix broken CacheResolver tests \(see [\#650](https://github.com/liip/LiipImagineBundle/issues/650)\). [\#655](https://github.com/liip/LiipImagineBundle/pull/655) *([kamazee](https://github.com/kamazee))* +- Correctly handles all rotations, even those involving flippin. [\#654](https://github.com/liip/LiipImagineBundle/pull/654) *([Heshyo](https://github.com/Heshyo))* +- Incorporate feedback from @WouterJ for PR 651. [\#653](https://github.com/liip/LiipImagineBundle/pull/653) *([kix](https://github.com/kix))* +- Applied fixes from StyleCI. [\#652](https://github.com/liip/LiipImagineBundle/pull/652) *([lsmith77](https://github.com/lsmith77))* +- Add notes on basic usage. [\#651](https://github.com/liip/LiipImagineBundle/pull/651) *([kix](https://github.com/kix))* +- Fix travis php version. [\#649](https://github.com/liip/LiipImagineBundle/pull/649) *([Koc](https://github.com/Koc))* +- Update StreamLoader.php. [\#648](https://github.com/liip/LiipImagineBundle/pull/648) *([kix](https://github.com/kix))* +- Applied fixes from StyleCI. [\#646](https://github.com/liip/LiipImagineBundle/pull/646) *([lsmith77](https://github.com/lsmith77))* +- Updated build matrix. [\#645](https://github.com/liip/LiipImagineBundle/pull/645) *([lsmith77](https://github.com/lsmith77))* +- Fix typo. [\#634](https://github.com/liip/LiipImagineBundle/pull/634) *([trsteel88](https://github.com/trsteel88))* +- Added support for special characters and white spaces in image name. [\#629](https://github.com/liip/LiipImagineBundle/pull/629) *([ivanbarlog](https://github.com/ivanbarlog))* +- Updated docs for features introduced in Symfony 2.4. [\#621](https://github.com/liip/LiipImagineBundle/pull/621) *([foaly-nr1](https://github.com/foaly-nr1))* +- Use identity instead equality. [\#619](https://github.com/liip/LiipImagineBundle/pull/619) *([piotrantosik](https://github.com/piotrantosik))* +- Context parameter cannot be an empty string. [\#618](https://github.com/liip/LiipImagineBundle/pull/618) *([aistis-](https://github.com/aistis-))* +- Introduced DownscaleFilterLoader. [\#610](https://github.com/liip/LiipImagineBundle/pull/610) *([sascha-meissner](https://github.com/sascha-meissner))* + + +## [v1.3.1](https://github.com/liip/LiipImagineBundle/tree/1.3.1) + +*Released on* 2015-08-27 *and assigned* [`1.3.1`](https://github.com/liip/LiipImagineBundle/releases/tag/1.3.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.3.0...1.3.1)\).* + +- Fix deprecated twig filter syntax. [\#631](https://github.com/liip/LiipImagineBundle/pull/631) *([Rattler3](https://github.com/Rattler3))* +- Fix invalid yaml. [\#623](https://github.com/liip/LiipImagineBundle/pull/623) *([carlcraig](https://github.com/carlcraig))* +- Switch to docker based travis infrastructure. [\#622](https://github.com/liip/LiipImagineBundle/pull/622) *([lsmith77](https://github.com/lsmith77))* +- Return string, not Twig\_Markup object in Twig extension. [\#615](https://github.com/liip/LiipImagineBundle/pull/615) *([lstrojny](https://github.com/lstrojny))* +- Use is\_file\(\) instead of Filesystem::exists\(\). [\#614](https://github.com/liip/LiipImagineBundle/pull/614) *([lstrojny](https://github.com/lstrojny))* +- Make it easier to get a dev environment up and running. [\#613](https://github.com/liip/LiipImagineBundle/pull/613) *([lstrojny](https://github.com/lstrojny))* +- Fix code block into README. [\#608](https://github.com/liip/LiipImagineBundle/pull/608) *([PedroTroller](https://github.com/PedroTroller))* +- Fix upscale size not being calculated correctly. [\#561](https://github.com/liip/LiipImagineBundle/pull/561) *([scuben](https://github.com/scuben))* + + +## [v1.3.0](https://github.com/liip/LiipImagineBundle/tree/1.3.0) + +*Released on* 2015-06-04 *and assigned* [`1.3.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.3.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.7...1.3.0)\).* + +- Use setFactory service definition method for Symfony \>= 2.6 \(when possible\). [\#566](https://github.com/liip/LiipImagineBundle/pull/566) *([adam187](https://github.com/adam187))* + + +## [v1.2.7](https://github.com/liip/LiipImagineBundle/tree/1.2.7) + +*Released on* 2015-06-02 *and assigned* [`1.2.7`](https://github.com/liip/LiipImagineBundle/releases/tag/1.2.7) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.6...1.2.7)\).* + +- Make AwsS3Resolver compatible with SDK v3. [\#605](https://github.com/liip/LiipImagineBundle/pull/605) *([cdaguerre](https://github.com/cdaguerre))* +- __\[Documentation\]__ Add missing coma and fix indentation in README.md. [\#604](https://github.com/liip/LiipImagineBundle/pull/604) *([grena](https://github.com/grena))* +- Removed TransformerInterface. [\#603](https://github.com/liip/LiipImagineBundle/pull/603) *([rvanlaarhoven](https://github.com/rvanlaarhoven))* +- Remove duplicate parameter. [\#601](https://github.com/liip/LiipImagineBundle/pull/601) *([ip512](https://github.com/ip512))* +- Fix typo. [\#600](https://github.com/liip/LiipImagineBundle/pull/600) *([hpatoio](https://github.com/hpatoio))* +- Adding details to use the bundle with remote images. [\#569](https://github.com/liip/LiipImagineBundle/pull/569) *([flug](https://github.com/flug))* + + +## [v1.2.6](https://github.com/liip/LiipImagineBundle/tree/1.2.6) + +*Released on* 2015-04-24 *and assigned* [`1.2.6`](https://github.com/liip/LiipImagineBundle/releases/tag/1.2.6) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.5...1.2.6)\).* + +- Check $filters is an array. [\#596](https://github.com/liip/LiipImagineBundle/pull/596) *([trsteel88](https://github.com/trsteel88))* + + +## [v1.2.5](https://github.com/liip/LiipImagineBundle/tree/1.2.5) + +*Released on* 2015-04-08 *and assigned* [`1.2.5`](https://github.com/liip/LiipImagineBundle/releases/tag/1.2.5) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.4...1.2.5)\).* + +- Add image rotate filter. [\#588](https://github.com/liip/LiipImagineBundle/pull/588) *([bocharsky-bw](https://github.com/bocharsky-bw))* +- Run php-cs-fixer on bundle. [\#583](https://github.com/liip/LiipImagineBundle/pull/583) *([trsteel88](https://github.com/trsteel88))* +- Fix typo. [\#582](https://github.com/liip/LiipImagineBundle/pull/582) *([bicpi](https://github.com/bicpi))* +- Fix typos. [\#581](https://github.com/liip/LiipImagineBundle/pull/581) *([bicpi](https://github.com/bicpi))* +- Fix typos. [\#580](https://github.com/liip/LiipImagineBundle/pull/580) *([bicpi](https://github.com/bicpi))* + + +## [v1.2.4](https://github.com/liip/LiipImagineBundle/tree/1.2.4) + +*Released on* 2015-03-27 *and assigned* [`1.2.4`](https://github.com/liip/LiipImagineBundle/releases/tag/1.2.4) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.3...1.2.4)\).* + +- Update how missing filters are logged. [\#579](https://github.com/liip/LiipImagineBundle/pull/579) *([trsteel88](https://github.com/trsteel88))* +- Use isDefined method for OptionsResolver instead of isKnown \(when possible\). [\#567](https://github.com/liip/LiipImagineBundle/pull/567) *([adam187](https://github.com/adam187))* + + +## [v1.2.3](https://github.com/liip/LiipImagineBundle/tree/1.2.3) + +*Released on* 2015-02-22 *and assigned* [`1.2.3`](https://github.com/liip/LiipImagineBundle/releases/tag/1.2.3) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.2...1.2.3)\).* + +- Fix invalid in\_array. [\#565](https://github.com/liip/LiipImagineBundle/pull/565) *([digitalkaoz](https://github.com/digitalkaoz))* +- Add a short introductory paragraph about the bundle. [\#559](https://github.com/liip/LiipImagineBundle/pull/559) *([javiereguiluz](https://github.com/javiereguiluz))* +- Update Filters.rst. [\#556](https://github.com/liip/LiipImagineBundle/pull/556) *([Spawnrad](https://github.com/Spawnrad))* +- Fixed the syntax of the internal doc links. [\#554](https://github.com/liip/LiipImagineBundle/pull/554) *([javiereguiluz](https://github.com/javiereguiluz))* +- Updated README.md to point to new .rst doc files. [\#551](https://github.com/liip/LiipImagineBundle/pull/551) *([Khez](https://github.com/Khez))* +- Fix typo on readme file. [\#550](https://github.com/liip/LiipImagineBundle/pull/550) *([erivello](https://github.com/erivello))* +- Switched the documentation from Markdown to ReStructuredText. [\#545](https://github.com/liip/LiipImagineBundle/pull/545) *([javiereguiluz](https://github.com/javiereguiluz))* +- Fix Filter Documentation. [\#544](https://github.com/liip/LiipImagineBundle/pull/544) *([wodka](https://github.com/wodka))* +- Add support for the new quality options. [\#473](https://github.com/liip/LiipImagineBundle/pull/473) *([patrickli](https://github.com/patrickli))* + + +## [v1.2.2](https://github.com/liip/LiipImagineBundle/tree/1.2.2) + +*Released on* 2015-01-08 *and assigned* [`1.2.2`](https://github.com/liip/LiipImagineBundle/releases/tag/1.2.2) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.1...1.2.2)\).* + +- Update the filter\_sets Documentation about removed configurations. [\#543](https://github.com/liip/LiipImagineBundle/pull/543) *([mbiagetti](https://github.com/mbiagetti))* +- Implement interlace filter. [\#503](https://github.com/liip/LiipImagineBundle/pull/503) *([wodka](https://github.com/wodka))* + + +## [v1.2.1](https://github.com/liip/LiipImagineBundle/tree/1.2.1) + +*Released on* 2014-12-10 *and assigned* [`1.2.1`](https://github.com/liip/LiipImagineBundle/releases/tag/1.2.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.2.0...1.2.1)\).* + +- Argument to s3 resolver prototype definition has been added. [\#536](https://github.com/liip/LiipImagineBundle/pull/536) *([ruslan-polutsygan](https://github.com/ruslan-polutsygan))* + + +## [v1.2.0](https://github.com/liip/LiipImagineBundle/tree/1.2.0) + +*Released on* 2014-12-10 *and assigned* [`1.2.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.2.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.1.1...1.2.0)\).* + +- S3 resolver put options. [\#535](https://github.com/liip/LiipImagineBundle/pull/535) *([ruslan-polutsygan](https://github.com/ruslan-polutsygan))* +- __\[Minor\]__ Fixed PHPDoc. [\#528](https://github.com/liip/LiipImagineBundle/pull/528) *([sdaoudi](https://github.com/sdaoudi))* + + +## [v1.1.1](https://github.com/liip/LiipImagineBundle/tree/1.1.1) + +*Released on* 2014-11-12 *and assigned* [`1.1.1`](https://github.com/liip/LiipImagineBundle/releases/tag/1.1.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.1.0...1.1.1)\).* + +- Fix crash when no post processor is defined. [\#526](https://github.com/liip/LiipImagineBundle/pull/526) *([lolautruche](https://github.com/lolautruche))* +- __\[Cache Resolver\]__ Sanitize URL to directory name in web path resolved. [\#480](https://github.com/liip/LiipImagineBundle/pull/480) *([teohhanhui](https://github.com/teohhanhui))* + + +## [v1.1.0](https://github.com/liip/LiipImagineBundle/tree/1.1.0) + +*Released on* 2014-10-29 *and assigned* [`1.1.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.1.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.8...1.1.0)\).* + +- __\[Post Processor\]__ Handlers to be applied on filtered image binary. [\#519](https://github.com/liip/LiipImagineBundle/pull/519) *([kostiklv](https://github.com/kostiklv))* + + +## [v1.0.8](https://github.com/liip/LiipImagineBundle/tree/1.0.8) + +*Released on* 2014-10-22 *and assigned* [`1.0.8`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.8) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.7...1.0.8)\).* + +- Delete АГГЗ.jpeg. [\#515](https://github.com/liip/LiipImagineBundle/pull/515) *([crash21](https://github.com/crash21))* +- Update configuration.md. [\#513](https://github.com/liip/LiipImagineBundle/pull/513) *([hugohenrique](https://github.com/hugohenrique))* + + +## [v1.0.7](https://github.com/liip/LiipImagineBundle/tree/1.0.7) + +*Released on* 2014-10-18 *and assigned* [`1.0.7`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.7) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.6...1.0.7)\).* + +- Fix tests, upgrade phpunit up to 4.3. [\#511](https://github.com/liip/LiipImagineBundle/pull/511) *([makasim](https://github.com/makasim))* +- Image default when notloadable exception. [\#510](https://github.com/liip/LiipImagineBundle/pull/510) *([Neime](https://github.com/Neime))* +- Explain how to change the default resolver. [\#508](https://github.com/liip/LiipImagineBundle/pull/508) *([dbu](https://github.com/dbu))* +- Updated DI configuration to the current implementation of the loader. [\#500](https://github.com/liip/LiipImagineBundle/pull/500) *([peterrehm](https://github.com/peterrehm))* +- Support custom output format for each filter set. [\#477](https://github.com/liip/LiipImagineBundle/pull/477) *([teohhanhui](https://github.com/teohhanhui))* + + +## [v1.0.6](https://github.com/liip/LiipImagineBundle/tree/1.0.6) + +*Released on* 2014-09-17 *and assigned* [`1.0.6`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.6) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.5...1.0.6)\).* + +- Fix GridFSLoader. [\#461](https://github.com/liip/LiipImagineBundle/pull/461) *([aldeck](https://github.com/aldeck))* + + +## [v1.0.5](https://github.com/liip/LiipImagineBundle/tree/1.0.5) + +*Released on* 2014-09-15 *and assigned* [`1.0.5`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.5) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.4...1.0.5)\).* + +- Check if runtimeconfig path is stored. [\#498](https://github.com/liip/LiipImagineBundle/pull/498) *([trsteel88](https://github.com/trsteel88))* +- Update README.md. [\#490](https://github.com/liip/LiipImagineBundle/pull/490) *([JellyBellyDev](https://github.com/JellyBellyDev))* +- Update README.md. [\#488](https://github.com/liip/LiipImagineBundle/pull/488) *([JellyBellyDev](https://github.com/JellyBellyDev))* +- Fix auto rotate. [\#476](https://github.com/liip/LiipImagineBundle/pull/476) *([scuben](https://github.com/scuben))* +- Support animated gif. [\#466](https://github.com/liip/LiipImagineBundle/pull/466) *([scuben](https://github.com/scuben))* + + +## [v1.0.4](https://github.com/liip/LiipImagineBundle/tree/1.0.4) + +*Released on* 2014-07-30 *and assigned* [`1.0.4`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.4) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.3...1.0.4)\).* + +- Update WebPathResolverFactory.php. [\#467](https://github.com/liip/LiipImagineBundle/pull/467) *([JJK801](https://github.com/JJK801))* + + +## [v1.0.3](https://github.com/liip/LiipImagineBundle/tree/1.0.3) + +*Released on* 2014-07-30 *and assigned* [`1.0.3`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.3) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.2...1.0.3)\).* + +- Fixing issue with removed class Color. [\#458](https://github.com/liip/LiipImagineBundle/pull/458) *([lstrojny](https://github.com/lstrojny))* +- Added PHP 5.6 and HHVM to travis.yml. [\#454](https://github.com/liip/LiipImagineBundle/pull/454) *([Nyholm](https://github.com/Nyholm))* +- Make the Bundle compatible with config:dump-reference command. [\#452](https://github.com/liip/LiipImagineBundle/pull/452) *([lsmith77](https://github.com/lsmith77))* + + +## [v1.0.2](https://github.com/liip/LiipImagineBundle/tree/1.0.2) + +*Released on* 2014-06-24 *and assigned* [`1.0.2`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.2) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.1...1.0.2)\).* + +- Update README.md. [\#447](https://github.com/liip/LiipImagineBundle/pull/447) *([sgaze](https://github.com/sgaze))* +- Update configuration.md. [\#446](https://github.com/liip/LiipImagineBundle/pull/446) *([sgaze](https://github.com/sgaze))* + + +## [v1.0.1](https://github.com/liip/LiipImagineBundle/tree/1.0.1) + +*Released on* 2014-06-06 *and assigned* [`1.0.1`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0...1.0.1)\).* + +- __\[Stream\]__ throws exception when content cannot be read. [\#444](https://github.com/liip/LiipImagineBundle/pull/444) *([makasim](https://github.com/makasim))* +- Remove unused use-statement and fix phpdoc. [\#441](https://github.com/liip/LiipImagineBundle/pull/441) *([UFOMelkor](https://github.com/UFOMelkor))* + + +## [v1.0.0](https://github.com/liip/LiipImagineBundle/tree/1.0.0) + +*Released on* 2014-05-22 *and assigned* [`1.0.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha7...1.0.0)\).* + +- Added possibility to use imagine new metadata api. [\#413](https://github.com/liip/LiipImagineBundle/pull/413) *([digitalkaoz](https://github.com/digitalkaoz))* + + +## [v1.0.0-alpha7](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha7) + +*Released on* 2014-05-22 *and assigned* [`1.0.0-alpha7`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.0-alpha7) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha6...1.0.0-alpha7)\).* + +- Add a Signer Utility to sign filters, run php-cs-fixer on bundle. [\#405](https://github.com/liip/LiipImagineBundle/pull/405) *([trsteel88](https://github.com/trsteel88))* + + +## [v1.0.0-alpha6](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha6) + +*Released on* 2014-05-05 *and assigned* [`1.0.0-alpha6`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.0-alpha6) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha5...1.0.0-alpha6)\).* + +- __\[Router\]__ remove custom route loader. [\#425](https://github.com/liip/LiipImagineBundle/pull/425) *([makasim](https://github.com/makasim))* + + +## [v1.0.0-alpha5](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha5) + +*Released on* 2014-04-29 *and assigned* [`1.0.0-alpha5`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.0-alpha5) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha4...1.0.0-alpha5)\).* + +- Added scrutinizer config. [\#420](https://github.com/liip/LiipImagineBundle/pull/420) *([digitalkaoz](https://github.com/digitalkaoz))* +- Fixed testsuite \(see [\#417](https://github.com/liip/LiipImagineBundle/issues/417) and [\#403](https://github.com/liip/LiipImagineBundle/issues/403)\). [\#419](https://github.com/liip/LiipImagineBundle/pull/419) *([ama3ing](https://github.com/ama3ing))* +- Increase test coverage report. [\#417](https://github.com/liip/LiipImagineBundle/pull/417) *([digitalkaoz](https://github.com/digitalkaoz))* +- Enabled Symfony 2.4 on travis. [\#416](https://github.com/liip/LiipImagineBundle/pull/416) *([digitalkaoz](https://github.com/digitalkaoz))* +- Update configuration.md. [\#410](https://github.com/liip/LiipImagineBundle/pull/410) *([ama3ing](https://github.com/ama3ing))* +- __\[CI\]__ run tests only on 2.3 version. [\#407](https://github.com/liip/LiipImagineBundle/pull/407) *([makasim](https://github.com/makasim))* +- Watermark filter documentation update \(fixes [\#404](https://github.com/liip/LiipImagineBundle/issues/404)\). [\#406](https://github.com/liip/LiipImagineBundle/pull/406) *([ama3ing](https://github.com/ama3ing))* +- Replace NotFoundHttpException with SourceNotFoundException \(fixes [\#373](https://github.com/liip/LiipImagineBundle/issues/373)\). [\#403](https://github.com/liip/LiipImagineBundle/pull/403) *([ama3ing](https://github.com/ama3ing))* +- Removed unreachable statement. [\#402](https://github.com/liip/LiipImagineBundle/pull/402) *([ama3ing](https://github.com/ama3ing))* +- Trim of forwarding slash in path \(fix for [\#369](https://github.com/liip/LiipImagineBundle/issues/369)\). [\#401](https://github.com/liip/LiipImagineBundle/pull/401) *([ama3ing](https://github.com/ama3ing))* + + +## [v1.0.0-alpha4](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha4) + +*Released on* 2014-04-14 *and assigned* [`1.0.0-alpha4`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.0-alpha4) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha3...1.0.0-alpha4)\).* + +- __\[Config\]__ correctly process resolvers\loaders section if not array or null. [\#396](https://github.com/liip/LiipImagineBundle/pull/396) *([makasim](https://github.com/makasim))* +- Wrong image path \(see [\#368](https://github.com/liip/LiipImagineBundle/issues/368)\). [\#395](https://github.com/liip/LiipImagineBundle/pull/395) *([serdyuka](https://github.com/serdyuka))* + + +## [v1.0.0-alpha3](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha3) + +*Released on* 2014-04-14 *and assigned* [`1.0.0-alpha3`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.0-alpha3) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha2...1.0.0-alpha3)\).* + +- Added proxy to aws s3 resolver factory. [\#392](https://github.com/liip/LiipImagineBundle/pull/392) *([serdyuka](https://github.com/serdyuka))* + + +## [v1.0.0-alpha2](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha2) + +*Released on* 2014-04-10 *and assigned* [`1.0.0-alpha2`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.0-alpha2) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.0.0-alpha1...1.0.0-alpha2)\).* + +- Documentation update \(fixes [\#389](https://github.com/liip/LiipImagineBundle/issues/389)\). [\#390](https://github.com/liip/LiipImagineBundle/pull/390) *([ama3ing](https://github.com/ama3ing))* +- __\[WIP\]__ Added resolve events to cache manager. [\#388](https://github.com/liip/LiipImagineBundle/pull/388) *([serdyuka](https://github.com/serdyuka))* + + +## [v1.0.0-alpha1](https://github.com/liip/LiipImagineBundle/tree/1.0.0-alpha1) + +*Released on* 2014-04-07 *and assigned* [`1.0.0-alpha1`](https://github.com/liip/LiipImagineBundle/releases/tag/1.0.0-alpha1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.21.1...1.0.0-alpha1)\).* + +- Remove cli command. [\#387](https://github.com/liip/LiipImagineBundle/pull/387) *([serdyuka](https://github.com/serdyuka))* +- Fixed and improved tests for resolve cache command. [\#386](https://github.com/liip/LiipImagineBundle/pull/386) *([serdyuka](https://github.com/serdyuka))* +- __\[Config\]__ Fix default loader not found bug. [\#385](https://github.com/liip/LiipImagineBundle/pull/385) *([makasim](https://github.com/makasim))* +- Resolve command few paths. [\#383](https://github.com/liip/LiipImagineBundle/pull/383) *([serdyuka](https://github.com/serdyuka))* +- Move data loaders to binary folder. [\#382](https://github.com/liip/LiipImagineBundle/pull/382) *([serdyuka](https://github.com/serdyuka))* +- Documentation for cli command. [\#380](https://github.com/liip/LiipImagineBundle/pull/380) *([serdyuka](https://github.com/serdyuka))* +- Cli command to resolve cache. [\#379](https://github.com/liip/LiipImagineBundle/pull/379) *([serdyuka](https://github.com/serdyuka))* +- Update README.md. [\#374](https://github.com/liip/LiipImagineBundle/pull/374) *([daslicht](https://github.com/daslicht))* +- __\[Data Loader\]__ cleanup filesystem loader, simplify logic, add factory. [\#371](https://github.com/liip/LiipImagineBundle/pull/371) *([makasim](https://github.com/makasim))* +- __\[Cache Resolver\]__ allow configure cache\_prefix via factory. [\#370](https://github.com/liip/LiipImagineBundle/pull/370) *([makasim](https://github.com/makasim))* +- Set web\_path resolver as default if not configured. [\#367](https://github.com/liip/LiipImagineBundle/pull/367) *([makasim](https://github.com/makasim))* +- __\[Config\]__ remove path option. [\#366](https://github.com/liip/LiipImagineBundle/pull/366) *([makasim](https://github.com/makasim))* +- Fixed yaml code block on stream loader documentation. [\#363](https://github.com/liip/LiipImagineBundle/pull/363) *([rvanlaarhoven](https://github.com/rvanlaarhoven))* +- __\[Cache Resolver\]__ Use baseUrl and port while generating image path. [\#362](https://github.com/liip/LiipImagineBundle/pull/362) *([makasim](https://github.com/makasim))* +- Removed cache\_clearer documentation. [\#359](https://github.com/liip/LiipImagineBundle/pull/359) *([rvanlaarhoven](https://github.com/rvanlaarhoven))* +- CacheManager updated. [\#355](https://github.com/liip/LiipImagineBundle/pull/355) *([ossinkine](https://github.com/ossinkine))* +- FilesystemLoader updated. [\#354](https://github.com/liip/LiipImagineBundle/pull/354) *([ossinkine](https://github.com/ossinkine))* +- Update filters.md. [\#346](https://github.com/liip/LiipImagineBundle/pull/346) *([zazoomauro](https://github.com/zazoomauro))* + + +## [v0.21.1](https://github.com/liip/LiipImagineBundle/tree/v0.21.1) + +*Released on* 2014-03-14 *and assigned* [`0.21.1`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.21.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.21.0...v0.21.1)\).* + + +## [v0.21.0](https://github.com/liip/LiipImagineBundle/tree/v0.21.0) + +*Released on* 2014-03-14 *and assigned* [`0.21.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.21.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.20.2...v0.21.0)\).* + +- Added reference on how to get image path inside a controller. [\#340](https://github.com/liip/LiipImagineBundle/pull/340) *([ama3ing](https://github.com/ama3ing))* +- Add phpunit as require-dev. [\#339](https://github.com/liip/LiipImagineBundle/pull/339) *([makasim](https://github.com/makasim))* +- Twig helper not escape filter url. [\#337](https://github.com/liip/LiipImagineBundle/pull/337) *([makasim](https://github.com/makasim))* +- Added cache clearing & setting cachePrefix for Aws S3. [\#336](https://github.com/liip/LiipImagineBundle/pull/336) *([rvanlaarhoven](https://github.com/rvanlaarhoven))* +- Merge latest changes in master to develop branch. [\#334](https://github.com/liip/LiipImagineBundle/pull/334) *([makasim](https://github.com/makasim))* +- Update [avalanche123/Imagine](https://github.com/avalanche123/Imagine) to 0.6. [\#330](https://github.com/liip/LiipImagineBundle/pull/330) *([vlastv](https://github.com/vlastv))* +- __\[Config\]__ Cleanup bundle configuration. [\#325](https://github.com/liip/LiipImagineBundle/pull/325) *([makasim](https://github.com/makasim))* +- __\[Filter\]__ Dynamic filters. [\#313](https://github.com/liip/LiipImagineBundle/pull/313) *([makasim](https://github.com/makasim))* + + +## [v0.20.2](https://github.com/liip/LiipImagineBundle/tree/v0.20.2) + +*Released on* 2014-02-20 *and assigned* [`0.20.2`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.20.2) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.20.1...v0.20.2)\).* + +- GridFSLoader Bug. [\#331](https://github.com/liip/LiipImagineBundle/pull/331) *([peterrehm](https://github.com/peterrehm))* +- Update filters.md. [\#327](https://github.com/liip/LiipImagineBundle/pull/327) *([herb123456](https://github.com/herb123456))* + + +## [v0.20.1](https://github.com/liip/LiipImagineBundle/tree/v0.20.1) + +*Released on* 2014-02-10 *and assigned* [`0.20.1`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.20.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.20.0...v0.20.1)\).* + +- Fixed ProxyResolver-\>getBrowserPath. [\#323](https://github.com/liip/LiipImagineBundle/pull/323) *([digitalkaoz](https://github.com/digitalkaoz))* + + +## [v0.20.0](https://github.com/liip/LiipImagineBundle/tree/v0.20.0) + +*Released on* 2014-02-07 *and assigned* [`0.20.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.20.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.19.0...v0.20.0)\).* + +- __\[Cache Resolver\]__ Decouple WebPathResolver from http request. Simplify its logic. [\#320](https://github.com/liip/LiipImagineBundle/pull/320) *([makasim](https://github.com/makasim))* +- Added proxy cache resolver. [\#318](https://github.com/liip/LiipImagineBundle/pull/318) *([digitalkaoz](https://github.com/digitalkaoz))* + + +## [v0.19.0](https://github.com/liip/LiipImagineBundle/tree/v0.19.0) + +*Released on* 2014-02-07 *and assigned* [`0.19.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.19.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.18.0...v0.19.0)\).* + +- Improved exception on generation failure. [\#321](https://github.com/liip/LiipImagineBundle/pull/321) *([digitalkaoz](https://github.com/digitalkaoz))* +- Added background\_image filter. [\#319](https://github.com/liip/LiipImagineBundle/pull/319) *([digitalkaoz](https://github.com/digitalkaoz))* +- Fix tests on current develop branch. [\#316](https://github.com/liip/LiipImagineBundle/pull/316) *([makasim](https://github.com/makasim))* +- __\[Cache Resolver\]__ CacheResolver has to cache isStored method too. [\#308](https://github.com/liip/LiipImagineBundle/pull/308) *([makasim](https://github.com/makasim))* +- __\[Cache Resolver\]__ Improve caches invalidation. [\#304](https://github.com/liip/LiipImagineBundle/pull/304) *([makasim](https://github.com/makasim))* + + +## [v0.18.0](https://github.com/liip/LiipImagineBundle/tree/v0.18.0) + +*Released on* 2014-01-29 *and assigned* [`0.18.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.18.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.17.1...v0.18.0)\).* + +- Added an "auto\_rotate" filter based on exif data. [\#254](https://github.com/liip/LiipImagineBundle/pull/254) *([digitalkaoz](https://github.com/digitalkaoz))* + + +## [v0.17.1](https://github.com/liip/LiipImagineBundle/tree/v0.17.1) + +*Released on* 2014-01-24 *and assigned* [`0.17.1`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.17.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.17.0...v0.17.1)\).* + +- Fixed missing namespace. [\#306](https://github.com/liip/LiipImagineBundle/pull/306) *([digitalkaoz](https://github.com/digitalkaoz))* +- __\[Cache\]__ cache manager has to use isStored inside getBrowserPath method. [\#303](https://github.com/liip/LiipImagineBundle/pull/303) *([makasim](https://github.com/makasim))* +- __\[Cache Resolver\]__ Use binary on store method call. [\#301](https://github.com/liip/LiipImagineBundle/pull/301) *([makasim](https://github.com/makasim))* +- __\[Filter Manager\]__ make use of binary object. [\#297](https://github.com/liip/LiipImagineBundle/pull/297) *([makasim](https://github.com/makasim))* +- __\[Data Loader\]__ remove deprecated phpcr loader. [\#292](https://github.com/liip/LiipImagineBundle/pull/292) *([makasim](https://github.com/makasim))* +- Rework data loaders. Introduce mime type guesser. [\#291](https://github.com/liip/LiipImagineBundle/pull/291) *([makasim](https://github.com/makasim))* +- __\[Tests\]__ increase code coverage by tests. [\#290](https://github.com/liip/LiipImagineBundle/pull/290) *([makasim](https://github.com/makasim))* +- __\[Logger\]__ use PSR one logger. [\#286](https://github.com/liip/LiipImagineBundle/pull/286) *([makasim](https://github.com/makasim))* +- __\[Cache Resolver\]__ Resolver get rid of get browser path. [\#284](https://github.com/liip/LiipImagineBundle/pull/284) *([makasim](https://github.com/makasim))* +- __\[Tests\]__ use real amazon libs in tests. [\#283](https://github.com/liip/LiipImagineBundle/pull/283) *([makasim](https://github.com/makasim))* +- __\[Cache Resolver\]__ do not expose "targetPath". [\#282](https://github.com/liip/LiipImagineBundle/pull/282) *([makasim](https://github.com/makasim))* +- __\[Cache Resolver\]__ remove request parameter. [\#281](https://github.com/liip/LiipImagineBundle/pull/281) *([makasim](https://github.com/makasim))* + + +## [v0.17.0](https://github.com/liip/LiipImagineBundle/tree/v0.17.0) + +*Released on* 2013-12-04 *and assigned* [`0.17.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.17.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.16.0...v0.17.0)\).* + +- Handle image extensions in doctrine loader. [\#276](https://github.com/liip/LiipImagineBundle/pull/276) *([dbu](https://github.com/dbu))* +- Exclude Tests directory on composer archive. [\#274](https://github.com/liip/LiipImagineBundle/pull/274) *([oziks](https://github.com/oziks))* +- Fix composer require-dev. [\#272](https://github.com/liip/LiipImagineBundle/pull/272) *([havvg](https://github.com/havvg))* +- Update filters.md. [\#267](https://github.com/liip/LiipImagineBundle/pull/267) *([uwej711](https://github.com/uwej711))* +- Add comment for image parameter in watermark filter configuration example. [\#263](https://github.com/liip/LiipImagineBundle/pull/263) *([USvER](https://github.com/USvER))* + + +## [v0.16.0](https://github.com/liip/LiipImagineBundle/tree/v0.16.0) + +*Released on* 2013-09-30 *and assigned* [`0.16.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.16.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.15.1...v0.16.0)\).* + +- Add Upscale filter. [\#248](https://github.com/liip/LiipImagineBundle/pull/248) *([maximecolin](https://github.com/maximecolin))* + + +## [v0.15.1](https://github.com/liip/LiipImagineBundle/tree/v0.15.1) + +*Released on* 2013-09-20 *and assigned* [`0.15.1`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.15.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.15.0...v0.15.1)\).* + +- Set ContentType of AWS cache object. [\#246](https://github.com/liip/LiipImagineBundle/pull/246) *([eXtreme](https://github.com/eXtreme))* + + +## [v0.15.0](https://github.com/liip/LiipImagineBundle/tree/v0.15.0) + +*Released on* 2013-09-18 *and assigned* [`0.15.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.15.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.14.0...v0.15.0)\).* + +- Deprecate the phpcr loader as CmfMediaBundle provides a better one now. [\#243](https://github.com/liip/LiipImagineBundle/pull/243) *([dbu](https://github.com/dbu))* +- Fix missing filename in exception. [\#240](https://github.com/liip/LiipImagineBundle/pull/240) *([havvg](https://github.com/havvg))* +- Corrected aws-sdk-php link. [\#233](https://github.com/liip/LiipImagineBundle/pull/233) *([javiacei](https://github.com/javiacei))* + + +## [v0.14.0](https://github.com/liip/LiipImagineBundle/tree/v0.14.0) + +*Released on* 2013-08-21 *and assigned* [`0.14.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.14.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.13.0...v0.14.0)\).* + +- Add AwsS3Resolver for new SDK version. [\#227](https://github.com/liip/LiipImagineBundle/pull/227) *([havvg](https://github.com/havvg))* + + +## [v0.13.0](https://github.com/liip/LiipImagineBundle/tree/v0.13.0) + +*Released on* 2013-08-19 *and assigned* [`0.13.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.13.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.12.0...v0.13.0)\).* + +- Watermark loader. [\#222](https://github.com/liip/LiipImagineBundle/pull/222) *([KingCrunch](https://github.com/KingCrunch))* + + +## [v0.12.0](https://github.com/liip/LiipImagineBundle/tree/v0.12.0) + +*Released on* 2013-08-19 *and assigned* [`0.12.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.12.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.11.1...v0.12.0)\).* + +- Update [avalanche123/Imagine](https://github.com/avalanche123/Imagine) to 0.5. [\#221](https://github.com/liip/LiipImagineBundle/pull/221) *([KingCrunch](https://github.com/KingCrunch))* + + +## [v0.11.1](https://github.com/liip/LiipImagineBundle/tree/v0.11.1) + +*Released on* 2013-08-05 *and assigned* [`0.11.1`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.11.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.11.0...v0.11.1)\).* + +- Added documentation on inset and outbound modes of thumbnail filter Documentation \(see [\#207](https://github.com/liip/LiipImagineBundle/issues/207)\). [\#210](https://github.com/liip/LiipImagineBundle/pull/210) *([rjbijl](https://github.com/rjbijl))* + + +## [v0.11.0](https://github.com/liip/LiipImagineBundle/tree/v0.11.0) + +*Released on* 2013-06-21 *and assigned* [`0.11.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.11.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.10.1...v0.11.0)\).* + +- Add link filter. [\#201](https://github.com/liip/LiipImagineBundle/pull/201) *([EmmanuelVella](https://github.com/EmmanuelVella))* +- Thumbnail filter was not applied when allow\_upscale=true and one dimension. [\#200](https://github.com/liip/LiipImagineBundle/pull/200) *([teohhanhui](https://github.com/teohhanhui))* +- Add badge poser in README. [\#199](https://github.com/liip/LiipImagineBundle/pull/199) *([agiuliano](https://github.com/agiuliano))* +- Add docs about allow\_scale of thumbnail filter. [\#198](https://github.com/liip/LiipImagineBundle/pull/198) *([havvg](https://github.com/havvg))* +- Add documentation on S3 object URL options. [\#197](https://github.com/liip/LiipImagineBundle/pull/197) *([havvg](https://github.com/havvg))* + + +## [v0.10.1](https://github.com/liip/LiipImagineBundle/tree/v0.10.1) + +*Released on* 2013-05-29 *and assigned* [`0.10.1`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.10.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.10.0...v0.10.1)\).* + +- Mkdir\(\) doesn't take care about the umask. [\#189](https://github.com/liip/LiipImagineBundle/pull/189) *([KingCrunch](https://github.com/KingCrunch))* +- The quickest PR to review I guess. [\#188](https://github.com/liip/LiipImagineBundle/pull/188) *([Sydney-o9](https://github.com/Sydney-o9))* + + +## [v0.10.0](https://github.com/liip/LiipImagineBundle/tree/v0.10.0) + +*Released on* 2013-05-17 *and assigned* [`0.10.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.10.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.9.4...v0.10.0)\).* + +- CacheResolver. [\#184](https://github.com/liip/LiipImagineBundle/pull/184) *([havvg](https://github.com/havvg))* +- Fix broken tests on windows. [\#179](https://github.com/liip/LiipImagineBundle/pull/179) *([kevinarcher](https://github.com/kevinarcher))* + + +## [v0.9.4](https://github.com/liip/LiipImagineBundle/tree/v0.9.4) + +*Released on* 2013-05-14 *and assigned* [`0.9.4`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.9.4) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.9.3...v0.9.4)\).* + +- Fix doc of CacheManager::resolve to not lie. [\#186](https://github.com/liip/LiipImagineBundle/pull/186) *([dbu](https://github.com/dbu))* +- Small documentation fix for getting browserPath for a thumb from controller. [\#178](https://github.com/liip/LiipImagineBundle/pull/178) *([leberknecht](https://github.com/leberknecht))* +- Improve phpcr loader doc. [\#177](https://github.com/liip/LiipImagineBundle/pull/177) *([dbu](https://github.com/dbu))* +- Allow Symfony 2.3 and greater. [\#176](https://github.com/liip/LiipImagineBundle/pull/176) *([tommygnr](https://github.com/tommygnr))* + + +## [v0.9.3](https://github.com/liip/LiipImagineBundle/tree/v0.9.3) + +*Released on* 2013-04-17 *and assigned* [`0.9.3`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.9.3) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.9.2...v0.9.3)\).* + +- Add CacheManagerAwareTrait. [\#173](https://github.com/liip/LiipImagineBundle/pull/173) *([havvg](https://github.com/havvg))* + + +## [v0.9.2](https://github.com/liip/LiipImagineBundle/tree/v0.9.2) + +*Released on* 2013-04-08 *and assigned* [`0.9.2`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.9.2) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.9.1...v0.9.2)\).* + +- Add background filter. [\#171](https://github.com/liip/LiipImagineBundle/pull/171) *([maxbeutel](https://github.com/maxbeutel))* +- Made the phpcr loader search for the requested path with or without a file extension. [\#169](https://github.com/liip/LiipImagineBundle/pull/169) *([lsmith77](https://github.com/lsmith77))* +- Use composer require command. [\#160](https://github.com/liip/LiipImagineBundle/pull/160) *([gimler](https://github.com/gimler))* +- Update installation.md. [\#159](https://github.com/liip/LiipImagineBundle/pull/159) *([dlondero](https://github.com/dlondero))* +- Update README.md. [\#158](https://github.com/liip/LiipImagineBundle/pull/158) *([dlondero](https://github.com/dlondero))* + + +## [v0.9.1](https://github.com/liip/LiipImagineBundle/tree/v0.9.1) + +*Released on* 2013-02-20 *and assigned* [`0.9.1`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.9.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/v0.9.0...v0.9.1)\).* + +- Added the 'strip' filter. [\#152](https://github.com/liip/LiipImagineBundle/pull/152) *([uwej711](https://github.com/uwej711))* + + +## [v0.9.0](https://github.com/liip/LiipImagineBundle/tree/v0.9.0) + +*Released on* 2013-02-13 *and assigned* [`0.9.0`](https://github.com/liip/LiipImagineBundle/releases/tag/v0.9.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/371140531ca574af759ef44b8eff5dac43e13df1...v0.9.0)\).* + +- Add FilterManager::applyFilter. [\#150](https://github.com/liip/LiipImagineBundle/pull/150) *([havvg](https://github.com/havvg))* +- Add "Introduction" chapter to documentation. [\#149](https://github.com/liip/LiipImagineBundle/pull/149) *([havvg](https://github.com/havvg))* +- Split documentation and README into chapters. [\#148](https://github.com/liip/LiipImagineBundle/pull/148) *([havvg](https://github.com/havvg))* +- Add route options to routing loader. [\#138](https://github.com/liip/LiipImagineBundle/pull/138) *([sveriger](https://github.com/sveriger))* +- Added a data loader for PHPCR. [\#134](https://github.com/liip/LiipImagineBundle/pull/134) *([Burgov](https://github.com/Burgov))* +- __\[Minor\]__ Cleanup. [\#133](https://github.com/liip/LiipImagineBundle/pull/133) *([havvg](https://github.com/havvg))* +- Add image form type. [\#130](https://github.com/liip/LiipImagineBundle/pull/130) *([EmmanuelVella](https://github.com/EmmanuelVella))* +- New minor Imagine version. [\#129](https://github.com/liip/LiipImagineBundle/pull/129) *([jcrombez](https://github.com/jcrombez))* +- Pathinfo-related notices in generateUrl\(\). [\#128](https://github.com/liip/LiipImagineBundle/pull/128) *([thanosp](https://github.com/thanosp))* +- Updated the Imagine library to version 0.4.0. [\#127](https://github.com/liip/LiipImagineBundle/pull/127) *([ubick](https://github.com/ubick))* +- Added some documentation to Outside the web root chapter. [\#122](https://github.com/liip/LiipImagineBundle/pull/122) *([nass600](https://github.com/nass600))* +- Added PasteFilterLoader. [\#118](https://github.com/liip/LiipImagineBundle/pull/118) *([lmcd](https://github.com/lmcd))* +- Add info on the StreamWrapper of GaufretteBundle. [\#115](https://github.com/liip/LiipImagineBundle/pull/115) *([havvg](https://github.com/havvg))* +- Properly set config parameter in the container. [\#113](https://github.com/liip/LiipImagineBundle/pull/113) *([kevinarcher](https://github.com/kevinarcher))* +- Adding cache directory permissions configuration parameter. [\#112](https://github.com/liip/LiipImagineBundle/pull/112) *([kevinarcher](https://github.com/kevinarcher))* +- Renamed "auto\_clear\_cache" to "cache\_clearer". [\#102](https://github.com/liip/LiipImagineBundle/pull/102) *([Spea](https://github.com/Spea))* +- Added option to disable cache\_clearer. [\#101](https://github.com/liip/LiipImagineBundle/pull/101) *([Spea](https://github.com/Spea))* +- Cache resolver service argument order in readme. [\#100](https://github.com/liip/LiipImagineBundle/pull/100) *([johnnypeck](https://github.com/johnnypeck))* +- Added GridFS Loader. [\#99](https://github.com/liip/LiipImagineBundle/pull/99) *([jdewit](https://github.com/jdewit))* +- Update composer.json. [\#95](https://github.com/liip/LiipImagineBundle/pull/95) *([krispypen](https://github.com/krispypen))* +- Use the basePath in the file path resolver \(useful in "\_dev" or "\_\*" env\). [\#92](https://github.com/liip/LiipImagineBundle/pull/92) *([khepin](https://github.com/khepin))* +- Add basePath injection to filesystem resolver. [\#91](https://github.com/liip/LiipImagineBundle/pull/91) *([havvg](https://github.com/havvg))* +- Add "using the controller as a service" to the documentation. [\#88](https://github.com/liip/LiipImagineBundle/pull/88) *([inmarelibero](https://github.com/inmarelibero))* +- __\[Minor\]__ fix in readme. [\#87](https://github.com/liip/LiipImagineBundle/pull/87) *([stefax](https://github.com/stefax))* +- Ensure that hardcoded filter formats are applied. [\#86](https://github.com/liip/LiipImagineBundle/pull/86) *([lsmith77](https://github.com/lsmith77))* +- Cache clearer only registered for sf2.1 \(fixes [\#81](https://github.com/liip/LiipImagineBundle/issues/81)\). [\#82](https://github.com/liip/LiipImagineBundle/pull/82) *([digitalkaoz](https://github.com/digitalkaoz))* +- Issue 43 - Added a cache clearer for generated images. [\#80](https://github.com/liip/LiipImagineBundle/pull/80) *([sixty-nine](https://github.com/sixty-nine))* +- Added NoCacheResolver. [\#76](https://github.com/liip/LiipImagineBundle/pull/76) *([ghost](https://github.com/ghost))* +- Fixed errors in README.md. [\#75](https://github.com/liip/LiipImagineBundle/pull/75) *([iamdto](https://github.com/iamdto))* +- Add LoggerInterface to AmazonS3Resolver. [\#70](https://github.com/liip/LiipImagineBundle/pull/70) *([havvg](https://github.com/havvg))* +- Fix AmazonS3Resolver. [\#69](https://github.com/liip/LiipImagineBundle/pull/69) *([havvg](https://github.com/havvg))* +- Several fixes to the AmazonS3Resolver based on feedback. [\#68](https://github.com/liip/LiipImagineBundle/pull/68) *([havvg](https://github.com/havvg))* +- Move getFilePath to AbstractFilesystemResolver. [\#67](https://github.com/liip/LiipImagineBundle/pull/67) *([havvg](https://github.com/havvg))* +- Add AmazonS3Resolver and ResolverInterface::remove. [\#66](https://github.com/liip/LiipImagineBundle/pull/66) *([havvg](https://github.com/havvg))* +- Throwing an error if source image doesn't exist. [\#65](https://github.com/liip/LiipImagineBundle/pull/65) *([fixe](https://github.com/fixe))* +- Add GaufretteFilesystemLoader. [\#63](https://github.com/liip/LiipImagineBundle/pull/63) *([havvg](https://github.com/havvg))* +- Mark image services as non public. [\#62](https://github.com/liip/LiipImagineBundle/pull/62) *([lstrojny](https://github.com/lstrojny))* +- Updates PdfTransformer so that imagick is injected. [\#61](https://github.com/liip/LiipImagineBundle/pull/61) *([lucasaba](https://github.com/lucasaba))* +- Add crop filter; add missing option for thumbnail filter. [\#58](https://github.com/liip/LiipImagineBundle/pull/58) *([gimler](https://github.com/gimler))* +- Add file transformers to the file loader. [\#57](https://github.com/liip/LiipImagineBundle/pull/57) *([lucasaba](https://github.com/lucasaba))* +- Use of protected class properties in FilesystemLoader. [\#54](https://github.com/liip/LiipImagineBundle/pull/54) *([petrjaros](https://github.com/petrjaros))* +- 'cache\_resolver' property name change. [\#53](https://github.com/liip/LiipImagineBundle/pull/53) *([petrjaros](https://github.com/petrjaros))* +- Add composer.json. [\#51](https://github.com/liip/LiipImagineBundle/pull/51) *([iampersistent](https://github.com/iampersistent))* +- Fix for last version of Symfony. [\#50](https://github.com/liip/LiipImagineBundle/pull/50) *([benji07](https://github.com/benji07))* +- Allowed a file extension to be inferred for source files without one. [\#47](https://github.com/liip/LiipImagineBundle/pull/47) *([web-dev](https://github.com/web-dev))* +- Added a configuration option for the data root. [\#46](https://github.com/liip/LiipImagineBundle/pull/46) *([web-dev](https://github.com/web-dev))* +- README update: source img outside web root. [\#45](https://github.com/liip/LiipImagineBundle/pull/45) *([scoolen](https://github.com/scoolen))* +- Fixing typo in README.md. [\#44](https://github.com/liip/LiipImagineBundle/pull/44) *([stefanosala](https://github.com/stefanosala))* +- Update template extension and helper names. [\#41](https://github.com/liip/LiipImagineBundle/pull/41) *([iampersistent](https://github.com/iampersistent))* +- Refactor RelativeResize code and add documentation. [\#39](https://github.com/liip/LiipImagineBundle/pull/39) *([jmikola](https://github.com/jmikola))* +- Add Resize and RelativeResize filters. [\#37](https://github.com/liip/LiipImagineBundle/pull/37) *([jmikola](https://github.com/jmikola))* +- Extracted the abstract class Resolver from WebPathResolver. [\#35](https://github.com/liip/LiipImagineBundle/pull/35) *([sixty-nine](https://github.com/sixty-nine))* +- Fix service name. [\#34](https://github.com/liip/LiipImagineBundle/pull/34) *([lenar](https://github.com/lenar))* +- Removed webRoot logic outside controller. [\#28](https://github.com/liip/LiipImagineBundle/pull/28) *([LouTerrailloune](https://github.com/LouTerrailloune))* +- Fixed redirect using wrong variable. [\#27](https://github.com/liip/LiipImagineBundle/pull/27) *([Spea](https://github.com/Spea))* +- Tweak response creation. [\#26](https://github.com/liip/LiipImagineBundle/pull/26) *([lsmith77](https://github.com/lsmith77))* +- Fixed unit tests, fixes GH-22. [\#24](https://github.com/liip/LiipImagineBundle/pull/24) *([ghost](https://github.com/ghost))* +- Added missing docblock. [\#20](https://github.com/liip/LiipImagineBundle/pull/20) *([LouTerrailloune](https://github.com/LouTerrailloune))* +- Allow-all default setting for liip\_imagine.formats. [\#14](https://github.com/liip/LiipImagineBundle/pull/14) *([ghost](https://github.com/ghost))* +- Added support for many filter transformations in one filter set \(style\), fixes GH-1. [\#11](https://github.com/liip/LiipImagineBundle/pull/11) *([ghost](https://github.com/ghost))* +- Fixed ImagineLoader - Cache prefix was not used in urls. [\#6](https://github.com/liip/LiipImagineBundle/pull/6) *([ghost](https://github.com/ghost))* +- Fixed CachePathResolver\#getBrowserPath. [\#5](https://github.com/liip/LiipImagineBundle/pull/5) *([ghost](https://github.com/ghost))* +- Added check for the existence of extension info. [\#147](https://github.com/liip/LiipImagineBundle/pull/147) *([thanosp](https://github.com/thanosp))* +- Add Tests for bundle features. [\#140](https://github.com/liip/LiipImagineBundle/pull/140) *([havvg](https://github.com/havvg))* + +--- + +*The templates for new release changelog entries are created using +[github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator); the final formatting and edits are +completed manually by one of the many [project contributors](https://github.com/liip/LiipImagineBundle/graphs/contributors).* diff --git a/UPGRADE.md b/UPGRADE.md index 11df0b01a..210034bee 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,23 +1,40 @@ + # Upgrade -## 1.9.0 +All important upgrade requirements will be enumerated in this +[`UPGRADE.md`](https://github.com/liip/LiipImagineBundle/blob/1.0/UPGRADE.md) file and all notable alterations to this project will be documented in the +[`CHANGELOG.md`](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md) file. +This project adheres to [semantic versioning](http://semver.org/spec/v2.0.0.html). + + +## v1.9.0 - - __[Data Loader]__ The arguments for the `FileSystemLoader` class constructor have changed. Passing an array of roots +*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v190) for the `1.9.0` release.* + + - __\[Data Loader\]__ The arguments for the `FileSystemLoader` class constructor have changed. Passing an array of roots as the third parameter and an (optional) `LocatorInterace` as the fourth parameter is deprecated. A `LocatorInterface` should now be passed as third parameter, and the array of data roots to the `LocatorInterface::__construct()` method directly. All prior signatures will continue to work until `2.0` is release. - - __[Console]__ Added the `--force` parameter to resolve console command to force image resolution regardless of cache. + + - __\[Console\]__ Added the `--force` parameter to resolve console command to force image resolution regardless of cache. Added the `--as-script` parameter to resolve console command to disable verbose, "prettified" output. - - __[Composer]__ Imagine library upgraded to version 0.7.x. -## 1.8.0 + - __\[Composer\]__ Imagine library upgraded to version 0.7.x. + - - __[Routing]__ The `Resources/config/routing.xml` file has been deprecated and will be removed in `2.0`. Use the new +## v1.8.0 + +*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v180) for the `1.8.0` release.* + + - __\[Routing\]__ The `Resources/config/routing.xml` file has been deprecated and will be removed in `2.0`. Use the new YAML variant moving forward `Resources/config/routing.yaml`. -## 1.7.3 - - __[Data Loader]__ The `FileSystemLoader` now allows you to assign keys to data roots, and directly reference them when +## v1.7.3 + +*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v173) for the `1.7.3` release.* + + - __\[Data Loader\]__ The `FileSystemLoader` now allows you to assign keys to data roots, and directly reference them when requesting resources. ```yml @@ -39,7 +56,7 @@ are given indexes of their short bundle name (for example, given the bundle `FooBundle`, you can request a file from its public resources path via `@FooBundle:path/to/file.ext`). - - __[Data Loader]__ The `FileSystemLoader` now supports automatically registering the `Resources/public` folders within + - __\[Data Loader\]__ The `FileSystemLoader` now supports automatically registering the `Resources/public` folders within all loaded bundles. This can be enabled via the following configuration. ```yml @@ -82,12 +99,15 @@ ``` - - __[Data Locator]__ The `*Locator` services passed to `FileSystemLoader` are now marked as "non-shared" or "prototype" + - __\[Data Locator\]__ The `*Locator` services passed to `FileSystemLoader` are now marked as "non-shared" or "prototype" within the DI container, resulting in new instances being passed every time the services are requested. -## 1.7.2 - - __[Data Loader]__ The `FileSystemLoader`'s resource locator has been abstracted out into `FileSystemLocator` +## v1.7.2 + +*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v172) for the `1.7.2` release.* + + - __\[Data Loader\]__ The `FileSystemLoader`'s resource locator has been abstracted out into `FileSystemLocator` (provides the same `realpath`-based locator algorithm introduced in the `1.7.0` release) and `FileSystemInsecureLocator` (provides the old locator algorithm from version `1.6.x` and prior). @@ -96,11 +116,11 @@ relies heavily on multi-level symbolic links that renders the new locator difficult (and sometime impossible) to setup. - - __[Deprecation]__ __[Data Loader]__ Instantiating `FileSystemLoader` without providing a forth constructor argument + - __\[Deprecation\]__ __\[Data Loader\]__ Instantiating `FileSystemLoader` without providing a forth constructor argument of signature `\Liip\ImagineBundle\Binary\Locator\LocatorInterface $locator` is deprecated and the ability to do so will be removed in the next major release, `2.0`. - - __[Configuration]__ The `liip_imagine.loaders.default.filesystem.locator` bundle configuration option has been + - __\[Configuration\]__ The `liip_imagine.loaders.default.filesystem.locator` bundle configuration option has been introduced and allows the following `enum` values: `filesystem` and `filesystem_insecure`. These correspond to the aforementioned `FileSystemLocator` and `FileSystemInsecureLocator` resource locator implementations that affect the behavior of `FileSystemLoader`. This option defaults to `filesystem`. @@ -123,9 +143,9 @@ setup. ``` - - __[Dependency Injection]__ All compiler passes (filters, post-processors, data loaders, cache resolvers, etc) have + - __\[Dependency Injection\]__ All compiler passes (filters, post-processors, data loaders, cache resolvers, etc) have been updated to log their behavior, allowing you to easily debug tagged services, including both core-provided and - custom services defined by your application). In Symfony `>= 3.2` this output is located in the + custom services defined by your application). In Symfony `>= 3.2` this output is located in the `var/cache/[dev|prod|env]/app*ProjectContainerCompiler.log` file. Output will be similar to the following example on a fresh install. @@ -156,18 +176,21 @@ setup. ``` -## 1.7.1 - - __[Data Loader]__ The `FileSystemLoader` data loader performs a more robust security check against image resource +## v1.7.1 + +*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v171) for the `1.7.1` release.* + + - __\[Data Loader\]__ The `FileSystemLoader` data loader performs a more robust security check against image resource paths to ensure they reside within the defined data root path(s). If utilizing symbolic links, you should reference the troubleshooting guide at the end of this upgrade notice. - - __[Data Loader]__ The `FileSystemLoader` data loader now accepts an array of paths (as strings) for its third + - __\[Data Loader\]__ The `FileSystemLoader` data loader now accepts an array of paths (as strings) for its third constructor argument, enabling the loader to check multiple paths for the requested image resource. *Note: this change breaks those extending this class and relying on the protected property `$dataRoot`, which has been renamed to `$dataRoots` and is now of the type `string[]` instead of the prior type of `string`.* - - __[Configuration]__ The `liip_imagine.loaders.default.filesystem.data_root` bundle configuration option now accepts + - __\[Configuration\]__ The `liip_imagine.loaders.default.filesystem.data_root` bundle configuration option now accepts an array of paths (as strings) or a single scalar path ()if only one is required for your configuration), allowing the `filesystem` data loader to check multiple data root paths for the requested image resource. The following YML configuration shows examples for all allowed value types. @@ -191,21 +214,24 @@ setup. data_root: /single/root/path ``` - - - __[Troubleshooting]__ If you are using the `filesystem` data loader and have *symbolic links* within the `data_root` + + - __\[Troubleshooting\]__ If you are using the `filesystem` data loader and have *symbolic links* within the `data_root` that point outside this path (the `data_root` option defaults to `%kernel.root_dir%/../web`) then you are *required* to configure all outside resource paths as additional `data_root` paths using the following option key in your application's configuration: `liip_imagine.loaders.default.filesystem.data_root`. - + The following is a list of the most common exception error messages encountered when the `data_root` option is not correctly configured: - > Source image not resolvable "%s" in root path(s) "%s" - > Source image invalid "%s" as it is outside of the defined root path(s) "%s" -## 1.0.0-alpha7 - - __[Configuration]__ `liip_imagine.controller_action` option was removed in favour of an array of actions. See +## v1.0.0-alpha7 + +*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v100-alpha7) for the `1.0.0-alpha7` release.* + + - __\[Configuration\]__ `liip_imagine.controller_action` option was removed in favour of an array of actions. See `liip_imagine.controller` config ```diff @@ -218,106 +244,112 @@ setup. ``` -## 1.0.0-alpha6 - - __[Route]__ `ImagineLoader` was removed. Please adjust your `app/config/routing.yml` file. +## v1.0.0-alpha6 + +*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v100-alpha6) for the `1.0.0-alpha6` release.* + + - __\[Route\]__ `ImagineLoader` was removed. Please adjust your `app/config/routing.yml` file. ```diff - + -_imagine: - resource: . - type: imagine +_liip_imagine: + resource: "@LiipImagineBundle/Resources/config/routing.xml" - + ``` - - __[Configuration]__ `liip_imagine.filter_sets.route` option and sub options were removed. + - __\[Configuration\]__ `liip_imagine.filter_sets.route` option and sub options were removed. + + - __\[Configuration\]__ `liip_imagine.cache_prefix` option was removed. + - - __[Configuration]__ `liip_imagine.cache_prefix` option was removed. +## v1.0.0-alpha5 -## 1.0.0-alpha5 +*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v100-alpha5) for the `1.0.0-alpha5` release.* - - __[Symfony]__ Required minimum symfony version was updated to 2.3. + - __\[Symfony\]__ Required minimum symfony version was updated to 2.3. - - __[Logger]__ Symfony `LoggerInterface` was replaced with PSR-3 one. + - __\[Logger\]__ Symfony `LoggerInterface` was replaced with PSR-3 one. - - __[Cache]__ New `isStored` method was added. + - __\[Cache\]__ New `isStored` method was added. - - __[Cache]__ The method `ResolverInterface::getBrowserPath` was removed. + - __\[Cache\]__ The method `ResolverInterface::getBrowserPath` was removed. - - __[Cache]__ The method `ResolverInterface::store` accept `BinaryInterface` as first argument. + - __\[Cache\]__ The method `ResolverInterface::store` accept `BinaryInterface` as first argument. - - __[Cache]__ The method `ResolverInterface::store` return nothing. + - __\[Cache\]__ The method `ResolverInterface::store` return nothing. - - __[Cache]__ The method `ResolverInterface::remove` return nothing. + - __\[Cache\]__ The method `ResolverInterface::remove` return nothing. - - __[Cache]__ The method `ResolverInterface::remove` takes required array of filter as first argument. + - __\[Cache\]__ The method `ResolverInterface::remove` takes required array of filter as first argument. - - __[Cache]__ The method `ResolverInterface::remove` takes optional path as second argument. + - __\[Cache\]__ The method `ResolverInterface::remove` takes optional path as second argument. - - __[Cache]__ The method `ResolverInterface::clean` was removed. + - __\[Cache\]__ The method `ResolverInterface::clean` was removed. - - __[Cache]__ The method `ResolverInterface::resolve` takes path and filter as arguments. + - __\[Cache\]__ The method `ResolverInterface::resolve` takes path and filter as arguments. - - __[Cache]__ The method `ResolverInterface::resolve` return absolute url of the cached image. + - __\[Cache\]__ The method `ResolverInterface::resolve` return absolute url of the cached image. - - __[Cache]__ The method `CacheManager::resolve` may throw OutOfBoundsException if required resolver does not exist. + - __\[Cache\]__ The method `CacheManager::resolve` may throw OutOfBoundsException if required resolver does not exist. - - __[Cache]__ The method `CacheManager::resolve` return absolute url of the cached image. + - __\[Cache\]__ The method `CacheManager::resolve` return absolute url of the cached image. - - __[Cache]__ The method `CacheManager::store` accept `BinaryInterface` as first argument. + - __\[Cache\]__ The method `CacheManager::store` accept `BinaryInterface` as first argument. - - __[Cache]__ The method `CacheManager::store` return nothing. + - __\[Cache\]__ The method `CacheManager::store` return nothing. - - __[Cache]__ The method `CacheManager::clearResolversCache` was removed. + - __\[Cache\]__ The method `CacheManager::clearResolversCache` was removed. - - __[Cache]__ The method `CacheManager::getWebRoot` was removed. + - __\[Cache\]__ The method `CacheManager::getWebRoot` was removed. - - __[Cache]__ The method `CacheManager::getBrowserPath` third argument was changed, now it is `runtimeConfig`. + - __\[Cache\]__ The method `CacheManager::getBrowserPath` third argument was changed, now it is `runtimeConfig`. - - __[Cache]__ The method `CacheManager::generateUrl` third argument was changed, now it is `runtimeConfig`. + - __\[Cache\]__ The method `CacheManager::generateUrl` third argument was changed, now it is `runtimeConfig`. - - __[Cache]__ `NoCacheResolver` renamed to `NoCacheWebPathResolver`. + - __\[Cache\]__ `NoCacheResolver` renamed to `NoCacheWebPathResolver`. - - __[Cache]__ `AbstractFilesystemResolver` was removed. + - __\[Cache\]__ `AbstractFilesystemResolver` was removed. - - __[Data Loader]__ `LoaderInterface::find` now can return string or `BinaryInterface` instance. + - __\[Data Loader\]__ `LoaderInterface::find` now can return string or `BinaryInterface` instance. - - __[Data Loader]__ `DataManager::find` now can return `BinaryInterface` instance only. + - __\[Data Loader\]__ `DataManager::find` now can return `BinaryInterface` instance only. - - __[Data Loader]__ All data loaders moved to `Binary/Loader` folder. + - __\[Data Loader\]__ All data loaders moved to `Binary/Loader` folder. - - __[Data Loader]__ Tag name `liip_imagine.data.loader` changed to `liip_imagine.binary.loader` + - __\[Data Loader\]__ Tag name `liip_imagine.data.loader` changed to `liip_imagine.binary.loader` - - __[Data Loader]__ Parameter key `liip_imagine.data.loader.filesystem.class` changed to + - __\[Data Loader\]__ Parameter key `liip_imagine.data.loader.filesystem.class` changed to `liip_imagine.binary.loader.filesystem.class` - - __[Data Loader]__ Parameter key `liip_imagine.data.loader.stream.class` changed to + - __\[Data Loader\]__ Parameter key `liip_imagine.data.loader.stream.class` changed to `liip_imagine.binary.loader.stream.class` - - __[Data Loader]__ Service id `liip_imagine.data.loader.prototype.filesystem` changed to + - __\[Data Loader\]__ Service id `liip_imagine.data.loader.prototype.filesystem` changed to `liip_imagine.binary.loader.prototype.filesystem` - - __[Data Loader]__ Service id `liip_imagine.data.loader.prototype.stream` changed to + - __\[Data Loader\]__ Service id `liip_imagine.data.loader.prototype.stream` changed to `liip_imagine.binary.loader.prototype.stream` - - __[Filter]__ `FilterManager::applyFilter` now return instance of `BinaryInterface`. + - __\[Filter\]__ `FilterManager::applyFilter` now return instance of `BinaryInterface`. - - __[Filter]__ `FilterManager::applyFilter` first argument was changed from Image instance to BinaryInterface one. + - __\[Filter\]__ `FilterManager::applyFilter` first argument was changed from Image instance to BinaryInterface one. - - __[Filter]__ `FilterManager::get` was removed. + - __\[Filter\]__ `FilterManager::get` was removed. - - __[Configuration]__ `liip_imagine.filter_sets.path` option was removed. + - __\[Configuration\]__ `liip_imagine.filter_sets.path` option was removed. - - __[Configuration]__ `liip_imagine.filter_sets.format` option was removed. + - __\[Configuration\]__ `liip_imagine.filter_sets.format` option was removed. - - __[Configuration]__ `liip_imagine.cache_mkdir_mode` option was removed. + - __\[Configuration\]__ `liip_imagine.cache_mkdir_mode` option was removed. - - __[Configuration]__ `liip_imagine.web_root` option was removed. + - __\[Configuration\]__ `liip_imagine.web_root` option was removed. - - __[Configuration]__ `liip_imagine.cache` default value was changed from `web_path`to `default`. + - __\[Configuration\]__ `liip_imagine.cache` default value was changed from `web_path`to `default`. - - __[Configuration]__ `liip_imagine.formats` option was removed. + - __\[Configuration\]__ `liip_imagine.formats` option was removed. - - __[Configuration]__ `liip_imagine.data_root` option was removed. + - __\[Configuration\]__ `liip_imagine.data_root` option was removed. From 2b9f4e66a56b735c15435f5a154eded8d02d0750 Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Sat, 2 Sep 2017 19:08:10 -0400 Subject: [PATCH 25/27] fix typo in upgrade file --- UPGRADE.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index 210034bee..b68000daa 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -9,7 +9,7 @@ This project adheres to [semantic versioning](http://semver.org/spec/v2.0.0.html ## v1.9.0 -*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v190) for the `1.9.0` release.* +*View the [changelog entry](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md#v190) for the `1.9.0` release.* - __\[Data Loader\]__ The arguments for the `FileSystemLoader` class constructor have changed. Passing an array of roots as the third parameter and an (optional) `LocatorInterace` as the fourth parameter is deprecated. A `LocatorInterface` @@ -24,7 +24,7 @@ This project adheres to [semantic versioning](http://semver.org/spec/v2.0.0.html ## v1.8.0 -*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v180) for the `1.8.0` release.* +*View the [changelog entry](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md#v180) for the `1.8.0` release.* - __\[Routing\]__ The `Resources/config/routing.xml` file has been deprecated and will be removed in `2.0`. Use the new YAML variant moving forward `Resources/config/routing.yaml`. @@ -32,7 +32,7 @@ This project adheres to [semantic versioning](http://semver.org/spec/v2.0.0.html ## v1.7.3 -*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v173) for the `1.7.3` release.* +*View the [changelog entry](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md#v173) for the `1.7.3` release.* - __\[Data Loader\]__ The `FileSystemLoader` now allows you to assign keys to data roots, and directly reference them when requesting resources. @@ -105,7 +105,7 @@ This project adheres to [semantic versioning](http://semver.org/spec/v2.0.0.html ## v1.7.2 -*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v172) for the `1.7.2` release.* +*View the [changelog entry](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md#v172) for the `1.7.2` release.* - __\[Data Loader\]__ The `FileSystemLoader`'s resource locator has been abstracted out into `FileSystemLocator` (provides the same `realpath`-based locator algorithm introduced in the `1.7.0` release) and `FileSystemInsecureLocator` @@ -179,7 +179,7 @@ setup. ## v1.7.1 -*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v171) for the `1.7.1` release.* +*View the [changelog entry](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md#v171) for the `1.7.1` release.* - __\[Data Loader\]__ The `FileSystemLoader` data loader performs a more robust security check against image resource paths to ensure they reside within the defined data root path(s). If utilizing symbolic links, you should reference @@ -229,7 +229,7 @@ setup. ## v1.0.0-alpha7 -*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v100-alpha7) for the `1.0.0-alpha7` release.* +*View the [changelog entry](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md#v100-alpha7) for the `1.0.0-alpha7` release.* - __\[Configuration\]__ `liip_imagine.controller_action` option was removed in favour of an array of actions. See `liip_imagine.controller` config @@ -247,7 +247,7 @@ setup. ## v1.0.0-alpha6 -*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v100-alpha6) for the `1.0.0-alpha6` release.* +*View the [changelog entry](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md#v100-alpha6) for the `1.0.0-alpha6` release.* - __\[Route\]__ `ImagineLoader` was removed. Please adjust your `app/config/routing.yml` file. @@ -268,7 +268,7 @@ setup. ## v1.0.0-alpha5 -*View the [changelog entry](https://github.com/robfrawley/LiipImagineBundle/blob/1.0/CHANGELOG.md#v100-alpha5) for the `1.0.0-alpha5` release.* +*View the [changelog entry](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md#v100-alpha5) for the `1.0.0-alpha5` release.* - __\[Symfony\]__ Required minimum symfony version was updated to 2.3. From 6b166b47e26acc824cd6ea95fa035320db06baf8 Mon Sep 17 00:00:00 2001 From: Rob Frawley 2nd Date: Fri, 8 Sep 2017 01:58:46 -0400 Subject: [PATCH 26/27] fix and amend console resolve and remove commands - change --as-script/-s option name/shortcut to --machine-readable/-m to fix collision with core symfony 2.x console options (the long name was changed to provide additional clarity) - change option shortcut and name for "as-script/s" to "machine-readable/m" to fix collision with symfony-provided "shell/s" option from the 2.x series - align output formatting and internal implementation of remove command with the improvements already introduced to the resolve command in 1.9.0 - update help content for both remove and resolve commands to align with updated output formatting and better describe all available functionality of the commands - extracted common functionality between resolve and remove commands to a new shared abstract class to remove code duplication - updated CHANGELOG.md and UPGRADE.md to note BC break and detail additional changes - renamed test filter sets to more realistic names and added some additional ones too - completely rewrote remove command tests and refactored resolve command tests to enable shared fixtures and methods between the two - in test app, moved default web loader to descrete, new "web" named loader and made the "default" loader a chain loader that uses "web" as welll as many of the other ones --- Binary/Loader/FileSystemLoader.php | 2 +- CHANGELOG.md | 11 +- Command/AbstractCacheCommand.php | 242 ++++++++++++ Command/RemoveCacheCommand.php | 129 ++++-- Command/ResolveCacheCommand.php | 216 ++++------ .../Filter/Loader/ResampleFilterLoader.php | 2 +- .../LiipImagineExtensionTest.php | 2 +- .../Binary/Loader/ChainLoaderTest.php | 2 +- .../Command/AbstractCacheCommandTestCase.php | 263 +++++++++++++ .../Command/AbstractCommandTestCase.php | 43 -- .../Command/RemoveCacheCommandTest.php | 340 ++++++++++++++++ Tests/Functional/Command/RemoveCacheTest.php | 269 ------------- .../Command/ResolveCacheCommandTest.php | 306 +++++++++++++++ Tests/Functional/Command/ResolveCacheTest.php | 369 ------------------ .../Controller/ImagineControllerTest.php | 36 +- .../Resources/public/cats-bar-bundle.jpg | Bin 0 -> 34087 bytes .../Fixtures/CacheCommandFixtures.php | 99 +++++ .../Resources/public/cats-foo-bundle.jpg | Bin 0 -> 34087 bytes Tests/Functional/app/config/config.yml | 32 +- .../Loader/ResampleFilterLoaderTest.php | 2 +- UPGRADE.md | 15 + 21 files changed, 1477 insertions(+), 903 deletions(-) create mode 100644 Command/AbstractCacheCommand.php create mode 100644 Tests/Functional/Command/AbstractCacheCommandTestCase.php delete mode 100644 Tests/Functional/Command/AbstractCommandTestCase.php create mode 100644 Tests/Functional/Command/RemoveCacheCommandTest.php delete mode 100644 Tests/Functional/Command/RemoveCacheTest.php create mode 100644 Tests/Functional/Command/ResolveCacheCommandTest.php delete mode 100644 Tests/Functional/Command/ResolveCacheTest.php create mode 100644 Tests/Functional/Fixtures/BarBundle/Resources/public/cats-bar-bundle.jpg create mode 100644 Tests/Functional/Fixtures/CacheCommandFixtures.php create mode 100644 Tests/Functional/Fixtures/FooBundle/Resources/public/cats-foo-bundle.jpg diff --git a/Binary/Loader/FileSystemLoader.php b/Binary/Loader/FileSystemLoader.php index ef7104b0f..529c26f47 100644 --- a/Binary/Loader/FileSystemLoader.php +++ b/Binary/Loader/FileSystemLoader.php @@ -42,7 +42,7 @@ class FileSystemLoader implements LoaderInterface * * @param MimeTypeGuesserInterface $mimeGuesser * @param ExtensionGuesserInterface $extensionGuesser - * @param LocatorInterface + * @param LocatorInterface $locator */ public function __construct(MimeTypeGuesserInterface $mimeGuesser, ExtensionGuesserInterface $extensionGuesser, $locator) { diff --git a/CHANGELOG.md b/CHANGELOG.md index 529132f9d..3c1d47a3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,18 @@ This project adheres to [semantic versioning](http://semver.org/spec/v2.0.0.html ## [Unreleased](https://github.com/liip/LiipImagineBundle/tree/HEAD) *Note: Recent developments can be tracked via the -[latest changelog](https://github.com/liip/LiipImagineBundle/compare/1.9.0...HEAD), the -[active milestone](https://github.com/liip/LiipImagineBundle/milestone/15), as well as all +[latest changelog](https://github.com/liip/LiipImagineBundle/compare/1.9.1...HEAD), the +[active milestone](https://github.com/liip/LiipImagineBundle/milestone/16), as well as all [open milestones](https://github.com/liip/LiipImagineBundle/milestones).* +## [v1.9.1](https://github.com/liip/LiipImagineBundle/tree/1.9.1) + +*Released on* 2017-09-08 *and assigned* [`1.9.1`](https://github.com/liip/LiipImagineBundle/releases/tag/1.9.1) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.9.0...1.9.1)\).* + +- __\[Console\]__ __\[BC BREAK\]__ The resolve command's --as-script/-s option/shortcut renamed to --machine-readable/-m \(fixes [\#988](https://github.com/liip/LiipImagineBundle/pull/988)\), its output updated to aligned with the resolve command, and the "--machine-readable/-m" option added. [\#991](https://github.com/liip/LiipImagineBundle/pull/991) *([robfrawley](https://github.com/robfrawley))* + + ## [v1.9.0](https://github.com/liip/LiipImagineBundle/tree/1.9.0) *Released on* 2017-08-30 *and assigned* [`1.9.0`](https://github.com/liip/LiipImagineBundle/releases/tag/1.9.0) *tag \([view verbose changelog](https://github.com/liip/LiipImagineBundle/compare/1.8.0...1.9.0)\).* diff --git a/Command/AbstractCacheCommand.php b/Command/AbstractCacheCommand.php new file mode 100644 index 000000000..557892eb6 --- /dev/null +++ b/Command/AbstractCacheCommand.php @@ -0,0 +1,242 @@ +output = $output; + $this->machineReadable = $input->getOption('machine-readable'); + $this->actionFailures = 0; + } + + /** + * @param InputInterface $input + * + * @return array + */ + protected function resolveFilters(InputInterface $input) + { + $filters = $input->getOption('filter'); + + if (0 !== count($deprecated = $input->getOption('filters'))) { + $filters = array_merge($filters, $deprecated); + @trigger_error('The --filters option was deprecated in 1.9.0 and removed in 2.0.0. Use the --filter option instead.', E_USER_DEPRECATED); + } + + if (0 === count($filters) && 0 === count($filters = array_keys($this->getFilterManager()->getFilterConfiguration()->all()))) { + $this->output->writeln(' [ERROR] You do not have any configured filters available. '); + } + + return $filters; + } + + /** + * @param string $command + */ + protected function writeCommandHeading($command) + { + if ($this->machineReadable) { + return; + } + + $title = sprintf('[liip/imagine-bundle] %s Image Caches', ucfirst($command)); + $this->writeNewline(); + $this->output->writeln(sprintf('%s', $title)); + $this->output->writeln(str_repeat('=', strlen($title))); + $this->writeNewline(); + } + + /** + * @param string[] $filters + * @param string[] $targets + * @param bool $glob + */ + protected function writeResultSummary(array $filters, array $targets, $glob = false) + { + if ($this->machineReadable) { + return; + } + + $targetCount = count($targets); + $filterCount = count($filters); + $actionCount = ($glob ? $filterCount : ($filterCount * $targetCount)) - $this->actionFailures; + + $this->writeNewline(); + $this->output->writeln(vsprintf(' Completed %d %s (%d %s / %s %s) %s', array( + $actionCount, + $this->getPluralized($actionCount, 'operation'), + $filterCount, + $this->getPluralized($filterCount, 'filter'), + $glob ? '?' : $targetCount, + $this->getPluralized($targetCount, 'image'), + $this->getResultSummaryFailureMarkup(), + ))); + $this->writeNewline(); + } + + /** + * @param string $filter + * @param string|null $target + */ + protected function writeActionStart($filter, $target = null) + { + if (!$this->machineReadable) { + $this->output->write(' - '); + } + + $this->output->write(sprintf('%s[%s] ', $target ?: '*', $filter)); + } + + /** + * @param string $result + * @param bool $continued + */ + protected function writeActionResult($result, $continued = true) + { + $this->output->write($continued ? sprintf('%s: ', $result) : $result); + + if (!$continued) { + $this->writeNewline(); + } + } + + /** + * @param string $detail + */ + protected function writeActionDetail($detail) + { + $this->output->write($detail); + $this->writeNewline(); + } + + /** + * @param \Exception $exception + */ + protected function writeActionException(\Exception $exception) + { + $this->writeActionResult('failure'); + $this->writeActionDetail($exception->getMessage()); + ++$this->actionFailures; + } + + /** + * @return int + */ + protected function getReturnCode() + { + return 0 === $this->actionFailures ? 0 : 255; + } + + /** + * @return CacheManager + */ + protected function getCacheManager() + { + static $manager; + + if (null === $manager) { + $manager = $this->getContainer()->get('liip_imagine.cache.manager'); + } + + return $manager; + } + + /** + * @return FilterManager + */ + protected function getFilterManager() + { + static $manager; + + if (null === $manager) { + $manager = $this->getContainer()->get('liip_imagine.filter.manager'); + } + + return $manager; + } + + /** + * @return DataManager + */ + protected function getDataManager() + { + static $manager; + + if (null === $manager) { + $manager = $this->getContainer()->get('liip_imagine.data.manager'); + } + + return $manager; + } + + /** + * @param int $count + */ + private function writeNewline($count = 1) + { + $this->output->write(str_repeat(PHP_EOL, $count)); + } + + /** + * @param int $size + * @param string $word + * + * @return string + */ + private function getPluralized($size, $word) + { + return 1 === $size ? $word : sprintf('%ss', $word); + } + + /** + * @return string + */ + private function getResultSummaryFailureMarkup() + { + if (0 === $this->actionFailures) { + return ''; + } + + return vsprintf(' encountered %s %s ', array( + $this->actionFailures, + $this->getPluralized($this->actionFailures, 'failure'), + )); + } +} diff --git a/Command/RemoveCacheCommand.php b/Command/RemoveCacheCommand.php index aa8d53514..ab6e33376 100644 --- a/Command/RemoveCacheCommand.php +++ b/Command/RemoveCacheCommand.php @@ -11,41 +11,62 @@ namespace Liip\ImagineBundle\Command; -use Liip\ImagineBundle\Imagine\Cache\CacheManager; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -class RemoveCacheCommand extends ContainerAwareCommand +class RemoveCacheCommand extends AbstractCacheCommand { protected function configure() { $this ->setName('liip:imagine:cache:remove') - ->setDescription('Remove cache for given paths and set of filters.') - ->addArgument('paths', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Image paths') - ->addOption('filters', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'List of filters to remove for passed images (Deprecated, use "filter").') + ->setDescription('Remove asset caches for the passed asset paths(s) and filter name(s)') + ->addArgument('paths', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, + 'Asset paths to remove caches of (passing none will use all paths).') ->addOption('filter', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'List of filters to remove for passed images.') + 'Filter name to remove caches of (passing none will use all registered filters)') + ->addOption('machine-readable', 'm', InputOption::VALUE_NONE, + 'Enable machine parseable output (removing extraneous output and all text styles)') + ->addOption('filters', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'Deprecated in 1.9.0 and removed in 2.0.0 (use the --filter option instead)') ->setHelp(<<<'EOF' -The %command.name% command removes cache by specified parameters. +The %command.name% command removes asset cache for the passed image(s) and filter(s). + +For an application that has only the two files "foo.ext" and "bar.ext", and only the filter sets +named "thumb_sm" and "thumb_lg", the following examples will behave as shown. + +# bin/console %command.name% foo.ext +Removes caches for foo.ext using all configured filters, outputting: + - foo.ext[thumb_sm] removed + - foo.ext[thumb_lg] removed + +# bin/console %command.name% --filter=thumb_sm --filter=thumb_lg foo.ext bar.ext +Removes caches for both foo.ext and bar.ext using thumb_sm and thumb_lg filters, outputting: + - foo.ext[thumb_sm] removed + - foo.ext[thumb_lg] removed + - bar.ext[thumb_sm] removed + - bar.ext[thumb_lg] removed + +# bin/console %command.name% --filter=thumb_sm +Removes all caches for thumb_sm filter, outputting: + - *[thumb_sm] glob-removal -Paths should be separated by spaces: -php app/console %command.name% path1 path2 -All cache for a given `paths` will be lost. +# bin/console %command.name% +Removes all caches for all configured filters, removing all cached assets, outputting: + - *[thumb_sm] glob-removal + - *[thumb_lg] glob-removal -If you use --filter parameter: -php app/console %command.name% --filter=thumb1 --filter=thumb2 -All cache for a given filters will be lost. +# bin/console %command.name% --force --filter=thumb_sm foo.ext +Removing caches for foo.ext using thumb_sm filter when already removed will caused skipping, outputting: + - foo.ext[thumb_sm] skipped -You can combine these parameters: -php app/console %command.name% path1 path2 --filter=thumb1 --filter=thumb2 +# bin/console %command.name% --filter=does_not_exist --filter=thumb_sm foo.ext +Removes caches for foo.ext for thumb_sm while failing inline on invalid filter (or other errors), outputting: + - foo.ext[does_not_exist] failure: Could not find configuration for a filter: does_not_exist + - foo.ext[thumb_sm] removed -php app/console %command.name% -Cache for all paths and filters will be lost when executing this command without parameters. EOF ); } @@ -58,34 +79,68 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $paths = $input->getArgument('paths'); - $filters = $this->resolveInputFilters($input); + $this->initializeInstState($input, $output); + $this->writeCommandHeading('remove'); - if (empty($filters)) { - $filters = null; - } + $filters = $this->resolveFilters($input); + $targets = $input->getArgument('paths'); - /* @var CacheManager cacheManager */ - $cacheManager = $this->getContainer()->get('liip_imagine.cache.manager'); - $cacheManager->remove($paths, $filters); + if (0 === count($targets)) { + $this->doCacheRemoveAsGlobbedFilterName($filters); + } else { + $this->doCacheRemoveAsFiltersAndTargets($filters, $targets); + } - return 0; + return $this->getReturnCode(); } /** - * @param InputInterface $input - * - * @return array|mixed + * @param string[] $filters */ - private function resolveInputFilters(InputInterface $input) + private function doCacheRemoveAsGlobbedFilterName(array $filters) { - $filters = $input->getOption('filter'); + foreach ($filters as $f) { + $this->doCacheRemove($f); + } + + $this->writeResultSummary($filters, array(), true); + } - if (count($filtersDeprecated = $input->getOption('filters'))) { - $filters = array_merge($filters, $filtersDeprecated); - @trigger_error('As of 1.9, use of the "--filters" option has been deprecated in favor of "--filter" and will be removed in 2.0.', E_USER_DEPRECATED); + /** + * @param string[] $filters + * @param string[] $targets + */ + private function doCacheRemoveAsFiltersAndTargets(array $filters, array $targets) + { + foreach ($targets as $t) { + foreach ($filters as $f) { + $this->doCacheRemove($f, $t); + } } - return $filters; + $this->writeResultSummary($filters, $targets); + } + + /** + * @param string $filter + * @param string|null $target + */ + private function doCacheRemove($filter, $target = null) + { + $this->writeActionStart($filter, $target); + + try { + if (null === $target) { + $this->getCacheManager()->remove(null, $filter); + $this->writeActionResult('glob-removal', false); + } elseif ($this->getCacheManager()->isStored($target, $filter)) { + $this->getCacheManager()->remove($target, $filter); + $this->writeActionResult('removed', false); + } else { + $this->writeActionResult('skipped', false); + } + } catch (\Exception $e) { + $this->writeActionException($e); + } } } diff --git a/Command/ResolveCacheCommand.php b/Command/ResolveCacheCommand.php index 0b3209cb3..df9d3cdf7 100644 --- a/Command/ResolveCacheCommand.php +++ b/Command/ResolveCacheCommand.php @@ -11,193 +11,113 @@ namespace Liip\ImagineBundle\Command; -use Liip\ImagineBundle\Imagine\Cache\CacheManager; -use Liip\ImagineBundle\Imagine\Data\DataManager; -use Liip\ImagineBundle\Imagine\Filter\FilterManager; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -class ResolveCacheCommand extends ContainerAwareCommand +class ResolveCacheCommand extends AbstractCacheCommand { protected function configure() { $this ->setName('liip:imagine:cache:resolve') - ->setDescription('Resolve cache for given path and set of filters.') + ->setDescription('Resolves asset caches for the passed asset paths(s) and filter set name(s)') ->addArgument('paths', InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'Any number of image paths to act on.') - ->addOption('filters', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'List of filters to apply to passed images (Deprecated, use "filter").') + 'Asset paths to resolve caches for') ->addOption('filter', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'List of filters to apply to passed images.') + 'Filter name to resolve caches for (passing none will use all registered filters)') ->addOption('force', 'F', InputOption::VALUE_NONE, - 'Force image resolution regardless of cache.') - ->addOption('as-script', 's', InputOption::VALUE_NONE, - 'Only print machine-parseable results.') + 'Force asset cache resolution (ignoring whether it already cached)') + ->addOption('machine-readable', 'm', InputOption::VALUE_NONE, + 'Enable machine parseable output (removing extraneous output and all text styles)') + ->addOption('filters', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'Deprecated in 1.9.0 and removed in 2.0.0 (use the --filter option instead)') ->setHelp(<<<'EOF' -The %command.name% command resolves the passed image(s) for the resolved -filter(s), outputting results using the following basic format: - - "image.ext[filter]" (resolved|cached|failed) as "path/to/cached/image.ext" - -# bin/console %command.name% --filter=thumb1 foo.ext bar.ext -Resolve both foo.ext and bar.ext using thumb1 filter, outputting: - - "foo.ext[thumb1]" resolved as "http://localhost/media/cache/thumb1/foo.ext" - - "bar.ext[thumb1]" resolved as "http://localhost/media/cache/thumb1/bar.ext" +The %command.name% command resolves asset cache for the passed image(s) and filter(s). -# bin/console %command.name% --filter=thumb1 --filter=thumb2 foo.ext -Resolve foo.ext using both thumb1 and thumb2 filters, outputting: - - "foo.ext[thumb1]" resolved as "http://localhost/media/cache/thumb1/foo.ext" - - "foo.ext[thumb2]" resolved as "http://localhost/media/cache/thumb2/foo.ext" +For an application that has only the two files "foo.ext" and "bar.ext", and only the filter sets +named "thumb_sm" and "thumb_lg", the following examples will behave as shown. -# bin/console %command.name% foo.ext -Resolve foo.ext using all configured filters (as none are specified), outputting: - - "foo.ext[thumb1]" resolved as "http://localhost/media/cache/thumb1/foo.ext" - - "foo.ext[thumb2]" resolved as "http://localhost/media/cache/thumb2/foo.ext" +# bin/console %command.name% foo.ext bar.ext +Resolves caches for both foo.ext and bar.ext using all configured filters, outputting: + - foo.ext[thumb_sm] resolved: http://localhost/media/cache/thumb_sm/foo.ext + - bar.ext[thumb_sm] resolved: http://localhost/media/cache/thumb_sm/bar.ext + - foo.ext[thumb_lg] resolved: http://localhost/media/cache/thumb_lg/foo.ext + - bar.ext[thumb_lg] resolved: http://localhost/media/cache/thumb_lg/bar.ext -# bin/console %command.name% --force --filter=thumb1 foo.ext -Resolve foo.ext using thumb1 and force creation regardless of cache, outputting: - - "foo.ext[thumb1]" resolved as "http://localhost/media/cache/thumb1/foo.ext" - -EOF - ); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $force = $input->getOption('force'); - $paths = $input->getArgument('paths'); - $filters = $this->resolveInputFilters($input); - $machine = $input->getOption('as-script'); - $failed = 0; - - $filterManager = $this->getFilterManager(); - $dataManager = $this->getDataManager(); - $cacheManager = $this->getCacheManager(); - - if (0 === count($filters)) { - $filters = array_keys($filterManager->getFilterConfiguration()->all()); - } +# bin/console %command.name% --filter=thumb_sm foo.ext +Resolves caches for foo.ext using only thumb_sm filter, outputting: + - foo.ext[thumb_sm] resolved: http://localhost/media/cache/thumb_sm/foo.ext - $this->outputTitle($output, $machine); - - foreach ($paths as $path) { - foreach ($filters as $filter) { - $output->write(sprintf('- %s[%s] ', $path, $filter)); - - try { - if ($force || !$cacheManager->isStored($path, $filter)) { - $cacheManager->store($filterManager->applyFilter($dataManager->find($filter, $path), $filter), $path, $filter); - $output->write('resolved: '); - } else { - $output->write('cached: '); - } - - $output->writeln($cacheManager->resolve($path, $filter)); - } catch (\Exception $e) { - $output->writeln(sprintf('failed: %s', $e->getMessage())); - ++$failed; - } - } - } +# bin/console %command.name% --filter=thumb_sm --filter=thumb_lg foo.ext +Resolves caches for foo.ext using both thumb_sm and thumb_lg filters, outputting: + - foo.ext[thumb_sm] resolved: http://localhost/media/cache/thumb_sm/foo.ext + - foo.ext[thumb_lg] resolved: http://localhost/media/cache/thumb_lg/foo.ext - $this->outputSummary($output, $machine, count($filters), count($paths), $failed); +# bin/console %command.name% --force --filter=thumb_sm foo.ext +Resolving caches for foo.ext using thumb_sm filter when already cached will caused skipped, outputting: + - foo.ext[thumb_sm] skipped: http://localhost/media/cache/thumb_sm/foo.ext - return 0 === $failed ? 0 : 255; - } +# bin/console %command.name% --force --filter=thumb_sm foo.ext +Resolving caches for foo.ext using thumb_sm filter when already cached with force option re-resolves (ignoring cache), outputting: + - foo.ext[thumb_sm] resolved: http://localhost/media/cache/thumb_sm/foo.ext - /** - * @param OutputInterface $output - * @param bool $machine - */ - private function outputTitle(OutputInterface $output, $machine) - { - if (!$machine) { - $title = '[liip/imagine-bundle] Image Resolver'; +# bin/console %command.name% --filter=does_not_exist --filter=thumb_sm foo.ext +Resolves caches for foo.ext using thumb_sm while failing inline on invalid filter (or other errors), outputting: + - foo.ext[does_not_exist] failed: Could not find configuration for a filter: does_not_exist + - foo.ext[thumb_sm] removed: http://localhost/media/cache/thumb_sm/foo.ext - $output->writeln(sprintf('%s', $title)); - $output->writeln(str_repeat('=', strlen($title))); - $output->writeln(''); - } +EOF + ); } /** + * @param InputInterface $input * @param OutputInterface $output - * @param bool $machine - * @param int $filters - * @param int $paths - * @param int $failed - */ - private function outputSummary(OutputInterface $output, $machine, $filters, $paths, $failed) - { - if (!$machine) { - $operations = ($filters * $paths) - $failed; - - $output->writeln(''); - $output->writeln(vsprintf('Completed %d %s (%d %s on %d %s) %s', array( - $operations, - $this->pluralizeWord($operations, 'operation'), - $filters, - $this->pluralizeWord($filters, 'filter'), - $paths, - $this->pluralizeWord($paths, 'image'), - 0 === $failed ? '' : sprintf('[encountered %d %s]', $failed, $this->pluralizeWord($failed, 'failure')), - ))); - } - } - - /** - * @param int $count - * @param string $singular - * @param string $pluralEnding * - * @return string + * @return int */ - private function pluralizeWord($count, $singular, $pluralEnding = 's') + protected function execute(InputInterface $input, OutputInterface $output) { - return 1 === $count ? $singular : $singular.$pluralEnding; - } + $this->initializeInstState($input, $output); + $this->writeCommandHeading('resolve'); - /** - * @param InputInterface $input - * - * @return array|mixed - */ - private function resolveInputFilters(InputInterface $input) - { - $filters = $input->getOption('filter'); + $filters = $this->resolveFilters($input); + $targets = $input->getArgument('paths'); + $doForce = $input->getOption('force'); - if (count($filtersDeprecated = $input->getOption('filters'))) { - $filters = array_merge($filters, $filtersDeprecated); - @trigger_error('As of 1.9, use of the "--filters" option has been deprecated in favor of "--filter" and will be removed in 2.0.', E_USER_DEPRECATED); + foreach ($targets as $t) { + foreach ($filters as $f) { + $this->doCacheResolve($t, $f, $doForce); + } } - return $filters; - } + $this->writeResultSummary($filters, $targets); - /** - * @return FilterManager - */ - private function getFilterManager() - { - return $this->getContainer()->get('liip_imagine.filter.manager'); + return $this->getReturnCode(); } /** - * @return DataManager + * @param string $target + * @param string $filter + * @param bool $forced */ - private function getDataManager() + private function doCacheResolve($target, $filter, $forced) { - return $this->getContainer()->get('liip_imagine.data.manager'); - } + $this->writeActionStart($filter, $target); + + try { + if ($forced || !$this->getCacheManager()->isStored($target, $filter)) { + $this->getCacheManager()->store($this->getFilterManager()->applyFilter($this->getDataManager()->find($filter, $target), $filter), $target, $filter); + $this->writeActionResult('resolved'); + } else { + $this->writeActionResult('skipped'); + } - /** - * @return CacheManager - */ - private function getCacheManager() - { - return $this->getContainer()->get('liip_imagine.cache.manager'); + $this->writeActionDetail($this->getCacheManager()->resolve($target, $filter)); + } catch (\Exception $e) { + $this->writeActionException($e); + } } } diff --git a/Imagine/Filter/Loader/ResampleFilterLoader.php b/Imagine/Filter/Loader/ResampleFilterLoader.php index 0c91d5579..b19fc5f03 100644 --- a/Imagine/Filter/Loader/ResampleFilterLoader.php +++ b/Imagine/Filter/Loader/ResampleFilterLoader.php @@ -128,7 +128,7 @@ private function resolveOptions(array $options) $resolver->setAllowedValues('unit', array( ImageInterface::RESOLUTION_PIXELSPERINCH, - ImageInterface::RESOLUTION_PIXELSPERCENTIMETER + ImageInterface::RESOLUTION_PIXELSPERCENTIMETER, )); $resolver->setNormalizer('filter', function (Options $options, $value) { diff --git a/Tests/DependencyInjection/LiipImagineExtensionTest.php b/Tests/DependencyInjection/LiipImagineExtensionTest.php index 9d7034d1c..f12b8cc2c 100644 --- a/Tests/DependencyInjection/LiipImagineExtensionTest.php +++ b/Tests/DependencyInjection/LiipImagineExtensionTest.php @@ -57,7 +57,7 @@ public function testLoadWithDefaults() new Reference('liip_imagine.cache.manager'), new Reference('liip_imagine.cache.signer'), new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), - '%liip_imagine.controller.redirect_response_code%' + '%liip_imagine.controller.redirect_response_code%', ) ); } diff --git a/Tests/Functional/Binary/Loader/ChainLoaderTest.php b/Tests/Functional/Binary/Loader/ChainLoaderTest.php index f95611823..2e2813a6f 100644 --- a/Tests/Functional/Binary/Loader/ChainLoaderTest.php +++ b/Tests/Functional/Binary/Loader/ChainLoaderTest.php @@ -33,7 +33,7 @@ public function testFind() { static::createClient(); - $loader = $this->getLoader('baz'); + $loader = $this->getLoader('default'); foreach (array('images/cats.jpeg', 'images/cats2.jpeg', 'file.ext', 'bar-bundle-file.ext', 'foo-bundle-file.ext') as $file) { $this->assertNotNull($loader->find($file)); diff --git a/Tests/Functional/Command/AbstractCacheCommandTestCase.php b/Tests/Functional/Command/AbstractCacheCommandTestCase.php new file mode 100644 index 000000000..5fc824958 --- /dev/null +++ b/Tests/Functional/Command/AbstractCacheCommandTestCase.php @@ -0,0 +1,263 @@ +setApplication(new Application($this->createClient()->getKernel())); + if ($command instanceof ContainerAwareCommand) { + $command->setContainer($this->createClient()->getContainer()); + } + + $arguments = array_replace(array('command' => $command->getName()), $arguments); + + $commandTester = new CommandTester($command); + $return = $commandTester->execute($arguments, array('--env' => 'test')); + + return $commandTester->getDisplay(); + } + + /** + * @param string[] $images + * @param string[] $filters + */ + protected function assertImagesNotExist($images, $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertFileNotExists(sprintf('%s/%s/%s', $this->cacheRoot, $f, $i)); + } + } + } + + /** + * @param string[] $images + * @param string[] $filters + */ + protected function assertImagesExist($images, $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertFileExists(sprintf('%s/%s/%s', $this->cacheRoot, $f, $i)); + } + } + } + + /** + * @param string $output + * @param array $images + * @param array $filters + */ + protected function assertOutputContainsResolvedImages($output, array $images, array $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertOutputContainsImage($output, $i, $f, 'resolved'); + } + } + } + + /** + * @param string $output + * @param array $images + * @param array $filters + */ + protected function assertOutputContainsRemovedImages($output, array $images, array $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertOutputContainsImageShort($output, $i, $f, 'removed'); + } + } + } + + /** + * @param string $output + * @param array $filters + */ + protected function assertOutputContainsRemovedGlob($output, array $filters) + { + foreach ($filters as $f) { + $this->assertOutputContainsImageShort($output, '*', $f, 'glob-removal'); + } + } + + /** + * @param string $output + * @param array $images + * @param array $filters + */ + protected function assertOutputContainsSkippedImages($output, array $images, array $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertOutputContainsImage($output, $i, $f, 'skipped'); + } + } + } + + /** + * @param string $output + * @param array $images + * @param array $filters + */ + protected function assertOutputContainsSkippedImagesShort($output, array $images, array $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertOutputContainsImageShort($output, $i, $f, 'skipped'); + } + } + } + + /** + * @param string $output + * @param array $images + * @param array $filters + */ + protected function assertOutputContainsFailedImages($output, array $images, array $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + $this->assertContains(sprintf('%s[%s] failure: ', $i, $f), $output); + } + } + } + + /** + * @param string $output + * @param string $image + * @param string $filter + * @param string $type + */ + protected function assertOutputContainsImage($output, $image, $filter, $type) + { + $expected = vsprintf('%s[%s] %s: http://localhost/media/cache/%s/%s', array( + $image, + $filter, + $type, + $filter, + $image, + )); + $this->assertContains($expected, $output); + } + + /** + * @param string $output + * @param string $image + * @param string $filter + * @param string $type + */ + protected function assertOutputContainsImageShort($output, $image, $filter, $type) + { + $expected = vsprintf('%s[%s] %s', array( + $image, + $filter, + $type, + )); + $this->assertContains($expected, $output); + } + + /** + * @param string $output + * @param string[] $images + * @param string[] $filters + * @param int $failures + */ + protected function assertOutputContainsSummary($output, array $images, array $filters, $failures = 0) + { + $this->assertContains(sprintf('Completed %d operation', (count($images) * count($filters)) - $failures), $output); + $this->assertContains(sprintf('%d image', count($images)), $output); + $this->assertContains(sprintf('%d filter', count($filters)), $output); + if (0 !== $failures) { + $this->assertContains(sprintf('%d failure', $failures), $output); + } + } + + /** + * @param string $output + * @param string[] $filters + * @param int $failures + */ + protected function assertOutputContainsSummaryGlob($output, array $filters, $failures = 0) + { + $this->assertContains(sprintf('Completed %d operation', count($filters) - $failures), $output); + $this->assertContains('? images', $output); + $this->assertContains(sprintf('%d filter', count($filters)), $output); + + if (0 !== $failures) { + $this->assertContains(sprintf('%d failure', $failures), $output); + } + } + + /** + * @param string $output + * @param string[] $images + * @param string[] $filters + * @param int $failures + */ + protected function assertOutputNotContainsSummary($output, array $images, array $filters, $failures = 0) + { + $this->assertNotContains(sprintf('Completed %d operation', (count($images) * count($filters)) - $failures), $output); + $this->assertNotContains(sprintf('%d image', count($images)), $output); + $this->assertNotContains(sprintf('%d filter', count($filters)), $output); + if (0 !== $failures) { + $this->assertNotContains(sprintf('%d failure', $failures), $output); + } + } + + /** + * @param string[] $images + * @param string[] $filters + */ + protected function delResolvedImages(array $images, array $filters) + { + foreach ($images as $i) { + foreach ($filters as $f) { + if (file_exists($f = sprintf('%s/%s/%s', $this->cacheRoot, $f, $i))) { + @unlink($f); + } + } + } + } + + /** + * @param string[] $images + * @param string[] $filters + * @param string $content + */ + protected function putResolvedImages(array $images, array $filters, $content = 'anImageContent') + { + $this->delResolvedImages($images, $filters); + + foreach ($images as $i) { + foreach ($filters as $f) { + $this->filesystem->dumpFile(sprintf('%s/%s/%s', $this->cacheRoot, $f, $i), $content); + } + } + } +} diff --git a/Tests/Functional/Command/AbstractCommandTestCase.php b/Tests/Functional/Command/AbstractCommandTestCase.php deleted file mode 100644 index 35e56d8a6..000000000 --- a/Tests/Functional/Command/AbstractCommandTestCase.php +++ /dev/null @@ -1,43 +0,0 @@ -setApplication(new Application($this->createClient()->getKernel())); - if ($command instanceof ContainerAwareCommand) { - $command->setContainer($this->createClient()->getContainer()); - } - - $arguments = array_replace(array('command' => $command->getName()), $arguments); - - $commandTester = new CommandTester($command); - $return = $commandTester->execute($arguments, array('--env' => 'test')); - - return $commandTester->getDisplay(); - } -} diff --git a/Tests/Functional/Command/RemoveCacheCommandTest.php b/Tests/Functional/Command/RemoveCacheCommandTest.php new file mode 100644 index 000000000..73945b35f --- /dev/null +++ b/Tests/Functional/Command/RemoveCacheCommandTest.php @@ -0,0 +1,340 @@ +putResolvedImages($images, $filters); + $this->assertImagesExist($images, $filters); + + $output = $this->executeRemoveCacheCommand(array(), array()); + + $this->assertImagesNotExist($images, $allFilters); + $this->assertOutputContainsRemovedGlob($output, $allFilters); + $this->assertOutputContainsSummaryGlob($output, $allFilters); + + $this->delResolvedImages($images, $allFilters); + } + + /** + * @return array + */ + public static function provideRemovesWhenPassedPathsAndFiltersData() + { + return CacheCommandFixtures::getAvailableFilterAndImageCombinations(); + } + + /** + * @dataProvider provideRemovesWhenPassedPathsAndFiltersData + * + * @param array $images + * @param array $filters + */ + public function testRemovesWhenPassedPathsAndFilters(array $images, array $filters) + { + $this->putResolvedImages($images, $filters); + $this->assertImagesExist($images, $filters); + + $output = $this->executeRemoveCacheCommand($images, $filters); + + $this->assertImagesNotExist($images, $filters); + $this->assertOutputContainsRemovedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); + } + + /** + * @return array + */ + public static function provideRemovesWhenPassedOnlyImagesData() + { + return static::provideRemovesCachesWithoutPathsOrFiltersData(); + } + + /** + * @dataProvider provideRemovesWhenPassedOnlyImagesData + * + * @param array $images + * @param array $filters + * @param array $allFilters + */ + public function testRemovesWhenPassedOnlyImages(array $images, array $filters, array $allFilters) + { + $this->putResolvedImages($images, $filters); + $this->assertImagesExist($images, $filters); + + $output = $this->executeRemoveCacheCommand($images, array()); + + $this->assertImagesNotExist($images, $filters); + $this->assertOutputContainsRemovedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $allFilters); + + if (count($allFilters) !== count($filters)) { + $this->assertOutputContainsSkippedImagesShort($output, $images, array_diff($allFilters, $filters)); + } + + $this->delResolvedImages($images, $allFilters); + } + + /** + * @return array + */ + public static function provideRemovesWhenPassedOnlyFiltersData() + { + return array_map(function (array $entry) { + $entry[] = CacheCommandFixtures::getValidImages(); + + return $entry; + }, CacheCommandFixtures::getAvailableFilterAndImageCombinations()); + } + + /** + * @dataProvider provideRemovesWhenPassedOnlyFiltersData + * + * @param array $images + * @param array $filters + * @param array $allImages + */ + public function testRemovesWhenPassedOnlyFilters(array $images, array $filters, array $allImages) + { + $this->putResolvedImages($allImages, $filters); + $this->assertImagesExist($allImages, $filters); + + $output = $this->executeRemoveCacheCommand(array(), $filters); + + $this->assertImagesNotExist($allImages, $filters); + $this->assertOutputContainsRemovedGlob($output, $filters); + $this->assertOutputContainsSummaryGlob($output, $filters); + + $this->delResolvedImages($allImages, $filters); + } + + /** + * @return array + */ + public static function provideRemoveSkipsWhenCacheDoesNotExistData() + { + $data = array_map(function (array $entry) { + $entry[] = CacheCommandFixtures::getImagesNotInArray($entry[0]); + + return $entry; + }, CacheCommandFixtures::getAvailableFilterAndImageCombinations()); + + return array_filter($data, function (array $entry) { + return null !== $entry[2]; + }); + } + + /** + * @dataProvider provideRemoveSkipsWhenCacheDoesNotExistData + * + * @param array $images + * @param array $filters + * @param array $existingImages + */ + public function testRemoveSkipsWhenCacheDoesNotExist(array $images, array $filters, array $existingImages) + { + $this->putResolvedImages($existingImages, $filters); + $this->assertImagesExist($existingImages, $filters); + $this->assertImagesNotExist($images, $filters); + + $output = $this->executeRemoveCacheCommand($images, $filters); + + $this->assertImagesExist($existingImages, $filters); + $this->assertImagesNotExist($images, $filters); + $this->assertOutputContainsSkippedImagesShort($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); + + $this->delResolvedImages(array_merge($images, $existingImages), $filters); + } + + /** + * @return array + */ + public static function provideRemoveShowsFailureAndContinuesWhenPassedInvalidFiltersData() + { + return array_map(function (array $entry) { + $entry[] = CacheCommandFixtures::getInvalidFilters(); + + return $entry; + }, CacheCommandFixtures::getAvailableFilterAndImageCombinations()); + } + + /** + * @dataProvider provideRemoveShowsFailureAndContinuesWhenPassedInvalidFiltersData + * + * @param array $images + * @param array $filters + * @param array $invalidFilters + */ + public function testRemoveShowsFailureAndContinuesWhenPassedInvalidFilters(array $images, array $filters, array $invalidFilters) + { + $allFilters = array_merge($filters, $invalidFilters); + + $this->putResolvedImages($images, $allFilters); + $this->assertImagesExist($images, $allFilters); + + $return = null; + $output = $this->executeRemoveCacheCommand($images, $allFilters, array(), $return); + + $this->assertSame(255, $return); + $this->assertImagesNotExist($images, $filters); + $this->assertImagesExist($images, $invalidFilters); + $this->assertOutputContainsRemovedImages($output, $images, $filters); + $this->assertOutputContainsFailedImages($output, $images, $invalidFilters); + $this->assertOutputContainsSummary($output, $images, $allFilters, count($images) * count($invalidFilters)); + + $this->delResolvedImages($images, $allFilters); + } + + /** + * @return array + */ + public static function provideRemoveShowsFailureAndContinuesWhenPassedInvalidPathsData() + { + return array_map(function (array $entry) { + $entry[] = CacheCommandFixtures::getInvalidImages(); + + return $entry; + }, CacheCommandFixtures::getAvailableFilterAndImageCombinations()); + } + + /** + * @dataProvider provideRemoveShowsFailureAndContinuesWhenPassedInvalidPathsData + * + * @param array $images + * @param array $filters + * @param array $invalidImages + */ + public function testRemoveShowsFailureAndContinuesWhenPassedInvalidPaths(array $images, array $filters, array $invalidImages) + { + $allImages = array_merge($images, $invalidImages); + + $this->putResolvedImages($images, $filters); + $this->assertImagesExist($images, $filters); + $this->assertImagesNotExist($invalidImages, $filters); + + $output = $this->executeRemoveCacheCommand($allImages, $filters); + + $this->assertImagesNotExist($allImages, $filters); + $this->assertOutputContainsRemovedImages($output, $images, $filters); + $this->assertOutputContainsSkippedImagesShort($output, $invalidImages, $filters); + $this->assertOutputContainsSummary($output, $allImages, $filters); + + $this->delResolvedImages($allImages, $filters); + } + + /** + * @return array + */ + public static function provideRemoveOutputsMachineParseableTextWhenPassedMachineReadableOptionData() + { + return CacheCommandFixtures::getAvailableFilterAndImageCombinations(); + } + + /** + * @dataProvider provideRemoveOutputsMachineParseableTextWhenPassedMachineReadableOptionData + * + * @param array $images + * @param array $filters + */ + public function testRemoveOutputsMachineParseableTextWhenPassedMachineReadableOption(array $images, array $filters) + { + $this->putResolvedImages($images, $filters); + $this->assertImagesExist($images, $filters); + + $output = $this->executeRemoveCacheCommand($images, $filters, array('--machine-readable' => true)); + + $this->assertImagesNotExist($images, $filters); + $this->assertNotContains('[liip/imagine-bundle]', $output); + $this->assertNotContains('=====================', $output); + $this->assertOutputContainsRemovedImages($output, $images, $filters); + $this->assertOutputNotContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); + } + + /** + * @return array + */ + public static function provideRemoveEmitsDeprecationMessageWhenUsingLegacyFiltersOptionData() + { + return CacheCommandFixtures::getAvailableFilterAndImageCombinations(); + } + + /** + * @dataProvider provideRemoveEmitsDeprecationMessageWhenUsingLegacyFiltersOptionData + * + * @group legacy + * @expectedDeprecation The --filters option was deprecated in 1.9.0 and removed in 2.0.0. Use the --filter option instead. + */ + public function testRemoveEmitsDeprecationMessageWhenUsingLegacyFiltersOption(array $images, array $filters) + { + $this->putResolvedImages($images, $filters); + $this->assertImagesExist($images, $filters); + + $output = $this->executeRemoveCacheCommand($images, array(), array('paths' => $images, '--filters' => $filters)); + + $this->assertImagesNotExist($images, $filters); + $this->assertOutputContainsRemovedImages($output, $images, $filters); + + $this->delResolvedImages($images, $filters); + } + + /** + * @param string[] $paths + * @param string[] $filters + * @param string[] $additionalOptions + * @param int $return + * + * @return string + */ + private function executeRemoveCacheCommand(array $paths, array $filters = array(), array $additionalOptions = array(), &$return = null) + { + $options = array_merge(array('paths' => $paths), $additionalOptions); + + if (0 < count($filters)) { + $options['--filter'] = $filters; + } + + return $this->executeConsole(new RemoveCacheCommand(), $options, $return); + } +} diff --git a/Tests/Functional/Command/RemoveCacheTest.php b/Tests/Functional/Command/RemoveCacheTest.php deleted file mode 100644 index bd98777f2..000000000 --- a/Tests/Functional/Command/RemoveCacheTest.php +++ /dev/null @@ -1,269 +0,0 @@ -assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - - $this->executeConsole(new RemoveCacheCommand()); - } - - public function testExecuteSuccessfullyWithEmptyCacheAndOnePathAndOneFilter() - { - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - - $this->executeConsole( - new RemoveCacheCommand(), - array( - 'paths' => array('images/cats.jpeg'), - '--filter' => array('thumbnail_web_path'), - )); - } - - public function testExecuteSuccessfullyWithEmptyCacheAndMultiplePaths() - { - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - - $this->executeConsole( - new RemoveCacheCommand(), - array('paths' => array('images/cats.jpeg', 'images/cats2.jpeg')) - ); - } - - public function testExecuteSuccessfullyWithEmptyCacheAndMultipleFilters() - { - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - - $this->executeConsole( - new RemoveCacheCommand(), - array('--filter' => array('thumbnail_web_path', 'thumbnail_default')) - ); - } - - public function testShouldRemoveAllCacheIfParametersDoesNotPassed() - { - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg', - 'anImageContent2' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats.jpeg', - 'anImageContent' - ); - - $this->executeConsole(new RemoveCacheCommand()); - - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); - } - - public function testShouldRemoveCacheBySinglePath() - { - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg', - 'anImageContent2' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats2.jpeg', - 'anImageContent2' - ); - - $this->executeConsole( - new RemoveCacheCommand(), - array('paths' => array('images/cats.jpeg')) - ); - - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg'); - $this->assertFileExists($this->cacheRoot.'/thumbnail_default/images/cats2.jpeg'); - } - - public function testShouldRemoveCacheByMultiplePaths() - { - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg', - 'anImageContent2' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats2.jpeg', - 'anImageContent2' - ); - - $this->executeConsole( - new RemoveCacheCommand(), - array('paths' => array('images/cats.jpeg', 'images/cats2.jpeg')) - ); - - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats2.jpeg'); - } - - public function testShouldRemoveCacheBySingleFilter() - { - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg', - 'anImageContent2' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats2.jpeg', - 'anImageContent2' - ); - - $this->executeConsole( - new RemoveCacheCommand(), - array('--filter' => array('thumbnail_default')) - ); - - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats2.jpeg'); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg'); - } - - public function testShouldRemoveCacheByMultipleFilters() - { - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg', - 'anImageContent2' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats2.jpeg', - 'anImageContent2' - ); - - $this->executeConsole( - new RemoveCacheCommand(), - array('--filter' => array('thumbnail_default', 'thumbnail_web_path')) - ); - - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats2.jpeg'); - } - - public function testShouldRemoveCacheByOnePathAndMultipleFilters() - { - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg', - 'anImageContent2' - ); - - $this->executeConsole( - new RemoveCacheCommand(), - array( - 'paths' => array('images/cats.jpeg'), - '--filter' => array('thumbnail_default', 'thumbnail_web_path'), ) - ); - - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg'); - } - - public function testShouldRemoveCacheByMultiplePathsAndSingleFilter() - { - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_default/images/cats.jpeg', - 'anImageContent' - ); - $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg', - 'anImageContent2' - ); - - $this->executeConsole( - new RemoveCacheCommand(), - array( - 'paths' => array('images/cats.jpeg', 'images/cats2.jpeg'), - '--filter' => array('thumbnail_web_path'), ) - ); - - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats2.jpeg'); - $this->assertFileExists($this->cacheRoot.'/thumbnail_default/images/cats.jpeg'); - } - - /** - * @group legacy - * @expectedDeprecation As of 1.9, use of the "--filters" option has been deprecated in favor of "--filter" and will be removed in 2.0. - */ - public function testDeprecatedFiltersOption() - { - $this->executeConsole( - new RemoveCacheCommand(), - array( - 'paths' => array('images/cats.jpeg', 'images/cats2.jpeg'), - '--filters' => array('thumbnail_web_path'), ) - ); - } -} diff --git a/Tests/Functional/Command/ResolveCacheCommandTest.php b/Tests/Functional/Command/ResolveCacheCommandTest.php new file mode 100644 index 000000000..a9b87dfd5 --- /dev/null +++ b/Tests/Functional/Command/ResolveCacheCommandTest.php @@ -0,0 +1,306 @@ +putResolvedImages($images, $filters); + + $output = $this->executeResolveCacheCommand($images, $filters); + + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsSkippedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); + } + + /** + * @return array + */ + public static function provideResolvesWhenPassedOnlyImagesData() + { + return CacheCommandFixtures::getAvailableFilterAndImageCombinations(); + } + + /** + * @param array $images + * @param array $filters + * + * @dataProvider provideResolvedWhenPassedPathsAndFiltersData + */ + public function testResolvesWhenPassedOnlyImages(array $images, array $filters) + { + $this->putResolvedImages($images, $filters); + + $output = $this->executeResolveCacheCommand($images, $filters); + + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsSkippedImages($output, $images, $filters); + $this->assertOutputContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); + } + + /** + * @return array + */ + public static function provideResolvesWhenPassedPathsAndFiltersWithPartialCachesData() + { + $data = array_map(function (array $entry) { + $entry[] = CacheCommandFixtures::getImagesNotInArray($entry[0]); + $entry[] = CacheCommandFixtures::getFiltersNotInArray($entry[1]); + + return $entry; + }, CacheCommandFixtures::getAvailableFilterAndImageCombinations()); + + return array_filter($data, function (array $entry) { + return null !== $entry[2] && null !== $entry[3]; + }); + } + + /** + * @param array $images + * @param array $filters + * @param array $cachedImages + * @param array $cachedFilters + * + * @dataProvider provideResolvesWhenPassedPathsAndFiltersWithPartialCachesData + */ + public function testResolvesWhenPassedPathsAndFiltersWithPartialCaches(array $images, array $filters, array $cachedImages, array $cachedFilters) + { + $allImages = array_merge($images, $cachedImages); + $allFilters = array_merge($filters, $cachedFilters); + + $this->putResolvedImages($cachedImages, $cachedFilters); + + $this->assertImagesNotExist($images, $filters); + $this->assertImagesExist($cachedImages, $cachedFilters); + + $output = $this->executeResolveCacheCommand($allImages, $allFilters); + + $this->assertImagesExist($allImages, $allFilters); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + $this->assertOutputContainsSkippedImages($output, $cachedImages, $cachedFilters); + $this->assertOutputContainsSummary($output, $allImages, $allFilters); + + $this->delResolvedImages($allImages, $allFilters); + } + + /** + * @return array + */ + public static function provideResolvesForcedAllWhenPassedPathsAndFiltersWithPartialCachesData() + { + return static::provideResolvesWhenPassedPathsAndFiltersWithPartialCachesData(); + } + + /** + * @param array $images + * @param array $filters + * @param array $cachedImages + * @param array $cachedFilters + * + * @dataProvider provideResolvesForcedAllWhenPassedPathsAndFiltersWithPartialCachesData + */ + public function testForcedResolveWhenPassedPathsAndFiltersWithPartialCaches(array $images, array $filters, array $cachedImages, array $cachedFilters) + { + $allImages = array_merge($images, $cachedImages); + $allFilters = array_merge($filters, $cachedFilters); + + $this->putResolvedImages($cachedImages, $cachedFilters); + + $this->assertImagesNotExist($images, $filters); + $this->assertImagesExist($cachedImages, $cachedFilters); + + $output = $this->executeResolveCacheCommand($allImages, $allFilters, array('--force' => true)); + + $this->assertImagesExist($allImages, $allFilters); + $this->assertOutputContainsResolvedImages($output, $allImages, $allFilters); + $this->assertOutputContainsSummary($output, $allImages, $allFilters); + + $this->delResolvedImages($allImages, $allFilters); + } + + /** + * @return array + */ + public static function provideResolveShowsFailureAndContinuesWhenPassedInvalidFiltersData() + { + return array_map(function (array $entry) { + $entry[] = CacheCommandFixtures::getInvalidFilters(); + + return $entry; + }, CacheCommandFixtures::getAvailableFilterAndImageCombinations()); + } + + /** + * @param array $images + * @param array $filters + * @param array $invalidFilters + * + * @dataProvider provideResolveShowsFailureAndContinuesWhenPassedInvalidFiltersData + */ + public function testResolveShowsFailureAndContinuesWhenPassedInvalidFilters(array $images, array $filters, array $invalidFilters) + { + $allFilters = array_merge($filters, $invalidFilters); + + $this->assertImagesNotExist($images, $allFilters); + + $return = null; + $output = $this->executeResolveCacheCommand($images, $allFilters, array(), $return); + + $this->assertSame(255, $return); + $this->assertImagesExist($images, $filters); + $this->assertImagesNotExist($images, $invalidFilters); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + $this->assertOutputContainsFailedImages($output, $images, $invalidFilters); + $this->assertOutputContainsSummary($output, $images, $allFilters, count($images) * count($invalidFilters)); + + $this->delResolvedImages($images, $allFilters); + } + + /** + * @return array + */ + public static function provideResolveShowsFailureAndContinuesWhenPassedInvalidPathsData() + { + return array_map(function (array $entry) { + $entry[] = CacheCommandFixtures::getInvalidImages(); + + return $entry; + }, CacheCommandFixtures::getAvailableFilterAndImageCombinations()); + } + + /** + * @param array $images + * @param array $filters + * @param array $invalidImages + * + * @dataProvider provideResolveShowsFailureAndContinuesWhenPassedInvalidPathsData + */ + public function testResolveShowsFailureAndContinuesWhenPassedInvalidPaths(array $images, array $filters, array $invalidImages) + { + $allImages = array_merge($images, $invalidImages); + + $this->assertImagesNotExist($allImages, $filters); + + $return = null; + $output = $this->executeResolveCacheCommand($allImages, $filters, array(), $return); + + $this->assertSame(255, $return); + $this->assertImagesExist($images, $filters); + $this->assertImagesNotExist($invalidImages, $filters); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + $this->assertOutputContainsFailedImages($output, $invalidImages, $filters); + $this->assertOutputContainsSummary($output, $allImages, $filters, count($filters) * count($invalidImages)); + + $this->delResolvedImages($allImages, $filters); + } + + /** + * @return array + */ + public static function provideResolveOutputsMachineParseableTextWhenPassedMachineReadableOptionData() + { + return CacheCommandFixtures::getAvailableFilterAndImageCombinations(); + } + + /** + * @param array $images + * @param array $filters + * + * @dataProvider provideResolveOutputsMachineParseableTextWhenPassedMachineReadableOptionData + */ + public function testResolveOutputsMachineParseableTextWhenPassedMachineReadableOption(array $images, array $filters) + { + $this->assertImagesNotExist($images, $filters); + + $output = $this->executeResolveCacheCommand($images, $filters, array('--machine-readable' => true)); + + $this->assertImagesExist($images, $filters); + $this->assertNotContains('[liip/imagine-bundle]', $output); + $this->assertNotContains('=====================', $output); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + $this->assertOutputNotContainsSummary($output, $images, $filters); + + $this->delResolvedImages($images, $filters); + } + + /** + * @return array + */ + public static function provideResolveEmitsDeprecationMessageWhenUsingLegacyFiltersOptionData() + { + return CacheCommandFixtures::getAvailableFilterAndImageCombinations(); + } + + /** + * @dataProvider provideResolveEmitsDeprecationMessageWhenUsingLegacyFiltersOptionData + * + * @group legacy + * @expectedDeprecation The --filters option was deprecated in 1.9.0 and removed in 2.0.0. Use the --filter option instead. + */ + public function testResolveEmitsDeprecationMessageWhenUsingLegacyFiltersOption(array $images, array $filters) + { + $this->assertImagesNotExist($images, $filters); + + $output = $this->executeResolveCacheCommand($images, array(), array('paths' => $images, '--filters' => $filters)); + + $this->assertImagesExist($images, $filters); + $this->assertOutputContainsResolvedImages($output, $images, $filters); + + $this->delResolvedImages($images, $filters); + } + + /** + * @param string[] $paths + * @param string[] $filters + * @param string[] $additionalOptions + * @param int $return + * + * @return string + */ + protected function executeResolveCacheCommand(array $paths, array $filters = array(), array $additionalOptions = array(), &$return = null) + { + $options = array_merge(array('paths' => $paths), $additionalOptions); + + if (0 < count($filters)) { + $options['--filter'] = $filters; + } + + return $this->executeConsole(new ResolveCacheCommand(), $options, $return); + } +} diff --git a/Tests/Functional/Command/ResolveCacheTest.php b/Tests/Functional/Command/ResolveCacheTest.php deleted file mode 100644 index 822944812..000000000 --- a/Tests/Functional/Command/ResolveCacheTest.php +++ /dev/null @@ -1,369 +0,0 @@ -assertImagesNotExist($images, $filters); - - $return = null; - $output = $this->executeResolveCacheCommand($images, $filters, array(), $return); - - $this->assertSame(0, $return); - $this->assertImagesExist($images, $filters); - $this->assertImagesNotExist($images, array('thumbnail_default')); - $this->assertOutputContainsResolvedImages($output, $images, $filters); - $this->assertOutputContainsSummary($output, $images, $filters); - - $this->delResolvedImages($images, $filters); - } - - public function testShouldResolveWithCacheExists() - { - $images = array('images/cats.jpeg'); - $filters = array('thumbnail_web_path'); - - $this->putResolvedImages($images, $filters); - - $output = $this->executeResolveCacheCommand($images, $filters); - - $this->assertImagesExist($images, $filters); - $this->assertImagesNotExist($images, array('thumbnail_default')); - $this->assertOutputContainsCachedImages($output, $images, $filters); - $this->assertOutputContainsSummary($output, $images, $filters); - - $this->delResolvedImages($images, $filters); - } - - public function testShouldResolveWithFewPathsAndSingleFilter() - { - $images = array('images/cats.jpeg', 'images/cats2.jpeg'); - $filters = array('thumbnail_web_path'); - - $output = $this->executeResolveCacheCommand($images, $filters); - - $this->assertImagesExist($images, $filters); - $this->assertOutputContainsResolvedImages($output, $images, $filters); - - $output = $this->executeResolveCacheCommand($images, $filters); - - $this->assertImagesExist($images, $filters); - $this->assertOutputContainsCachedImages($output, $images, $filters); - $this->assertOutputContainsSummary($output, $images, $filters); - - $this->delResolvedImages($images, $filters); - } - - public function testShouldResolveWithFewPathsSingleFilterAndPartiallyFullCache() - { - $imagesResolved = array('images/cats.jpeg'); - $imagesCached = array('images/cats2.jpeg'); - $images = array_merge($imagesResolved, $imagesCached); - $filters = array('thumbnail_web_path'); - - $this->putResolvedImages($imagesCached, $filters); - - $this->assertImagesNotExist($imagesResolved, $filters); - $this->assertImagesExist($imagesCached, $filters); - - $output = $this->executeResolveCacheCommand($images, $filters); - - $this->assertImagesExist($images, $filters); - $this->assertOutputContainsResolvedImages($output, $imagesResolved, $filters); - $this->assertOutputContainsCachedImages($output, $imagesCached, $filters); - $this->assertOutputContainsSummary($output, $images, $filters); - - $this->delResolvedImages($images, $filters); - } - - public function testShouldResolveWithFewPathsAndFewFilters() - { - $images = array('images/cats.jpeg', 'images/cats2.jpeg'); - $filters = array('thumbnail_web_path', 'thumbnail_default'); - - $output = $this->executeResolveCacheCommand($images, $filters); - - $this->assertImagesExist($images, $filters); - $this->assertOutputContainsResolvedImages($output, $images, $filters); - $this->assertOutputContainsSummary($output, $images, $filters); - - $this->delResolvedImages($images, $filters); - } - - public function testShouldResolveWithFewPathsAndWithoutFilters() - { - $images = array('images/cats.jpeg', 'images/cats2.jpeg'); - $filters = array('thumbnail_web_path', 'thumbnail_default'); - - $output = $this->executeResolveCacheCommand($images); - - $this->assertImagesExist($images, $filters); - $this->assertOutputContainsResolvedImages($output, $images, $filters); - $this->assertOutputContainsSummary($output, $images, $filters); - - $this->delResolvedImages($images, $filters); - } - - public function testCachedAndForceResolve() - { - $images = array('images/cats.jpeg', 'images/cats2.jpeg'); - $filters = array('thumbnail_web_path', 'thumbnail_default'); - - $this->assertImagesNotExist($images, $filters); - $this->putResolvedImages($images, $filters); - $this->assertImagesExist($images, $filters); - - $output = $this->executeResolveCacheCommand($images, $filters); - - $this->assertImagesExist($images, $filters); - $this->assertOutputContainsCachedImages($output, $images, $filters); - - $output = $this->executeResolveCacheCommand($images, $filters, array('--force' => true)); - - $this->assertImagesExist($images, $filters); - $this->assertOutputContainsResolvedImages($output, $images, $filters); - $this->assertOutputContainsSummary($output, $images, $filters); - - $this->delResolvedImages($images, $filters); - } - - public function testFailedResolve() - { - $images = array('images/cats.jpeg', 'images/cats2.jpeg'); - $filters = array('does_not_exist'); - - $this->assertImagesNotExist($images, $filters); - - $return = null; - $output = $this->executeResolveCacheCommand($images, $filters, array(), $return); - - $this->assertSame(255, $return); - $this->assertImagesNotExist($images, $filters); - $this->assertOutputContainsFailedImages($output, $images, $filters); - $this->assertOutputContainsSummary($output, $images, $filters, 2); - - $this->delResolvedImages($images, $filters); - } - - public function testMachineOption() - { - $images = array('images/cats.jpeg', 'images/cats2.jpeg'); - $filters = array('does_not_exist'); - - $this->assertImagesNotExist($images, $filters); - - $output = $this->executeResolveCacheCommand($images, $filters, array('--as-script' => true)); - - $this->assertImagesNotExist($images, $filters); - $this->assertNotContains('liip/imagine-bundle (cache resolver)', $output); - $this->assertNotContains('====================================', $output); - $this->assertOutputContainsFailedImages($output, $images, $filters); - $this->assertOutputNotContainsSummary($output, $images, $filters, 2); - - $this->delResolvedImages($images, $filters); - } - - /** - * @group legacy - * @expectedDeprecation As of 1.9, use of the "--filters" option has been deprecated in favor of "--filter" and will be removed in 2.0. - */ - public function testDeprecatedFiltersOption() - { - $images = array('images/cats.jpeg'); - $filters = array('thumbnail_web_path'); - - $this->assertImagesNotExist($images, $filters); - - $output = $this->executeConsole(new ResolveCacheCommand(), array('paths' => $images, '--filters' => $filters)); - - $this->assertImagesExist($images, $filters); - $this->assertOutputContainsResolvedImages($output, $images, $filters); - - $this->delResolvedImages($images, $filters); - } - - /** - * @param string[] $paths - * @param string[] $filters - * @param string[] $additionalOptions - * @param int $return - * - * @return string - */ - private function executeResolveCacheCommand(array $paths, array $filters = array(), array $additionalOptions = array(), &$return = null) - { - $options = array_merge(array('paths' => $paths), $additionalOptions); - - if (0 < count($filters)) { - $options['--filter'] = $filters; - } - - return $this->executeConsole(new ResolveCacheCommand(), $options, $return); - } - - /** - * @param string[] $images - * @param string[] $filters - */ - private function assertImagesNotExist($images, $filters) - { - foreach ($images as $i) { - foreach ($filters as $f) { - $this->assertFileNotExists(sprintf('%s/%s/%s', $this->cacheRoot, $f, $i)); - } - } - } - - /** - * @param string[] $images - * @param string[] $filters - */ - private function assertImagesExist($images, $filters) - { - foreach ($images as $i) { - foreach ($filters as $f) { - $this->assertFileExists(sprintf('%s/%s/%s', $this->cacheRoot, $f, $i)); - } - } - } - - /** - * @param string $output - * @param array $images - * @param array $filters - */ - private function assertOutputContainsResolvedImages($output, array $images, array $filters) - { - foreach ($images as $i) { - foreach ($filters as $f) { - $this->assertOutputContainsImage($output, $i, $f, 'resolved'); - } - } - } - - /** - * @param string $output - * @param array $images - * @param array $filters - */ - private function assertOutputContainsCachedImages($output, array $images, array $filters) - { - foreach ($images as $i) { - foreach ($filters as $f) { - $this->assertOutputContainsImage($output, $i, $f, 'cached'); - } - } - } - - /** - * @param string $output - * @param array $images - * @param array $filters - */ - private function assertOutputContainsFailedImages($output, array $images, array $filters) - { - foreach ($images as $i) { - foreach ($filters as $f) { - $this->assertContains(sprintf('%s[%s] failed: ', $i, $f), $output); - } - } - } - - /** - * @param string $output - * @param string $image - * @param string $filter - * @param string $type - */ - private function assertOutputContainsImage($output, $image, $filter, $type) - { - $expected = vsprintf('%s[%s] %s: http://localhost/media/cache/%s/%s', array( - $image, - $filter, - $type, - $filter, - $image, - )); - $this->assertContains($expected, $output); - } - - /** - * @param string $output - * @param string[] $images - * @param string[] $filters - * @param int $failures - */ - private function assertOutputContainsSummary($output, array $images, array $filters, $failures = 0) - { - $this->assertContains(sprintf('Completed %d operation', (count($images) * count($filters)) - $failures), $output); - $this->assertContains(sprintf('%d image', count($images)), $output); - $this->assertContains(sprintf('%d filter', count($filters)), $output); - if (0 !== $failures) { - $this->assertContains(sprintf('%d failure', $failures), $output); - } - } - - /** - * @param string $output - * @param string[] $images - * @param string[] $filters - * @param int $failures - */ - private function assertOutputNotContainsSummary($output, array $images, array $filters, $failures = 0) - { - $this->assertNotContains(sprintf('Completed %d operation', (count($images) * count($filters)) - $failures), $output); - $this->assertNotContains(sprintf('%d image', count($images)), $output); - $this->assertNotContains(sprintf('%d filter', count($filters)), $output); - if (0 !== $failures) { - $this->assertNotContains(sprintf('%d failure', $failures), $output); - } - } - - /** - * @param string[] $images - * @param string[] $filters - */ - private function delResolvedImages(array $images, array $filters) - { - foreach ($images as $i) { - foreach ($filters as $f) { - if (file_exists($f = sprintf('%s/%s/%s', $this->cacheRoot, $f, $i))) { - @unlink($f); - } - } - } - } - - /** - * @param string[] $images - * @param string[] $filters - * @param string $content - */ - private function putResolvedImages(array $images, array $filters, $content = 'anImageContent') - { - foreach ($images as $i) { - foreach ($filters as $f) { - $this->filesystem->dumpFile(sprintf('%s/%s/%s', $this->cacheRoot, $f, $i), $content); - } - } - } -} diff --git a/Tests/Functional/Controller/ImagineControllerTest.php b/Tests/Functional/Controller/ImagineControllerTest.php index 14301a439..426014bab 100644 --- a/Tests/Functional/Controller/ImagineControllerTest.php +++ b/Tests/Functional/Controller/ImagineControllerTest.php @@ -30,44 +30,44 @@ public function testCouldBeGetFromContainer() public function testShouldResolvePopulatingCacheFirst() { //guard - $this->assertFileNotExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); + $this->assertFileNotExists($this->cacheRoot.'/profile_thumb_sm/images/cats.jpeg'); - $this->client->request('GET', '/media/cache/resolve/thumbnail_web_path/images/cats.jpeg'); + $this->client->request('GET', '/media/cache/resolve/profile_thumb_sm/images/cats.jpeg'); $response = $this->client->getResponse(); $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $response); $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals('http://localhost/media/cache/thumbnail_web_path/images/cats.jpeg', $response->getTargetUrl()); + $this->assertEquals('http://localhost/media/cache/profile_thumb_sm/images/cats.jpeg', $response->getTargetUrl()); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); + $this->assertFileExists($this->cacheRoot.'/profile_thumb_sm/images/cats.jpeg'); } public function testShouldResolveFromCache() { $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg', + $this->cacheRoot.'/profile_thumb_sm/images/cats.jpeg', 'anImageContent' ); - $this->client->request('GET', '/media/cache/resolve/thumbnail_web_path/images/cats.jpeg'); + $this->client->request('GET', '/media/cache/resolve/profile_thumb_sm/images/cats.jpeg'); $response = $this->client->getResponse(); $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $response); $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals('http://localhost/media/cache/thumbnail_web_path/images/cats.jpeg', $response->getTargetUrl()); + $this->assertEquals('http://localhost/media/cache/profile_thumb_sm/images/cats.jpeg', $response->getTargetUrl()); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/cats.jpeg'); + $this->assertFileExists($this->cacheRoot.'/profile_thumb_sm/images/cats.jpeg'); } /** * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException - * @expectedExceptionMessage Signed url does not pass the sign check for path "images/cats.jpeg" and filter "thumbnail_web_path" and runtime config {"thumbnail":{"size":["50","50"]}} + * @expectedExceptionMessage Signed url does not pass the sign check for path "images/cats.jpeg" and filter "profile_thumb_sm" and runtime config {"thumbnail":{"size":["50","50"]}} */ public function testThrowBadRequestIfSignInvalidWhileUsingCustomFilters() { - $this->client->request('GET', '/media/cache/resolve/thumbnail_web_path/rc/invalidHash/images/cats.jpeg?'.http_build_query(array( + $this->client->request('GET', '/media/cache/resolve/profile_thumb_sm/rc/invalidHash/images/cats.jpeg?'.http_build_query(array( 'filters' => array( 'thumbnail' => array('size' => array(50, 50)), ), @@ -81,7 +81,7 @@ public function testThrowBadRequestIfSignInvalidWhileUsingCustomFilters() */ public function testShouldThrowNotFoundHttpExceptionIfFiltersNotArray() { - $this->client->request('GET', '/media/cache/resolve/thumbnail_web_path/rc/invalidHash/images/cats.jpeg?'.http_build_query(array( + $this->client->request('GET', '/media/cache/resolve/profile_thumb_sm/rc/invalidHash/images/cats.jpeg?'.http_build_query(array( 'filters' => 'some-string', '_hash' => 'hash', ))); @@ -93,7 +93,7 @@ public function testShouldThrowNotFoundHttpExceptionIfFiltersNotArray() */ public function testShouldThrowNotFoundHttpExceptionIfFileNotExists() { - $this->client->request('GET', '/media/cache/resolve/thumbnail_web_path/images/shrodinger_cats_which_not_exist.jpeg'); + $this->client->request('GET', '/media/cache/resolve/profile_thumb_sm/images/shrodinger_cats_which_not_exist.jpeg'); } /** @@ -119,7 +119,7 @@ public function testShouldResolveWithCustomFiltersPopulatingCacheFirst() $hash = $signer->sign($path, $params['filters']); - $expectedCachePath = 'thumbnail_web_path/rc/'.$hash.'/'.$path; + $expectedCachePath = 'profile_thumb_sm/rc/'.$hash.'/'.$path; $url = 'http://localhost/media/cache/resolve/'.$expectedCachePath.'?'.http_build_query($params); @@ -152,7 +152,7 @@ public function testShouldResolveWithCustomFiltersFromCache() $hash = $signer->sign($path, $params['filters']); - $expectedCachePath = 'thumbnail_web_path/rc/'.$hash.'/'.$path; + $expectedCachePath = 'profile_thumb_sm/rc/'.$hash.'/'.$path; $url = 'http://localhost/media/cache/resolve/'.$expectedCachePath.'?'.http_build_query($params); @@ -175,20 +175,20 @@ public function testShouldResolveWithCustomFiltersFromCache() public function testShouldResolvePathWithSpecialCharactersAndWhiteSpaces() { $this->filesystem->dumpFile( - $this->cacheRoot.'/thumbnail_web_path/images/foo bar.jpeg', + $this->cacheRoot.'/profile_thumb_sm/images/foo bar.jpeg', 'anImageContent' ); // we are calling url with encoded file name as it will be called by browser $urlEncodedFileName = 'foo+bar'; - $this->client->request('GET', '/media/cache/resolve/thumbnail_web_path/images/'.$urlEncodedFileName.'.jpeg'); + $this->client->request('GET', '/media/cache/resolve/profile_thumb_sm/images/'.$urlEncodedFileName.'.jpeg'); $response = $this->client->getResponse(); $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $response); $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals('http://localhost/media/cache/thumbnail_web_path/images/foo bar.jpeg', $response->getTargetUrl()); + $this->assertEquals('http://localhost/media/cache/profile_thumb_sm/images/foo bar.jpeg', $response->getTargetUrl()); - $this->assertFileExists($this->cacheRoot.'/thumbnail_web_path/images/foo bar.jpeg'); + $this->assertFileExists($this->cacheRoot.'/profile_thumb_sm/images/foo bar.jpeg'); } } diff --git a/Tests/Functional/Fixtures/BarBundle/Resources/public/cats-bar-bundle.jpg b/Tests/Functional/Fixtures/BarBundle/Resources/public/cats-bar-bundle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bc458e3508af75192b1b7c5d03e9f46206a18d88 GIT binary patch literal 34087 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 CN;`=F literal 0 HcmV?d00001 diff --git a/Tests/Functional/Fixtures/CacheCommandFixtures.php b/Tests/Functional/Fixtures/CacheCommandFixtures.php new file mode 100644 index 000000000..9ff8bdd7a --- /dev/null +++ b/Tests/Functional/Fixtures/CacheCommandFixtures.php @@ -0,0 +1,99 @@ +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 CN;`=F literal 0 HcmV?d00001 diff --git a/Tests/Functional/app/config/config.yml b/Tests/Functional/app/config/config.yml index 41b99282a..a1f92db59 100644 --- a/Tests/Functional/app/config/config.yml +++ b/Tests/Functional/app/config/config.yml @@ -26,6 +26,14 @@ liip_imagine: loaders: default: + chain: + loaders: + - web + - foo + - bar + - bundles_all + + web: filesystem: data_root: "%kernel.root_dir%/web" @@ -37,14 +45,6 @@ liip_imagine: filesystem: data_root: "%kernel.root_dir%/../../Fixtures/FileSystemLocator/root-02" - baz: - chain: - loaders: - - foo - - bar - - default - - bundles_all - bundles_all: filesystem: data_root: ~ @@ -76,14 +76,22 @@ liip_imagine: filter_sets: - thumbnail_web_path: + profile_thumb_sm: filters: - thumbnail: { size: [223, 223], mode: inset } + thumbnail: + size: [100, 100] + mode: inset - thumbnail_default: + profile_thumb_lg: filters: - thumbnail: { size: [223, 223], mode: inset } + thumbnail: + size: [200, 200] + mode: inset + profile_main: + filters: + scale: + dim: [300, 300] controller: redirect_response_code: 302 diff --git a/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php b/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php index 81d6833c0..82974b85a 100644 --- a/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php +++ b/Tests/Imagine/Filter/Loader/ResampleFilterLoaderTest.php @@ -60,7 +60,7 @@ public static function provideResampleData() $resolutions = array( 72.0, 120.0, - 240.0 + 240.0, ); $data = array(); diff --git a/UPGRADE.md b/UPGRADE.md index b68000daa..4cb2f0ddb 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -7,6 +7,21 @@ All important upgrade requirements will be enumerated in this This project adheres to [semantic versioning](http://semver.org/spec/v2.0.0.html). +## v1.9.1 + +*View the [changelog entry](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md#v191) for the `1.9.1` release.* + + - __\[Console\]__ __\[BC BREAK\]__ The resolve command's `--as-script`/`-s` option name/shortcut conflicted with Symfony + 2.x core console options (specifically `--shell`/`-s`) and has been renamed to `--machine-readable`/`-m` + \(fixes [\#988](https://github.com/liip/LiipImagineBundle/pull/988)\). The `-s` option shortcut was the only conflict, + but the `--as-script` option name proved confusing and unclear so it too was renamed. + + - __\[Console\]__ The output formatting for the `remove` command has been updated and aligned with the behavior + previously introduced in `1.9.0` for the `resolve` command, making both of them consistent and in-line with the + expected `2.0.0` output. The `--machine-readable`/`-m` option name/shortcut has now been added to the `remove` command + as well, enabling predictable, consistent, script parseable output stripped of text styles and supplemental formatting. + + ## v1.9.0 *View the [changelog entry](https://github.com/liip/LiipImagineBundle/blob/1.0/CHANGELOG.md#v190) for the `1.9.0` release.* From 4a69a44e5722068592cc594b10a275eb5c830d2d Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sat, 18 Nov 2017 10:14:58 +0100 Subject: [PATCH 27/27] test with php 7.2 --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f52ea50ba..df09bee9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ php: - 5.6 - 7.0 - 7.1 + - 7.2 cache: directories: @@ -67,5 +68,3 @@ script: after_script: - if [ "${TRAVIS_PHP_VERSION}" != "5.3" ]; then bin/coveralls -vvv; fi; - -...