From 2c6ba6b99f9b9478d56b3b2ffe2484bd04b50971 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Mon, 26 May 2014 10:50:32 +0200 Subject: [PATCH 01/11] Add static file mode to deliver thumbnails --- bin/console | 7 ++ .../Setup/StaticConfigurationDumper.php | 60 ++++++++++ .../Command/Setup/StaticMappingGenerator.php | 103 ++++++++++++++++++ .../Command/Setup/StaticSymLinkGenerator.php | 85 +++++++++++++++ .../Provider/FileServeServiceProvider.php | 9 ++ .../Provider/PhraseanetServiceProvider.php | 10 ++ .../Http/StaticFile/AbstractStaticMode.php | 27 +++++ .../Phrasea/Http/StaticFile/Apache.php | 62 +++++++++++ lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php | 58 ++++++++++ .../Phrasea/Http/StaticFile/NullMode.php | 30 +++++ .../Http/StaticFile/StaticFileFactory.php | 101 +++++++++++++++++ .../StaticFile/StaticFileModeInterface.php | 25 +++++ .../Http/StaticFile/Symlink/SymLinker.php | 89 +++++++++++++++ .../StaticFile/Symlink/SymLinkerEncoder.php | 38 +++++++ lib/classes/media/subdef.php | 15 ++- lib/classes/record/adapter.php | 4 + 16 files changed, 722 insertions(+), 1 deletion(-) create mode 100644 lib/Alchemy/Phrasea/Command/Setup/StaticConfigurationDumper.php create mode 100644 lib/Alchemy/Phrasea/Command/Setup/StaticMappingGenerator.php create mode 100644 lib/Alchemy/Phrasea/Command/Setup/StaticSymLinkGenerator.php create mode 100644 lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php create mode 100644 lib/Alchemy/Phrasea/Http/StaticFile/Apache.php create mode 100644 lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php create mode 100644 lib/Alchemy/Phrasea/Http/StaticFile/NullMode.php create mode 100644 lib/Alchemy/Phrasea/Http/StaticFile/StaticFileFactory.php create mode 100644 lib/Alchemy/Phrasea/Http/StaticFile/StaticFileModeInterface.php create mode 100644 lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php create mode 100644 lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinkerEncoder.php diff --git a/bin/console b/bin/console index 449afd50d7..21e0e2dd2b 100755 --- a/bin/console +++ b/bin/console @@ -19,6 +19,9 @@ namespace KonsoleKommander; use Alchemy\Phrasea\Command\Plugin\ListPlugin; use Alchemy\Phrasea\Command\Setup\H264ConfigurationDumper; use Alchemy\Phrasea\Command\Setup\H264MappingGenerator; +use Alchemy\Phrasea\Command\Setup\StaticConfigurationDumper; +use Alchemy\Phrasea\Command\Setup\StaticMappingGenerator; +use Alchemy\Phrasea\Command\Setup\StaticSymLinkGenerator; use Alchemy\Phrasea\Core\Version; use Alchemy\Phrasea\Command\BuildMissingSubdefs; use Alchemy\Phrasea\Command\CreateCollection; @@ -105,6 +108,10 @@ $cli->command(new H264MappingGenerator()); $cli->command(new XSendFileConfigurationDumper()); $cli->command(new XSendFileMappingGenerator()); +$cli->command(new StaticConfigurationDumper()); +$cli->command(new StaticMappingGenerator()); +$cli->command(new StaticSymLinkGenerator()); + $cli->loadPlugins(); exit(is_int($cli->run()) ? : 1); diff --git a/lib/Alchemy/Phrasea/Command/Setup/StaticConfigurationDumper.php b/lib/Alchemy/Phrasea/Command/Setup/StaticConfigurationDumper.php new file mode 100644 index 0000000000..31c1ed223c --- /dev/null +++ b/lib/Alchemy/Phrasea/Command/Setup/StaticConfigurationDumper.php @@ -0,0 +1,60 @@ +setDescription('Dump the virtual host configuration depending on Phraseanet configuration'); + } + + /** + * {@inheritdoc} + */ + protected function doExecute(InputInterface $input, OutputInterface $output) + { + $output->writeln(''); + + if ($this->container['phraseanet.xsendfile-factory']->isXSendFileModeEnabled()) { + throw new \LogicException('XSendFile mode is already activated'); + } + + if (!$this->container['phraseanet.static-file-factory']->isStaticFileModeEnabled()) { + $output->writeln('Static file support is disabled'); + $ret = 1; + } else { + $output->writeln('Static file support is enabled'); + $ret = 0; + } + + try { + $configuration = $this->container['phraseanet.static-file-factory']->getMode(true, true)->getVirtualHostConfiguration(); + $output->writeln('Static file configuration seems OK'); + $output->writeln($configuration); + } catch (RuntimeException $e) { + $output->writeln('Static file configuration seems invalid'); + $ret = 1; + } + + $output->writeln(''); + + return $ret; + } +} diff --git a/lib/Alchemy/Phrasea/Command/Setup/StaticMappingGenerator.php b/lib/Alchemy/Phrasea/Command/Setup/StaticMappingGenerator.php new file mode 100644 index 0000000000..65766045c7 --- /dev/null +++ b/lib/Alchemy/Phrasea/Command/Setup/StaticMappingGenerator.php @@ -0,0 +1,103 @@ +addOption('write', 'w', null, 'Writes the configuration') + ->addOption('enabled', 'e', null, 'Set the enable toggle to `true`') + ->addArgument('type', InputArgument::REQUIRED, 'The configuration type, either `nginx` or `apache`') + ->setDescription('Generates Phraseanet Static file configuration'); + } + + /** + * {@inheritdoc} + */ + protected function doExecute(InputInterface $input, OutputInterface $output) + { + $enabled = $input->getOption('enabled'); + $type = strtolower($input->getArgument('type')); + + $factory = new StaticFileFactory($this->container['monolog'], true, $type, $this->container['phraseanet.thumb-symlinker']); + $mode = $factory->getMode(true); + + $conf = array( + 'enabled' => $enabled, + 'type' => $type, + 'mapping' => $mode->getMapping(), + ); + + if ($input->getOption('write')) { + $output->write("Writing configuration ..."); + $this->container['phraseanet.configuration']['static-file'] = $conf; + $output->writeln(" OK"); + $output->writeln(""); + $output->write("It is now strongly recommended to use static-file:dump-configuration command to upgrade your virtual-host"); + } else { + $output->writeln("Configuration will not be written, use --write option to write it"); + $output->writeln(""); + $output->writeln(Yaml::dump(array('static-file' => $conf), 4)); + } + + return 0; + } + + private function computeMapping($paths) + { + $paths = array_unique($paths); + + $ret = array(); + + foreach ($paths as $path) { + $ret[$path] = $this->pathsToConf($path); + } + + return $ret; + } + + private function pathsToConf($path) + { + static $n = 0; + $n++; + + return array('mount-point' => 'mp4-videos-'.$n, 'directory' => $path, 'passphrase' => \random::generatePassword(32)); + } + + private function extractPath(\appbox $appbox) + { + $paths = array(); + + foreach ($appbox->get_databoxes() as $databox) { + foreach ($databox->get_subdef_structure() as $group => $subdefs) { + if ('video' !== $group) { + continue; + } + foreach ($subdefs as $subdef) { + $paths[] = $subdef->get_path(); + } + } + } + + return array_filter(array_unique($paths)); + } +} diff --git a/lib/Alchemy/Phrasea/Command/Setup/StaticSymLinkGenerator.php b/lib/Alchemy/Phrasea/Command/Setup/StaticSymLinkGenerator.php new file mode 100644 index 0000000000..bca4689144 --- /dev/null +++ b/lib/Alchemy/Phrasea/Command/Setup/StaticSymLinkGenerator.php @@ -0,0 +1,85 @@ +setDescription('Generates Phraseanet Static file symlinks'); + } + + /** + * {@inheritdoc} + */ + protected function doExecute(InputInterface $input, OutputInterface $output) + { + if (!$this->container['phraseanet.static-file-factory']->isStaticFileModeEnabled()) { + $output->writeln('Static file support is disabled'); + + return 1; + } + + $output->writeln("Removing symlinks ..."); + $this->container['filesystem']->remove($this->container['phraseanet.thumb-symlinker']->getPublicDir()); + $total = 0; + foreach ($this->container['phraseanet.appbox']->get_databoxes() as $databox) { + $sql = 'SELECT count(subdef_id) as total FROM subdef WHERE `name`="thumbnail"'; + $stmt = $databox->get_connection()->prepare($sql); + $stmt->execute(); + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + $stmt->closeCursor(); + $total += $row['total']; + } + $output->writeln("Creating symlinks ..."); + $progress = $this->getHelperSet()->get('progress'); + $progress->start($output, $total); + $i = 0; + do { + foreach ($this->container['phraseanet.appbox']->get_databoxes() as $databox) { + $sql = 'SELECT record_id FROM record'; + $stmt = $databox->get_connection()->prepare($sql); + $stmt->execute(); + $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC); + $stmt->closeCursor(); + + foreach ($rows as $row) { + $record = $databox->get_record($row['record_id']); + foreach ($record->get_subdefs() as $subdef) { + if ($subdef->get_name() !== 'thumbnail') { + continue; + } + $this->container['phraseanet.thumb-symlinker']->symlink($subdef->get_pathfile()); + $progress->advance(); + $i++; + } + } + } + } while ($i < $total); + + $progress->finish(); + + $output->writeln("OK"); + + return 0; + } +} diff --git a/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php index a22ab5d29d..8ed200e297 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php @@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Core\Provider; use Alchemy\Phrasea\Core\Event\Subscriber\XSendFileSubscriber; use Alchemy\Phrasea\Http\H264PseudoStreaming\H264Factory; use Alchemy\Phrasea\Http\ServeFileResponseFactory; +use Alchemy\Phrasea\Http\StaticFile\StaticFileFactory; use Alchemy\Phrasea\Http\XSendFile\XSendFileFactory; use Silex\Application; use Silex\ServiceProviderInterface; @@ -33,10 +34,18 @@ class FileServeServiceProvider implements ServiceProviderInterface return H264Factory::create($app); }); + $app['phraseanet.static-file-factory'] = $app->share(function ($app) { + return StaticFileFactory::create($app); + }); + $app['phraseanet.h264'] = $app->share(function ($app) { return $app['phraseanet.h264-factory']->createMode(false); }); + $app['phraseanet.static-file'] = $app->share(function ($app) { + return $app['phraseanet.static-file-factory']->getMode(false); + }); + $app['phraseanet.file-serve'] = $app->share(function (Application $app) { return ServeFileResponseFactory::create($app); }); diff --git a/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php index 1719745b3a..7a4caaa0d6 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/PhraseanetServiceProvider.php @@ -11,6 +11,8 @@ namespace Alchemy\Phrasea\Core\Provider; +use Alchemy\Phrasea\Http\StaticFile\Symlink\SymLinker; +use Alchemy\Phrasea\Http\StaticFile\Symlink\SymLinkerEncoder; use Alchemy\Phrasea\Metadata\PhraseanetMetadataReader; use Alchemy\Phrasea\Metadata\PhraseanetMetadataSetter; use Alchemy\Phrasea\Security\Firewall; @@ -41,6 +43,14 @@ class PhraseanetServiceProvider implements ServiceProviderInterface return $events; }); + $app['phraseanet.thumb-symlinker'] = $app->share(function (SilexApplication $app) { + return SymLinker::create($app); + }); + + $app['phraseanet.thumb-symlinker-encoder'] = $app->share(function (SilexApplication $app) { + return SymLinkerEncoder::create($app); + }); + $app['phraseanet.metadata-reader'] = $app->share(function (SilexApplication $app) { $reader = new PhraseanetMetadataReader(); diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php b/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php new file mode 100644 index 0000000000..50e87bee7b --- /dev/null +++ b/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php @@ -0,0 +1,27 @@ +symlinker = $symlinker; + + parent::__construct($mapping); + } +} diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php b/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php new file mode 100644 index 0000000000..f614c8b0a1 --- /dev/null +++ b/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php @@ -0,0 +1,62 @@ +mapping = $mapping; + } + + /** + * {@inheritdoc} + */ + public function getUrl($pathFile) + { + return Url::factory(sprintf('%s/%s', $this->mapping['mount-point'], $this->symlinker->getSymlinkBasePath($pathFile))); + } + + /** + * {@inheritdoc} + */ + public function getVirtualHostConfiguration() + { + $output = "\n"; + $output .= " Alias ".$this->mapping['mount-point']." ".$this->mapping['directory']."\n"; + $output .= "\n"; + $output .= " mapping['directory'].">\n"; + $output .= " Order allow,deny\n"; + $output .= " Allow from all\n"; + $output .= " \n"; + $output .= "\n"; + + return $output; + } +} diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php b/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php new file mode 100644 index 0000000000..5b23398d6d --- /dev/null +++ b/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php @@ -0,0 +1,58 @@ +mapping = $mapping; + } + + /** + * {@inheritdoc} + */ + public function getUrl($pathFile) + { + return Url::factory(sprintf('%s/%s', $this->mapping['mount-point'], $this->symlinker->getSymlinkBasePath($pathFile))); + } + + /** + * {@inheritdoc} + */ + public function getVirtualHostConfiguration() + { + $output = "\n"; + $output .= " location " . $this->mapping['mount-point']. " {\n"; + $output .= " alias ".$this->mapping['directory'].";\n"; + $output .= " }\n"; + + return $output; + } +} diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/NullMode.php b/lib/Alchemy/Phrasea/Http/StaticFile/NullMode.php new file mode 100644 index 0000000000..af0989d954 --- /dev/null +++ b/lib/Alchemy/Phrasea/Http/StaticFile/NullMode.php @@ -0,0 +1,30 @@ +logger = $logger; + $this->enabled = (Boolean) $enabled; + $this->type = strtolower($type); + $this->symlinker = $symlinker; + + $this->mapping = array( + 'mount-point' => $symlinker->getDefaultAlias(), + 'directory' => $symlinker->getPublicDir() + ); + } + + /** + * Creates a new instance of XSendFile Factory according to the application + * configuration. + * + * @param Application $app + * @return XSendFileFactory + */ + public static function create(Application $app) + { + $conf = $app['phraseanet.configuration']['static-file']; + + return new self($app['monolog'], $conf['enabled'], $conf['type'], $app['phraseanet.thumb-symlinker']); + } + + /** + * Returns a new instance of ModeInterface. + * + * @return ModeInterface + * + * @throws InvalidArgumentException if mode type is unknown + */ + public function getMode($throwException = false, $forceMode = false) + { + if (false === $this->enabled && true !== $forceMode) { + return new NullMode(); + } + + switch ($this->type) { + case 'nginx': + return new Nginx($this->mapping, $this->symlinker); + break; + case 'apache': + case 'apache2': + return new Apache($this->mapping, $this->symlinker); + default: + $this->logger->error('Invalid static file configuration.'); + if ($throwException) { + throw new InvalidArgumentException(sprintf( + 'Invalid static file type value "%s"', + $this->type + )); + } + + return new NullMode(); + } + } + + /** + * @return Boolean + */ + public function isStaticFileModeEnabled() + { + return $this->enabled; + } +} diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/StaticFileModeInterface.php b/lib/Alchemy/Phrasea/Http/StaticFile/StaticFileModeInterface.php new file mode 100644 index 0000000000..33a9ad31ee --- /dev/null +++ b/lib/Alchemy/Phrasea/Http/StaticFile/StaticFileModeInterface.php @@ -0,0 +1,25 @@ +encoder = $encoder; + $this->fs = $fs; + $this->registry = $registry; + $this->rootPath = $rootPath; + $this->publicDir = sprintf('%s/public/thumbnails', rtrim($this->rootPath, '/')); + } + + public function getPublicDir() + { + return $this->publicDir; + } + + public function getDefaultAlias() + { + return sprintf('/%s', self::ALIAS); + } + + public function symlink($pathFile) + { + $this->fs->symlink($pathFile, $this->getSymlinkPath($pathFile)) ; + } + + public function getSymlink($pathFile) + { + return $this->encoder->encode($pathFile); + } + + public function getSymlinkBasePath($pathFile) + { + $symlinkName = $this->getSymlink($pathFile); + + return sprintf('%s/%s/%s', + substr($symlinkName, 0, 2), + substr($symlinkName, 2, 2), + substr($symlinkName, 4) + ); + } + + public function getSymlinkPath($pathFile) + { + return sprintf( + '%s/%s', + $this->publicDir, + $this->getSymlinkBasePath($pathFile) + ); + } +} diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinkerEncoder.php b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinkerEncoder.php new file mode 100644 index 0000000000..b659d4e912 --- /dev/null +++ b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinkerEncoder.php @@ -0,0 +1,38 @@ +key = $key; + } + + public function encode($pathFile) + { + return hash_hmac('sha512', $pathFile , $this->key); + } +} diff --git a/lib/classes/media/subdef.php b/lib/classes/media/subdef.php index 08e264ab71..563880e7dc 100644 --- a/lib/classes/media/subdef.php +++ b/lib/classes/media/subdef.php @@ -730,6 +730,10 @@ class media_subdef extends media_abstract implements cache_cacheableInterface $subdef->get_permalink()->delete_data_from_cache(); } + if ($name === 'thumbnail' && $app['phraseanet.static-file-factory']->isStaticFileModeEnabled()) { + $app['phraseanet.thumb-symlinker']->symlink($subdef->get_pathfile()); + } + unset($media); return $subdef; @@ -742,10 +746,18 @@ class media_subdef extends media_abstract implements cache_cacheableInterface */ protected function generate_url() { - if ( ! $this->is_physically_present()) { + if (!$this->is_physically_present()) { return; } + if ($this->get_name() === 'thumbnail') { + if ($this->app['phraseanet.static-file-factory']->isStaticFileModeEnabled() && null !== $url = $this->app['phraseanet.static-file']->getUrl($this->get_pathfile())) { + $this->url = $url; + + return; + } + } + if ($this->app['phraseanet.h264-factory']->isH264Enabled() && in_array($this->mime, array('video/mp4'))) { if (null !== $url = $this->app['phraseanet.h264']->getUrl($this->get_pathfile())) { $this->url = $url; @@ -758,6 +770,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface . "/" . $this->record->get_record_id() . "/" . $this->get_name() . "/"); + return; } diff --git a/lib/classes/record/adapter.php b/lib/classes/record/adapter.php index 54ed198901..c6375bca7e 100644 --- a/lib/classes/record/adapter.php +++ b/lib/classes/record/adapter.php @@ -1514,6 +1514,10 @@ class record_adapter implements record_Interface, cache_cacheableInterface if (!$subdef->is_physically_present()) continue; + if ($subdef->get_name() === 'thumbnail' && $this->app['phraseanet.static-file-factory']->isStaticFileModeEnabled()) { + $this->app['filesystem']->remove($this->app['phraseanet.thumb-symlinker']->getSymlinkPath($subdef->get_pathfile())); + } + $ftodel[] = $subdef->get_pathfile(); $watermark = $subdef->get_path() . 'watermark_' . $subdef->get_file(); if (file_exists($watermark)) From 09785c67bd9875902c59a30afb65f9ba156d7f97 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Tue, 27 May 2014 19:30:29 +0200 Subject: [PATCH 02/11] Ensure symlink is always created --- lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php | 7 +++++++ lib/Alchemy/Phrasea/Http/StaticFile/Apache.php | 2 ++ lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php | 2 ++ lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php | 5 +++++ 4 files changed, 16 insertions(+) diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php b/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php index 50e87bee7b..eb09db5de5 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php @@ -24,4 +24,11 @@ abstract class AbstractStaticMode extends AbstractServerMode parent::__construct($mapping); } + + protected function ensureSymlink($pathFile) + { + if (false === $this->symlinker->hasSymlink($pathFile)) { + $this->symlinker->symlink($pathFile); + } + } } diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php b/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php index f614c8b0a1..76b77df46b 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php @@ -40,6 +40,8 @@ class Apache extends AbstractStaticMode implements StaticFileModeInterface */ public function getUrl($pathFile) { + $this->ensureSymlink($pathFile); + return Url::factory(sprintf('%s/%s', $this->mapping['mount-point'], $this->symlinker->getSymlinkBasePath($pathFile))); } diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php b/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php index 5b23398d6d..8019f61ae5 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php @@ -40,6 +40,8 @@ class Nginx extends AbstractStaticMode implements StaticFileModeInterface */ public function getUrl($pathFile) { + $this->ensureSymlink($pathFile); + return Url::factory(sprintf('%s/%s', $this->mapping['mount-point'], $this->symlinker->getSymlinkBasePath($pathFile))); } diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php index 320e158c59..87b53f3d7f 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php @@ -86,4 +86,9 @@ class SymLinker $this->getSymlinkBasePath($pathFile) ); } + + public function hasSymlink($pathFile) + { + return file_exists($this->getSymlinkPath($pathFile)); + } } From 4dcd0b85fb86fa11c8b2f36bc45d2f012fddcce0 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Wed, 28 May 2014 12:25:03 +0200 Subject: [PATCH 03/11] Add patch --- lib/classes/patch/385alpha1a.php | 64 ++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 lib/classes/patch/385alpha1a.php diff --git a/lib/classes/patch/385alpha1a.php b/lib/classes/patch/385alpha1a.php new file mode 100644 index 0000000000..b867d7ca8f --- /dev/null +++ b/lib/classes/patch/385alpha1a.php @@ -0,0 +1,64 @@ +release; + } + + /** + * {@inheritdoc} + */ + public function require_all_upgrades() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function concern() + { + return $this->concern; + } + + /** + * {@inheritdoc} + */ + public function apply(base $appbox, Application $app) + { + $config = $app['phraseanet.configuration']->getConfig(); + + $config['static-file'] = array( + 'enabled' => false, + 'type' => '', + ); + + $app['phraseanet.configuration']->setConfig($config); + + return true; + } +} From 2c023f4bca6e9077160f6c1be960bd495e99dc99 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Wed, 28 May 2014 12:25:18 +0200 Subject: [PATCH 04/11] Remove command --- bin/console | 2 - .../Command/Setup/StaticSymLinkGenerator.php | 85 ------------------- 2 files changed, 87 deletions(-) delete mode 100644 lib/Alchemy/Phrasea/Command/Setup/StaticSymLinkGenerator.php diff --git a/bin/console b/bin/console index 21e0e2dd2b..4fb292526e 100755 --- a/bin/console +++ b/bin/console @@ -21,7 +21,6 @@ use Alchemy\Phrasea\Command\Setup\H264ConfigurationDumper; use Alchemy\Phrasea\Command\Setup\H264MappingGenerator; use Alchemy\Phrasea\Command\Setup\StaticConfigurationDumper; use Alchemy\Phrasea\Command\Setup\StaticMappingGenerator; -use Alchemy\Phrasea\Command\Setup\StaticSymLinkGenerator; use Alchemy\Phrasea\Core\Version; use Alchemy\Phrasea\Command\BuildMissingSubdefs; use Alchemy\Phrasea\Command\CreateCollection; @@ -110,7 +109,6 @@ $cli->command(new XSendFileMappingGenerator()); $cli->command(new StaticConfigurationDumper()); $cli->command(new StaticMappingGenerator()); -$cli->command(new StaticSymLinkGenerator()); $cli->loadPlugins(); diff --git a/lib/Alchemy/Phrasea/Command/Setup/StaticSymLinkGenerator.php b/lib/Alchemy/Phrasea/Command/Setup/StaticSymLinkGenerator.php deleted file mode 100644 index bca4689144..0000000000 --- a/lib/Alchemy/Phrasea/Command/Setup/StaticSymLinkGenerator.php +++ /dev/null @@ -1,85 +0,0 @@ -setDescription('Generates Phraseanet Static file symlinks'); - } - - /** - * {@inheritdoc} - */ - protected function doExecute(InputInterface $input, OutputInterface $output) - { - if (!$this->container['phraseanet.static-file-factory']->isStaticFileModeEnabled()) { - $output->writeln('Static file support is disabled'); - - return 1; - } - - $output->writeln("Removing symlinks ..."); - $this->container['filesystem']->remove($this->container['phraseanet.thumb-symlinker']->getPublicDir()); - $total = 0; - foreach ($this->container['phraseanet.appbox']->get_databoxes() as $databox) { - $sql = 'SELECT count(subdef_id) as total FROM subdef WHERE `name`="thumbnail"'; - $stmt = $databox->get_connection()->prepare($sql); - $stmt->execute(); - $row = $stmt->fetch(\PDO::FETCH_ASSOC); - $stmt->closeCursor(); - $total += $row['total']; - } - $output->writeln("Creating symlinks ..."); - $progress = $this->getHelperSet()->get('progress'); - $progress->start($output, $total); - $i = 0; - do { - foreach ($this->container['phraseanet.appbox']->get_databoxes() as $databox) { - $sql = 'SELECT record_id FROM record'; - $stmt = $databox->get_connection()->prepare($sql); - $stmt->execute(); - $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC); - $stmt->closeCursor(); - - foreach ($rows as $row) { - $record = $databox->get_record($row['record_id']); - foreach ($record->get_subdefs() as $subdef) { - if ($subdef->get_name() !== 'thumbnail') { - continue; - } - $this->container['phraseanet.thumb-symlinker']->symlink($subdef->get_pathfile()); - $progress->advance(); - $i++; - } - } - } - } while ($i < $total); - - $progress->finish(); - - $output->writeln("OK"); - - return 0; - } -} From f27b960eebe1253d93ea487c68a6516715ecc3a9 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Wed, 28 May 2014 12:25:57 +0200 Subject: [PATCH 05/11] Remove links when unmounting db --- lib/classes/databox.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/classes/databox.php b/lib/classes/databox.php index a765bb4440..8a60d992b7 100644 --- a/lib/classes/databox.php +++ b/lib/classes/databox.php @@ -445,6 +445,21 @@ class databox extends base public function unmount_databox() { + if ($this->app['phraseanet.static-file-factory']->isStaticFileModeEnabled()) { + $sql = "SELECT path, file FROM subdef WHERE `name`='thumbnail'"; + $stmt = $this->get_connection()->prepare($sql); + $stmt->execute(); + $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC); + $stmt->closeCursor(); + foreach ($rows as $row) { + $pathfile = $this->app['phraseanet.thumb-symlinker']->getSymlinkPath(sprintf( + '%s/%s', + rtrim($row['path'], '/'), + $row['file'] + )); + $this->app['filesystem']->remove($pathfile); + } + } foreach ($this->get_collections() as $collection) { $collection->unmount_collection($this->app); } From 9ea18799c26b1a477b92a4f071bc98655152f06f Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Wed, 28 May 2014 12:26:17 +0200 Subject: [PATCH 06/11] Desactivate lazyload --- templates/web/prod/results/record.html.twig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/web/prod/results/record.html.twig b/templates/web/prod/results/record.html.twig index f2c96a2175..330df541f8 100644 --- a/templates/web/prod/results/record.html.twig +++ b/templates/web/prod/results/record.html.twig @@ -59,9 +59,10 @@ {% if rollover_gif %} {% set extraclass = 'rollover-gif-out' %} {% endif %} - {{thumbnail.format(record.get_thumbnail(), th_size, th_size, extraclass, true, true)}} + {% set lazyload = not app['phraseanet.static-file-factory'].isStaticFileModeEnabled %} + {{thumbnail.format(record.get_thumbnail(), th_size, th_size, extraclass, true, lazyload)}} {% if rollover_gif %} - {{thumbnail.format(rollover_gif, th_size, th_size, 'rollover-gif-hover', true, true)}} + {{thumbnail.format(rollover_gif, th_size, th_size, 'rollover-gif-hover', true, lazyload)}} {% endif %} From e72a8ebda3a65d2f87cb81e378b0c47afedd4de0 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Wed, 28 May 2014 12:27:16 +0200 Subject: [PATCH 07/11] Update configuration --- config/configuration.sample.yml | 7 +++++-- lib/conf.d/configuration.yml | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/config/configuration.sample.yml b/config/configuration.sample.yml index 8ea3bb4151..195e68d419 100644 --- a/config/configuration.sample.yml +++ b/config/configuration.sample.yml @@ -157,8 +157,11 @@ api_cors: max_age: 0 hosts: [] session: - idle: 0 - lifetime: 604800 # 1 week + idle: 0 + lifetime: 604800 # 1 week +static-file: + enabled: false + type: nginx crossdomain: site-control: 'master-only' allow-access-from: diff --git a/lib/conf.d/configuration.yml b/lib/conf.d/configuration.yml index d11f93caaf..7b5b1ac4f8 100644 --- a/lib/conf.d/configuration.yml +++ b/lib/conf.d/configuration.yml @@ -163,6 +163,10 @@ session: idle: 0 # 1 week lifetime: 604800 +static-file: + enabled: false + # 1 week + type: nginx crossdomain: allow-access-from: - From df013d4f21c14849f3a0a46748929375289a4337 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Wed, 28 May 2014 13:02:10 +0200 Subject: [PATCH 08/11] Allow to specify symlink directory --- config/configuration.sample.yml | 1 + .../Http/StaticFile/StaticFileFactory.php | 21 +++++++++------- .../Http/StaticFile/Symlink/SymLinker.php | 25 +++++++++++-------- lib/conf.d/configuration.yml | 2 +- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/config/configuration.sample.yml b/config/configuration.sample.yml index 195e68d419..5402a00fe6 100644 --- a/config/configuration.sample.yml +++ b/config/configuration.sample.yml @@ -162,6 +162,7 @@ session: static-file: enabled: false type: nginx + symlink-directory: '' crossdomain: site-control: 'master-only' allow-access-from: diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/StaticFileFactory.php b/lib/Alchemy/Phrasea/Http/StaticFile/StaticFileFactory.php index a11e566165..0af3339f11 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/StaticFileFactory.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/StaticFileFactory.php @@ -21,15 +21,16 @@ class StaticFileFactory private $enabled; private $logger; private $type; + /** @var Symlink\SymLinker */ private $symlinker; /** * Constructor * * @param LoggerInterface $logger - * @param boolean $enabled + * @param bool $enabled * @param string $type - * @param array $symlinker + * @param SymLinker $symlinker */ public function __construct(LoggerInterface $logger, $enabled, $type, SymLinker $symlinker) { @@ -40,16 +41,16 @@ class StaticFileFactory $this->mapping = array( 'mount-point' => $symlinker->getDefaultAlias(), - 'directory' => $symlinker->getPublicDir() + 'directory' => $symlinker->getSymlinkDir() ); } /** - * Creates a new instance of XSendFile Factory according to the application + * Creates a new instance of StaticFileFactory Factory according to the application * configuration. * * @param Application $app - * @return XSendFileFactory + * @return StaticFileFactory */ public static function create(Application $app) { @@ -59,11 +60,13 @@ class StaticFileFactory } /** - * Returns a new instance of ModeInterface. + * Returns a new instance of ModeInterface * - * @return ModeInterface + * @param bool $throwException + * @param bool $forceMode * - * @throws InvalidArgumentException if mode type is unknown + * @return Apache|Nginx|NullMode + * @throws InvalidArgumentException */ public function getMode($throwException = false, $forceMode = false) { @@ -92,7 +95,7 @@ class StaticFileFactory } /** - * @return Boolean + * @return bool */ public function isStaticFileModeEnabled() { diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php index 87b53f3d7f..f0f1f6f7c1 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php @@ -11,22 +11,23 @@ namespace Alchemy\Phrasea\Http\StaticFile\Symlink; +use Alchemy\Phrasea\Exception\InvalidArgumentException; use Silex\Application; use Symfony\Component\Filesystem\Filesystem; use Guzzle\Http\Url; /** - * Create & retrieve symlinks from public directory + * Create & retrieve symlinks */ class SymLinker { + /** Mount Point Alias Name */ const ALIAS = 'thumb'; protected $encoder; protected $fs; - protected $publicDir; + protected $symlinkDir; protected $registry; - protected $rootPath; public static function create(Application $app) { @@ -34,22 +35,24 @@ class SymLinker $app['phraseanet.thumb-symlinker-encoder'], $app['filesystem'], $app['phraseanet.registry'], - $app['root.path'] + isset($app['phraseanet.configuration']['static-file']['symlink-directory']) ? $app['phraseanet.configuration']['static-file']['symlink-directory'] : null ); } - public function __construct(SymLinkerEncoder $encoder, Filesystem $fs, \registryInterface $registry, $rootPath) + public function __construct(SymLinkerEncoder $encoder, Filesystem $fs, \registryInterface $registry, $symlinkDir) { $this->encoder = $encoder; $this->fs = $fs; - $this->registry = $registry; - $this->rootPath = $rootPath; - $this->publicDir = sprintf('%s/public/thumbnails', rtrim($this->rootPath, '/')); + + if (!$symlinkDir) { + throw new InvalidArgumentException("Symlink directory is not defined"); + } + $this->symlinkDir = rtrim($symlinkDir, '/'); } - public function getPublicDir() + public function getSymlinkDir() { - return $this->publicDir; + return $this->symlinkDir; } public function getDefaultAlias() @@ -82,7 +85,7 @@ class SymLinker { return sprintf( '%s/%s', - $this->publicDir, + $this->symlinkDir, $this->getSymlinkBasePath($pathFile) ); } diff --git a/lib/conf.d/configuration.yml b/lib/conf.d/configuration.yml index 7b5b1ac4f8..3f5dfc2cac 100644 --- a/lib/conf.d/configuration.yml +++ b/lib/conf.d/configuration.yml @@ -165,8 +165,8 @@ session: lifetime: 604800 static-file: enabled: false - # 1 week type: nginx + symlink-directory: '' crossdomain: allow-access-from: - From 9ba9533925a03435d3da2bc0a7ffc19ad88c204d Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Fri, 30 May 2014 11:47:43 +0200 Subject: [PATCH 09/11] Fix github comments --- .../Command/Setup/StaticMappingGenerator.php | 39 ------------------- .../Http/StaticFile/AbstractStaticMode.php | 18 ++++++++- .../Phrasea/Http/StaticFile/Apache.php | 12 ------ lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php | 12 ------ .../Http/StaticFile/Symlink/SymLinker.php | 6 +-- lib/classes/patch/385alpha1a.php | 1 + 6 files changed, 19 insertions(+), 69 deletions(-) diff --git a/lib/Alchemy/Phrasea/Command/Setup/StaticMappingGenerator.php b/lib/Alchemy/Phrasea/Command/Setup/StaticMappingGenerator.php index 65766045c7..1ecaff8592 100644 --- a/lib/Alchemy/Phrasea/Command/Setup/StaticMappingGenerator.php +++ b/lib/Alchemy/Phrasea/Command/Setup/StaticMappingGenerator.php @@ -61,43 +61,4 @@ class StaticMappingGenerator extends Command return 0; } - - private function computeMapping($paths) - { - $paths = array_unique($paths); - - $ret = array(); - - foreach ($paths as $path) { - $ret[$path] = $this->pathsToConf($path); - } - - return $ret; - } - - private function pathsToConf($path) - { - static $n = 0; - $n++; - - return array('mount-point' => 'mp4-videos-'.$n, 'directory' => $path, 'passphrase' => \random::generatePassword(32)); - } - - private function extractPath(\appbox $appbox) - { - $paths = array(); - - foreach ($appbox->get_databoxes() as $databox) { - foreach ($databox->get_subdef_structure() as $group => $subdefs) { - if ('video' !== $group) { - continue; - } - foreach ($subdefs as $subdef) { - $paths[] = $subdef->get_path(); - } - } - } - - return array_filter(array_unique($paths)); - } } diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php b/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php index eb09db5de5..a87237450c 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/AbstractStaticMode.php @@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\Http\StaticFile; use Alchemy\Phrasea\Http\AbstractServerMode; use Alchemy\Phrasea\Http\StaticFile\Symlink\SymLinker; +use Guzzle\Http\Url; abstract class AbstractStaticMode extends AbstractServerMode { @@ -25,7 +26,22 @@ abstract class AbstractStaticMode extends AbstractServerMode parent::__construct($mapping); } - protected function ensureSymlink($pathFile) + /** + * {@inheritdoc} + */ + public function getUrl($pathFile) + { + $this->ensureSymlink($pathFile); + + return Url::factory(sprintf('%s/%s', $this->mapping['mount-point'], $this->symlinker->getSymlinkBasePath($pathFile))); + } + + /** + * Creates a link if it does not exists + * + * @param $pathFile + */ + private function ensureSymlink($pathFile) { if (false === $this->symlinker->hasSymlink($pathFile)) { $this->symlinker->symlink($pathFile); diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php b/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php index 76b77df46b..f200550987 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/Apache.php @@ -12,8 +12,6 @@ namespace Alchemy\Phrasea\Http\StaticFile; use Alchemy\Phrasea\Exception\InvalidArgumentException; -use Alchemy\Phrasea\Http\AbstractServerMode; -use Guzzle\Http\Url; class Apache extends AbstractStaticMode implements StaticFileModeInterface { @@ -35,16 +33,6 @@ class Apache extends AbstractStaticMode implements StaticFileModeInterface $this->mapping = $mapping; } - /** - * {@inheritdoc} - */ - public function getUrl($pathFile) - { - $this->ensureSymlink($pathFile); - - return Url::factory(sprintf('%s/%s', $this->mapping['mount-point'], $this->symlinker->getSymlinkBasePath($pathFile))); - } - /** * {@inheritdoc} */ diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php b/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php index 8019f61ae5..a105f93336 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/Nginx.php @@ -12,8 +12,6 @@ namespace Alchemy\Phrasea\Http\StaticFile; use Alchemy\Phrasea\Exception\InvalidArgumentException; -use Alchemy\Phrasea\Http\AbstractServerMode; -use Guzzle\Http\Url; class Nginx extends AbstractStaticMode implements StaticFileModeInterface { @@ -35,16 +33,6 @@ class Nginx extends AbstractStaticMode implements StaticFileModeInterface $this->mapping = $mapping; } - /** - * {@inheritdoc} - */ - public function getUrl($pathFile) - { - $this->ensureSymlink($pathFile); - - return Url::factory(sprintf('%s/%s', $this->mapping['mount-point'], $this->symlinker->getSymlinkBasePath($pathFile))); - } - /** * {@inheritdoc} */ diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php index f0f1f6f7c1..0c21b0bfc8 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/Symlink/SymLinker.php @@ -35,7 +35,7 @@ class SymLinker $app['phraseanet.thumb-symlinker-encoder'], $app['filesystem'], $app['phraseanet.registry'], - isset($app['phraseanet.configuration']['static-file']['symlink-directory']) ? $app['phraseanet.configuration']['static-file']['symlink-directory'] : null + isset($app['phraseanet.configuration']['static-file']['symlink-directory']) ? $app['phraseanet.configuration']['static-file']['symlink-directory'] : $app['root.path'] . '/symlinks' ); } @@ -43,10 +43,6 @@ class SymLinker { $this->encoder = $encoder; $this->fs = $fs; - - if (!$symlinkDir) { - throw new InvalidArgumentException("Symlink directory is not defined"); - } $this->symlinkDir = rtrim($symlinkDir, '/'); } diff --git a/lib/classes/patch/385alpha1a.php b/lib/classes/patch/385alpha1a.php index b867d7ca8f..de5550fd8f 100644 --- a/lib/classes/patch/385alpha1a.php +++ b/lib/classes/patch/385alpha1a.php @@ -55,6 +55,7 @@ class patch_385alpha1a implements patchInterface $config['static-file'] = array( 'enabled' => false, 'type' => '', + 'symlink-directory' => '', ); $app['phraseanet.configuration']->setConfig($config); From ad69e19b925f09d820573cd45feaa3101fc0fc44 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Fri, 30 May 2014 14:35:11 +0200 Subject: [PATCH 10/11] Add unit tests --- .../Phrasea/Http/StaticFile/NullMode.php | 2 +- .../Http/StaticFile/ApacheModeTest.php | 37 +++++++++++++ .../Phrasea/Http/StaticFile/NginxModeTest.php | 37 +++++++++++++ .../Http/StaticFile/StaticFileFactoryTest.php | 54 +++++++++++++++++++ 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 tests/Alchemy/Tests/Phrasea/Http/StaticFile/ApacheModeTest.php create mode 100644 tests/Alchemy/Tests/Phrasea/Http/StaticFile/NginxModeTest.php create mode 100644 tests/Alchemy/Tests/Phrasea/Http/StaticFile/StaticFileFactoryTest.php diff --git a/lib/Alchemy/Phrasea/Http/StaticFile/NullMode.php b/lib/Alchemy/Phrasea/Http/StaticFile/NullMode.php index af0989d954..9a9f40608c 100644 --- a/lib/Alchemy/Phrasea/Http/StaticFile/NullMode.php +++ b/lib/Alchemy/Phrasea/Http/StaticFile/NullMode.php @@ -11,7 +11,7 @@ namespace Alchemy\Phrasea\Http\StaticFile; -class NullMode extends AbstractServerMode implements StaticFileModeInterface +class NullMode implements StaticFileModeInterface { /** * {@inheritdoc} diff --git a/tests/Alchemy/Tests/Phrasea/Http/StaticFile/ApacheModeTest.php b/tests/Alchemy/Tests/Phrasea/Http/StaticFile/ApacheModeTest.php new file mode 100644 index 0000000000..39a90c1a42 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Http/StaticFile/ApacheModeTest.php @@ -0,0 +1,37 @@ + __DIR__, + 'mount-point' => '/thumbs' + ), self::$DI['app']['phraseanet.thumb-symlinker']); + $conf = $mode->getVirtualHostConfiguration(); + $this->assertRegExp('#'.__DIR__ . '#', $conf); + } + + /** + * @dataProvider provideMappings + * @expectedException Alchemy\Phrasea\Exception\InvalidArgumentException + */ + public function testInvalidMapping($mapping) + { + new Apache($mapping, self::$DI['app']['phraseanet.thumb-symlinker']); + } + + public function provideMappings() + { + return array( + array(array('Directory' => __DIR__)), + array(array('wrong-key' => __DIR__, 'mount-point' => '/')), + array(array('directory' => __DIR__, 'wrong-key' => '/')), + ); + } +} diff --git a/tests/Alchemy/Tests/Phrasea/Http/StaticFile/NginxModeTest.php b/tests/Alchemy/Tests/Phrasea/Http/StaticFile/NginxModeTest.php new file mode 100644 index 0000000000..1f89f488d0 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Http/StaticFile/NginxModeTest.php @@ -0,0 +1,37 @@ + __DIR__, + 'mount-point' => '/thumbs' + ), self::$DI['app']['phraseanet.thumb-symlinker']); + $conf = $mode->getVirtualHostConfiguration(); + $this->assertRegExp('#'.__DIR__ . '#', $conf); + } + + /** + * @dataProvider provideMappings + * @expectedException Alchemy\Phrasea\Exception\InvalidArgumentException + */ + public function testInvalidMapping($mapping) + { + new Nginx($mapping, self::$DI['app']['phraseanet.thumb-symlinker']); + } + + public function provideMappings() + { + return array( + array(array('Directory' => __DIR__)), + array(array('wrong-key' => __DIR__, 'mount-point' => '/')), + array(array('directory' => __DIR__, 'wrong-key' => '/')), + ); + } +} diff --git a/tests/Alchemy/Tests/Phrasea/Http/StaticFile/StaticFileFactoryTest.php b/tests/Alchemy/Tests/Phrasea/Http/StaticFile/StaticFileFactoryTest.php new file mode 100644 index 0000000000..450ad0e7d2 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Http/StaticFile/StaticFileFactoryTest.php @@ -0,0 +1,54 @@ +assertInstanceOf('Alchemy\Phrasea\Http\StaticFile\StaticFileFactory', $factory); + } + + public function testFactoryWithStaticFileEnable() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $factory = new StaticFileFactory($logger, true, 'nginx', self::$DI['app']['phraseanet.thumb-symlinker']); + $this->assertInstanceOf('Alchemy\Phrasea\Http\StaticFile\AbstractStaticMode', $factory->getMode()); + } + + public function testFactoryWithStaticFileDisabled() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $factory = new StaticFileFactory($logger, false, 'nginx', self::$DI['app']['phraseanet.thumb-symlinker']); + $this->assertInstanceOf('Alchemy\Phrasea\Http\StaticFile\NullMode', $factory->getMode()); + $this->assertFalse($factory->isStaticFileModeEnabled()); + } + + /** + * @expectedException Alchemy\Phrasea\Exception\InvalidArgumentException + */ + public function testFactoryWithWrongTypeThrowsAnExceptionIfRequired() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $factory = new StaticFileFactory($logger, true, 'wrong-type', self::$DI['app']['phraseanet.thumb-symlinker']); + $factory->getMode(true); + } + + public function testFactoryWithWrongTypeDoesNotThrowsAnException() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $logger->expects($this->once()) + ->method('error') + ->with($this->isType('string')); + + $factory = new StaticFileFactory($logger, true, 'wrong-type', self::$DI['app']['phraseanet.thumb-symlinker']); + $this->assertInstanceOf('Alchemy\Phrasea\Http\StaticFile\NullMode', $factory->getMode(false)); + } +} From f047878f368ea733c671b63d410c9f6d6fb667bb Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Wed, 10 Sep 2014 11:29:08 +0200 Subject: [PATCH 11/11] bump to version 3.8.6-alpha3a --- lib/Alchemy/Phrasea/Core/Version.php | 7 +------ lib/classes/patch/{385alpha1a.php => 386alpha3a.php} | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) rename lib/classes/patch/{385alpha1a.php => 386alpha3a.php} (96%) diff --git a/lib/Alchemy/Phrasea/Core/Version.php b/lib/Alchemy/Phrasea/Core/Version.php index 209e36a2e8..cdffc01ed5 100644 --- a/lib/Alchemy/Phrasea/Core/Version.php +++ b/lib/Alchemy/Phrasea/Core/Version.php @@ -11,14 +11,9 @@ namespace Alchemy\Phrasea\Core; -/** - * - * @license http://opensource.org/licenses/gpl-3.0 GPLv3 - * @link www.phraseanet.com - */ class Version { - protected static $number = '3.8.6-alpha.2'; + protected static $number = '3.8.6-alpha.3'; protected static $name = 'Falcarius'; public static function getNumber() diff --git a/lib/classes/patch/385alpha1a.php b/lib/classes/patch/386alpha3a.php similarity index 96% rename from lib/classes/patch/385alpha1a.php rename to lib/classes/patch/386alpha3a.php index de5550fd8f..58350db477 100644 --- a/lib/classes/patch/385alpha1a.php +++ b/lib/classes/patch/386alpha3a.php @@ -13,7 +13,7 @@ use Alchemy\Phrasea\Application; use Symfony\Component\Finder\Finder; use Symfony\Component\Filesystem\Filesystem; -class patch_385alpha1a implements patchInterface +class patch_386alpha3a implements patchInterface { /** @var string */ private $release = '3.8.5-alpha.1';