diff --git a/config/configuration.sample.yml b/config/configuration.sample.yml index 6e233eb492..7f8dadb824 100644 --- a/config/configuration.sample.yml +++ b/config/configuration.sample.yml @@ -131,7 +131,7 @@ registration-fields: name: geonameid required: true xsendfile: - enable: false + enabled: false mapping: - directory: '' diff --git a/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php b/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php index 7343212d03..720eadbdf7 100644 --- a/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php +++ b/lib/Alchemy/Phrasea/Controller/AbstractDelivery.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\Controller; use Alchemy\Phrasea\Application; -use Alchemy\Phrasea\Response\DeliverDataInterface; +use Alchemy\Phrasea\Http\DeliverDataInterface; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; @@ -66,7 +66,6 @@ abstract class AbstractDelivery implements ControllerProviderInterface if ($file->getDataboxSubdef()->get_class() == \databox_subdef::CLASS_THUMBNAIL) { // default expiration is 5 days $expiration = 60 * 60 * 24 * 5; - $response->setExpires(new \DateTime(sprintf('+%d seconds', $expiration))); $response->setMaxAge($expiration); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php b/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php index eb206412e0..d846312209 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php @@ -11,7 +11,7 @@ namespace Alchemy\Phrasea\Controller\Prod; -use Alchemy\Phrasea\Response\DeliverDataInterface; +use Alchemy\Phrasea\Http\DeliverDataInterface; use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Lazaret.php b/lib/Alchemy/Phrasea/Controller/Prod/Lazaret.php index 60a21c1f1d..d6ed2e3e5d 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Lazaret.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Lazaret.php @@ -14,7 +14,7 @@ namespace Alchemy\Phrasea\Controller\Prod; use Entities\LazaretFile; use Alchemy\Phrasea\Border; use Alchemy\Phrasea\Border\Attribute\AttributeInterface; -use Alchemy\Phrasea\Response\DeliverDataInterface; +use Alchemy\Phrasea\Http\DeliverDataInterface; use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; @@ -522,7 +522,7 @@ class Lazaret implements ControllerProviderInterface $lazaretThumbFileName = $app['root.path'] . '/tmp/lazaret/' . $lazaretFile->getThumbFilename(); - return $app['phraseanet.file-serve']->deliverFile($lazaretThumbFileName, $lazaretFile->getOriginalName(), DeliverDataInterface::DISPOSITION_INLINE, 'image/jpeg'); + return $app['phraseanet.file-serve']->deliverFile($lazaretThumbFileName, $lazaretFile->getOriginalName(), DeliverDataInterface::DISPOSITION_INLINE, 'image/jpeg', 3600); } /** diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/XSendFileSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/XSendFileSubscriber.php index 2cbe9ea688..8b9381f82a 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/XSendFileSubscriber.php +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/XSendFileSubscriber.php @@ -28,13 +28,13 @@ class XSendFileSubscriber implements EventSubscriberInterface public static function getSubscribedEvents() { return array( - KernelEvents::REQUEST => array('applyHeaders', 16), + KernelEvents::REQUEST => array('applyHeaders', 0), ); } public function applyHeaders(GetResponseEvent $event) { - if ($this->app['phraseanet.configuration']['xsendfile']['enable']) { + if ($this->app['phraseanet.configuration']['xsendfile']['enabled']) { $request = $event->getRequest(); $request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect'); $request->headers->set('X-Accel-Mapping', (string) $this->app['phraseanet.xsendfile-mapping']); diff --git a/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php index bb6fad094b..d7d99bce44 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php @@ -13,7 +13,8 @@ namespace Alchemy\Phrasea\Core\Provider; use Silex\Application; use Silex\ServiceProviderInterface; -use Alchemy\Phrasea\Response\ServeFileResponseFactory; +use Alchemy\Phrasea\Http\ServeFileResponseFactory; +use Alchemy\Phrasea\Http\XsendfileMapping; use Alchemy\Phrasea\Core\Event\Subscriber\XSendFileSubscriber; class FileServeServiceProvider implements ServiceProviderInterface @@ -43,7 +44,7 @@ class FileServeServiceProvider implements ServiceProviderInterface }); $app['phraseanet.xsendfile-mapping'] = $app->share(function($app) { - return new Mapping($app['xsendfile.mapping']); + return new XsendfileMapping($app['xsendfile.mapping']); }); $app['phraseanet.file-serve'] = $app->share(function (Application $app) { diff --git a/lib/Alchemy/Phrasea/Response/DeliverDataInterface.php b/lib/Alchemy/Phrasea/Http/DeliverDataInterface.php similarity index 97% rename from lib/Alchemy/Phrasea/Response/DeliverDataInterface.php rename to lib/Alchemy/Phrasea/Http/DeliverDataInterface.php index 41f713a9ce..c783398be2 100644 --- a/lib/Alchemy/Phrasea/Response/DeliverDataInterface.php +++ b/lib/Alchemy/Phrasea/Http/DeliverDataInterface.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Alchemy\Phrasea\Response; +namespace Alchemy\Phrasea\Http; use Symfony\Component\HttpFoundation\ResponseHeaderBag; diff --git a/lib/Alchemy/Phrasea/Response/ServeFileResponseFactory.php b/lib/Alchemy/Phrasea/Http/ServeFileResponseFactory.php similarity index 93% rename from lib/Alchemy/Phrasea/Response/ServeFileResponseFactory.php rename to lib/Alchemy/Phrasea/Http/ServeFileResponseFactory.php index c28f8b5318..a22aeff8d4 100644 --- a/lib/Alchemy/Phrasea/Response/ServeFileResponseFactory.php +++ b/lib/Alchemy/Phrasea/Http/ServeFileResponseFactory.php @@ -9,9 +9,9 @@ * file that was distributed with this source code. */ -namespace Alchemy\Phrasea\Response; +namespace Alchemy\Phrasea\Http; -use Alchemy\Phrasea\Response\DeliverDataInterface; +use Alchemy\Phrasea\Http\DeliverDataInterface; use Alchemy\Phrasea\Application; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\BinaryFileResponse; @@ -38,7 +38,7 @@ class ServeFileResponseFactory implements DeliverDataInterface public static function create(Application $app) { return new self( - $app['phraseanet.configuration']['xsendfile']['enable'], + $app['phraseanet.configuration']['xsendfile']['enabled'], $app['unicode'] ); } @@ -46,11 +46,12 @@ class ServeFileResponseFactory implements DeliverDataInterface /** * {@inheritdoc} */ - public function deliverFile($file, $filename = '', $disposition = self::DISPOSITION_INLINE, $mimeType = null ,$cacheDuration = 3600) + public function deliverFile($file, $filename = '', $disposition = self::DISPOSITION_INLINE, $mimeType = null ,$cacheDuration = 0) { $response = new BinaryFileResponse($file); $response->setContentDisposition($disposition, $this->sanitizeFilename($filename), $this->sanitizeFilenameFallback($filename)); $response->setMaxAge($cacheDuration); + $response->setPrivate(); if (null !== $mimeType) { $response->headers->set('Content-Type', $mimeType); @@ -62,7 +63,7 @@ class ServeFileResponseFactory implements DeliverDataInterface /** * {@inheritdoc} */ - public function deliverData($data, $filename, $mimeType, $disposition = self::DISPOSITION_INLINE, $cacheDuration = 3600) + public function deliverData($data, $filename, $mimeType, $disposition = self::DISPOSITION_INLINE, $cacheDuration = 0) { $response = new Response($data); $response->headers->set('Content-Disposition', $response->headers->makeDisposition( diff --git a/lib/Alchemy/Phrasea/XSendFile/Mapping.php b/lib/Alchemy/Phrasea/Http/XsendfileMapping.php similarity index 96% rename from lib/Alchemy/Phrasea/XSendFile/Mapping.php rename to lib/Alchemy/Phrasea/Http/XsendfileMapping.php index e4c1596d76..07cdb5a385 100644 --- a/lib/Alchemy/Phrasea/XSendFile/Mapping.php +++ b/lib/Alchemy/Phrasea/Http/XsendfileMapping.php @@ -9,11 +9,11 @@ * file that was distributed with this source code. */ -namespace Alchemy\Phrasea\XSendFile; +namespace Alchemy\Phrasea\Http; use Alchemy\Phrasea\Exception\InvalidArgumentException; -class Mapping +class XsendfileMapping { private $mapping; diff --git a/lib/classes/API/V1/adapter.php b/lib/classes/API/V1/adapter.php index 5e8b847a14..7b9438ec09 100644 --- a/lib/classes/API/V1/adapter.php +++ b/lib/classes/API/V1/adapter.php @@ -351,7 +351,7 @@ class API_V1_adapter extends API_V1_Abstract 'defaultLanguage' => $app['phraseanet.registry']->get('id_GV_default_lng'), 'allowIndexing' => $app['phraseanet.registry']->get('GV_allow_search_engine'), 'modes' => array( - 'XsendFile' => $app['phraseanet.configuration']['xsendfile']['enable'], + 'XsendFile' => $app['phraseanet.configuration']['xsendfile']['enabled'], 'XsendFileMapping' => $app['phraseanet.configuration']['xsendfile']['mapping'], 'h264Streaming' => $app['phraseanet.registry']->get('GV_h264_streaming'), 'authTokenDirectory' => $app['phraseanet.registry']->get('GV_mod_auth_token_directory'), diff --git a/lib/classes/patch/3813.php b/lib/classes/patch/3813.php index 34ebf78482..f458cf2200 100644 --- a/lib/classes/patch/3813.php +++ b/lib/classes/patch/3813.php @@ -51,22 +51,24 @@ class patch_3813 implements patchInterface $xsendfilePath = $app['phraseanet.registry']->get('GV_X_Accel_Redirect'); $xsendfileMountPoint = $app['phraseanet.registry']->get('GV_X_Accel_Redirect_mount_point'); - $config = $app['phraseanet.configuration']->setDefault('xsendfile')->getConfig(); + $config = $app['phraseanet.configuration'] + ->setDefault('xsendfile') + ->getConfig(); - $config['xsendfile']['enable'] = (Boolean) $app['phraseanet.registry']->get('GV_modxsendfile', false); + $config['xsendfile']['enabled'] = (Boolean) $app['phraseanet.registry']->get('GV_modxsendfile', false); if (null !== $xsendfilePath && null !== $xsendfileMountPoint) { - $config['xsendfile']['mapping'][0] = array( + $config['xsendfile']['mapping'] = array(array( 'directory' => $xsendfilePath, 'mount-point' => $xsendfileMountPoint, - ); + )); } $app['phraseanet.configuration']->setConfig($config); $toRemove = array('GV_X_Accel_Redirect', 'GV_X_Accel_Redirect_mount_point', 'GV_modxsendfile'); - $sql = 'DELETE FROM registry WHERE key = :k'; + $sql = 'DELETE FROM registry WHERE `key` = :k'; $stmt = $appbox->get_connection()->prepare($sql); foreach ($toRemove as $registryKey) { $stmt->execute(array( diff --git a/lib/conf.d/configuration.yml b/lib/conf.d/configuration.yml index fb52350f7d..b713573135 100644 --- a/lib/conf.d/configuration.yml +++ b/lib/conf.d/configuration.yml @@ -134,7 +134,7 @@ registration-fields: name: geonameid required: true xsendfile: - enable: false + enabled: false mapping: - directory: '' diff --git a/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration-setup.yml b/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration-setup.yml index 1e9a88ae35..c449b45cfd 100644 --- a/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration-setup.yml +++ b/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration-setup.yml @@ -115,7 +115,7 @@ registration-fields: name: geonameid required: true xsendfile: - enable: false + enabled: false mapping: - directory: '' diff --git a/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration.yml b/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration.yml index 1e9a88ae35..c449b45cfd 100644 --- a/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration.yml +++ b/tests/Alchemy/Tests/Phrasea/Core/Configuration/Fixtures/configuration.yml @@ -115,7 +115,7 @@ registration-fields: name: geonameid required: true xsendfile: - enable: false + enabled: false mapping: - directory: '' diff --git a/tests/Alchemy/Tests/Phrasea/Core/Event/Subscriber/Fixtures/configuration-debugger.yml b/tests/Alchemy/Tests/Phrasea/Core/Event/Subscriber/Fixtures/configuration-debugger.yml index 7a2bd5cbc6..d29c6648a9 100644 --- a/tests/Alchemy/Tests/Phrasea/Core/Event/Subscriber/Fixtures/configuration-debugger.yml +++ b/tests/Alchemy/Tests/Phrasea/Core/Event/Subscriber/Fixtures/configuration-debugger.yml @@ -139,3 +139,9 @@ registration-fields: - name: geonameid required: true +xsendfile: + enabled: false + mapping: + - + directory: '' + mount-point: '' diff --git a/tests/Alchemy/Tests/Phrasea/Core/Event/Subscriber/Fixtures/configuration-maintenance.yml b/tests/Alchemy/Tests/Phrasea/Core/Event/Subscriber/Fixtures/configuration-maintenance.yml index 30d1b82bf6..4a9cd110d4 100644 --- a/tests/Alchemy/Tests/Phrasea/Core/Event/Subscriber/Fixtures/configuration-maintenance.yml +++ b/tests/Alchemy/Tests/Phrasea/Core/Event/Subscriber/Fixtures/configuration-maintenance.yml @@ -139,3 +139,9 @@ registration-fields: - name: geonameid required: true +xsendfile: + enabled: false + mapping: + - + directory: '' + mount-point: '' diff --git a/tests/Alchemy/Tests/Phrasea/Core/Provider/FileServeServiceProviderTest.php b/tests/Alchemy/Tests/Phrasea/Core/Provider/FileServeServiceProviderTest.php index 54ef1242f7..331603bbce 100644 --- a/tests/Alchemy/Tests/Phrasea/Core/Provider/FileServeServiceProviderTest.php +++ b/tests/Alchemy/Tests/Phrasea/Core/Provider/FileServeServiceProviderTest.php @@ -2,6 +2,10 @@ namespace Alchemy\Tests\Phrasea\Core\Provider; +use Alchemy\Phrasea\Core\Provider\ConfigurationServiceProvider; +use Alchemy\Phrasea\Core\Provider\FileServeServiceProvider; +use Silex\Application; + /** * @covers Alchemy\Phrasea\Core\Provider\FileServeServiceProvider */ @@ -10,7 +14,39 @@ class FileServeServiceProviderTest extends ServiceProviderTestCase public function provideServiceDescription() { return array( - array('Alchemy\Phrasea\Core\Provider\FileServeServiceProvider', 'phraseanet.file-serve', 'Alchemy\\Phrasea\\Response\\ServeFileResponseFactory'), + array( + 'Alchemy\Phrasea\Core\Provider\FileServeServiceProvider', + 'phraseanet.file-serve', + 'Alchemy\Phrasea\Http\ServeFileResponseFactory' + ), + array( + 'Alchemy\Phrasea\Core\Provider\FileServeServiceProvider', + 'phraseanet.xsendfile-mapping', + 'Alchemy\Phrasea\Http\XsendfileMapping' + ), ); } + + public function testMapping() + { + $app = new Application(); + + $app['root.path'] = __DIR__ . '/../../../../../..'; + $app->register(new ConfigurationServiceProvider()); + $app->register(new FileServeServiceProvider()); + $app['phraseanet.configuration.config-path'] = __DIR__ . '/fixtures/config-mapping.yml'; + $app['phraseanet.configuration.config-compiled-path'] = __DIR__ . '/fixtures/config-mapping.php'; + $this->assertEquals(array(array( + 'directory' => '/tmp', + 'mount-point' => 'mount', + ),array( + 'directory' => __DIR__ . '/../../../../../../tmp/download/', + 'mount-point' => '/download/', + ),array( + 'directory' => __DIR__ . '/../../../../../../tmp/lazaret/', + 'mount-point' => '/lazaret/', + )), $app['xsendfile.mapping']); + + unlink($app['phraseanet.configuration.config-compiled-path']); + } } diff --git a/tests/Alchemy/Tests/Phrasea/Core/Provider/XSendFileMappingServiceProviderTest.php b/tests/Alchemy/Tests/Phrasea/Core/Provider/XSendFileMappingServiceProviderTest.php deleted file mode 100644 index db61b4f9c8..0000000000 --- a/tests/Alchemy/Tests/Phrasea/Core/Provider/XSendFileMappingServiceProviderTest.php +++ /dev/null @@ -1,16 +0,0 @@ -assertInstanceOf("Symfony\Component\HttpFoundation\Response", $response); $this->assertEquals('inline; filename="cestlafete.jpg"', $response->headers->get('content-disposition')); + $this->assertEquals(0, $response->getMaxAge()); + $response->setPrivate(); + $this->assertTrue($response->headers->getCacheControlDirective('private')); + } + + public function testDeliverFileWithDuration() + { + $this->factory = new ServeFileResponseFactory(false, new \unicode()); + + $response = $this->factory->deliverFile(__DIR__ . '/../../../../files/cestlafete.jpg', 'hello', 'attachment', 'application/json', 23456); + + $this->assertEquals(23456, $response->getMaxAge()); + $this->assertTrue($response->headers->getCacheControlDirective('private')); } public function testDeliverFileWithFilename() @@ -45,7 +58,7 @@ class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract $this->factory = new ServeFileResponseFactory(true, new \unicode()); $request = Request::create('/'); $request->headers->set('X-SendFile-Type', 'X-Accel-Redirect'); - $request->headers->set('X-Accel-Mapping', (string) new Mapping(array( + $request->headers->set('X-Accel-Mapping', (string) new XsendfileMapping(array( array( 'directory' => __DIR__ . '/../../../../files/', 'mount-point' => '/protected/' @@ -65,7 +78,7 @@ class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract $this->factory = new ServeFileResponseFactory(true, new \unicode()); $request = Request::create('/'); $request->headers->set('X-SendFile-Type', 'X-Accel-Redirect'); - $request->headers->set('X-Accel-Mapping', (string) new Mapping(array( + $request->headers->set('X-Accel-Mapping', (string) new XsendfileMapping(array( array( 'directory' => __DIR__ . '/../../../../files/', 'mount-point' => '/protected/' @@ -95,7 +108,7 @@ class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract $this->factory = new ServeFileResponseFactory(true, new \unicode()); $request = Request::create('/'); $request->headers->set('X-SendFile-Type', 'X-Accel-Redirect'); - $request->headers->set('X-Accel-Mapping', (string) new Mapping(array( + $request->headers->set('X-Accel-Mapping', (string) new XsendfileMapping(array( array( 'directory' => __DIR__ . '/../../../../files/', 'mount-point' => '/protected/' diff --git a/tests/Alchemy/Tests/Phrasea/Http/XsendfileMappingTest.php b/tests/Alchemy/Tests/Phrasea/Http/XsendfileMappingTest.php new file mode 100644 index 0000000000..39231f30b9 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Http/XsendfileMappingTest.php @@ -0,0 +1,63 @@ + $dir, + 'mount-point' => '/protected/' + ) + )); + + $this->assertEquals('/protected='.realpath($dir), (string) $mapping); + } + + public function testMultiMapping() + { + $protected = __DIR__ . '/../../../../files/'; + $upload = __DIR__ . '/../../../../'; + $mapping = new XsendfileMapping(array( + array( + 'directory' => $protected, + 'mount-point' => '/protected/' + ), + array( + 'directory' => $upload, + 'mount-point' => '/uploads/' + ), + )); + + $this->assertEquals('/protected='.realpath($protected).',/uploads='.realpath($upload), (string) $mapping); + } + + public function testMultiMappingWithANotExsistingDir() + { + $protected = __DIR__ . '/../../../../files/'; + $mapping = new XsendfileMapping(array( + array( + 'directory' => $protected, + 'mount-point' => '/protected/' + ), + array( + 'directory' => '/path/to/nonexistent/directory', + 'mount-point' => '/test/' + ), + )); + + $this->assertEquals('/protected='.realpath($protected), (string) $mapping); + } + + public function testEmptyMapping() + { + $mapping = new XsendfileMapping(array()); + + $this->assertEquals('', (string) $mapping); + } +} diff --git a/tests/Alchemy/Tests/Phrasea/XSendFile/MappingTest.php b/tests/Alchemy/Tests/Phrasea/XSendFile/MappingTest.php deleted file mode 100644 index 6346e76d2c..0000000000 --- a/tests/Alchemy/Tests/Phrasea/XSendFile/MappingTest.php +++ /dev/null @@ -1,60 +0,0 @@ - __DIR__ . '/../../../../files/', - 'mount-point' => '/protected/' - ) - )); - - $this->assertEquals('/protected=/home/nlegoff/workspace/Phraseanet/tests/files', (string) $mapping); - } - - public function testMultiMapping() - { - $mapping = new Mapping(array( - array( - 'directory' => __DIR__ . '/../../../../files/', - 'mount-point' => '/protected/' - ), - array( - 'directory' => __DIR__ . '/../../../../', - 'mount-point' => '/uploads/' - ), - )); - - $this->assertEquals('/protected=/home/nlegoff/workspace/Phraseanet/tests/files,/uploads=/home/nlegoff/workspace/Phraseanet/tests', (string) $mapping); - } - - public function testMultiMappingWithANotExsistingDir() - { - $mapping = new Mapping(array( - array( - 'directory' => __DIR__ . '/../../../../files/', - 'mount-point' => '/protected/' - ), - array( - 'directory' => __DIR__ . '/../../../../do_not_exists', - 'mount-point' => '/test/' - ), - )); - - $this->assertEquals('/protected=/home/nlegoff/workspace/Phraseanet/tests/files', (string) $mapping); - } - - public function testEmptyMapping() - { - $mapping = new Mapping(array()); - - $this->assertEquals('', (string) $mapping); - } -}