diff --git a/bin/setup b/bin/setup index fa0ac5c9d0..a78f06182a 100755 --- a/bin/setup +++ b/bin/setup @@ -15,6 +15,9 @@ use Alchemy\Phrasea\Core\Version; use Alchemy\Phrasea\Command\UpgradeDBDatas; use Alchemy\Phrasea\Command\Setup\Install; use Alchemy\Phrasea\Command\Setup\PluginsReset; +use Alchemy\Phrasea\Command\Plugin\ListPlugin; +use Alchemy\Phrasea\Command\Plugin\AddPlugin; +use Alchemy\Phrasea\Command\Plugin\RemovePlugin; use Alchemy\Phrasea\CLI; use Alchemy\Phrasea\Command\Setup\CheckEnvironment; use Alchemy\Phrasea\Core\CLIProvider\DoctrineMigrationServiceProvider; @@ -58,6 +61,9 @@ if ($app['phraseanet.configuration-tester']->isInstalled()) { $app->command(new UpgradeDBDatas('system:upgrade-datas')); } +$app->command(new AddPlugin()); +$app->command(new ListPlugin()); +$app->command(new RemovePlugin()); $app->command(new PluginsReset()); $app->command(new CheckEnvironment('check:system')); $app->command(new Install('system:install')); diff --git a/config/configuration.sample.yml b/config/configuration.sample.yml index 9151ee0dc0..5e90137357 100644 --- a/config/configuration.sample.yml +++ b/config/configuration.sample.yml @@ -28,6 +28,11 @@ main: search-engine: type: Alchemy\Phrasea\SearchEngine\Phrasea\PhraseaEngine options: [] + task-manager: + logger: + max-files: 10 + enabled: true + level: INFO session: type: 'file' options: [] diff --git a/lib/Alchemy/Phrasea/CLI.php b/lib/Alchemy/Phrasea/CLI.php index 12c48d9a1d..27c826a6c7 100644 --- a/lib/Alchemy/Phrasea/CLI.php +++ b/lib/Alchemy/Phrasea/CLI.php @@ -82,6 +82,13 @@ class CLI extends Application $app->run(); } + public function boot() + { + parent::boot(); + + $this['console']->setDispatcher($this['dispatcher']); + } + public function run(\Symfony\Component\HttpFoundation\Request $request = null) { if (null !== $request) { diff --git a/lib/Alchemy/Phrasea/Command/Plugin/AbstractPluginCommand.php b/lib/Alchemy/Phrasea/Command/Plugin/AbstractPluginCommand.php index a100760b67..9d3ae8e735 100644 --- a/lib/Alchemy/Phrasea/Command/Plugin/AbstractPluginCommand.php +++ b/lib/Alchemy/Phrasea/Command/Plugin/AbstractPluginCommand.php @@ -30,6 +30,19 @@ abstract class AbstractPluginCommand extends Command return $manifests; } + protected function doExecute(InputInterface $input, OutputInterface $output) + { + if (basename($_SERVER['PHP_SELF']) === 'console') { + $output->writeln(""); + $output->writeln(sprintf(' /!\ Warning, this command is deprecated and will be removed as of Phraseanet 3.9, please use bin/setup %s instead /!\ ', $this->getName())); + $output->writeln(""); + } + + return $this->doExecutePluginAction($input, $output); + } + + abstract protected function doExecutePluginAction(InputInterface $input, OutputInterface $output); + protected function updateConfigFiles(InputInterface $input, OutputInterface $output) { $manifests = $this->validatePlugins($input, $output); diff --git a/lib/Alchemy/Phrasea/Command/Plugin/AddPlugin.php b/lib/Alchemy/Phrasea/Command/Plugin/AddPlugin.php index ebb9930e13..cd223e22b0 100644 --- a/lib/Alchemy/Phrasea/Command/Plugin/AddPlugin.php +++ b/lib/Alchemy/Phrasea/Command/Plugin/AddPlugin.php @@ -26,7 +26,7 @@ class AddPlugin extends AbstractPluginCommand ->addArgument('source', InputArgument::REQUIRED, 'The source is a folder'); } - protected function doExecute(InputInterface $input, OutputInterface $output) + protected function doExecutePluginAction(InputInterface $input, OutputInterface $output) { $source = $input->getArgument('source'); diff --git a/lib/Alchemy/Phrasea/Command/Plugin/ListPlugin.php b/lib/Alchemy/Phrasea/Command/Plugin/ListPlugin.php index db269b02ef..0b6eee9070 100644 --- a/lib/Alchemy/Phrasea/Command/Plugin/ListPlugin.php +++ b/lib/Alchemy/Phrasea/Command/Plugin/ListPlugin.php @@ -27,7 +27,7 @@ class ListPlugin extends AbstractPluginCommand ->addOption('json', 'j', InputOption::VALUE_NONE, 'Output result in JSON'); } - protected function doExecute(InputInterface $input, OutputInterface $output) + protected function doExecutePluginAction(InputInterface $input, OutputInterface $output) { $plugins = array_map(function (Plugin $plugin) use ($input) { if ($plugin->isErroneous()) { diff --git a/lib/Alchemy/Phrasea/Command/Plugin/RemovePlugin.php b/lib/Alchemy/Phrasea/Command/Plugin/RemovePlugin.php index e6625ef56c..0c3147f2a8 100644 --- a/lib/Alchemy/Phrasea/Command/Plugin/RemovePlugin.php +++ b/lib/Alchemy/Phrasea/Command/Plugin/RemovePlugin.php @@ -12,6 +12,7 @@ namespace Alchemy\Phrasea\Command\Plugin; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputArgument; @@ -23,13 +24,20 @@ class RemovePlugin extends AbstractPluginCommand $this ->setDescription('Removes a plugin given its name') - ->addArgument('name', InputArgument::REQUIRED, 'The name of the plugin'); + ->addArgument('name', InputArgument::REQUIRED, 'The name of the plugin') + ->addOption('keep-config', 'k', InputOption::VALUE_NONE, 'Use this flag to keep configuration'); } - protected function doExecute(InputInterface $input, OutputInterface $output) + protected function doExecutePluginAction(InputInterface $input, OutputInterface $output) { $name = $input->getArgument('name'); + if (!$this->container['plugins.manager']->hasPlugin($name)) { + $output->writeln(sprintf('There is no plugin named %s, aborting', $name)); + + return 0; + } + $output->write("Removing public assets..."); $this->container['plugins.assets-manager']->remove($name); $output->writeln(" OK"); @@ -42,6 +50,12 @@ class RemovePlugin extends AbstractPluginCommand $this->updateConfigFiles($input, $output); + if (!$input->getOption('keep-config')) { + $conf = $this->container['phraseanet.configuration']->getConfig(); + unset($conf['plugins'][$name]); + $this->container['phraseanet.configuration']->setConfig($conf); + } + return 0; } } diff --git a/lib/Alchemy/Phrasea/Command/Task/SchedulerRun.php b/lib/Alchemy/Phrasea/Command/Task/SchedulerRun.php index e0aa5ef58c..6c373de070 100644 --- a/lib/Alchemy/Phrasea/Command/Task/SchedulerRun.php +++ b/lib/Alchemy/Phrasea/Command/Task/SchedulerRun.php @@ -14,6 +14,8 @@ namespace Alchemy\Phrasea\Command\Task; use Alchemy\TaskManager\TaskManager; use Alchemy\Phrasea\Command\Command; use Alchemy\TaskManager\Event\TaskManagerSubscriber\LockFileSubscriber; +use Monolog\Handler\RotatingFileHandler; +use Monolog\Logger; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -38,6 +40,12 @@ class SchedulerRun extends Command protected function doExecute(InputInterface $input, OutputInterface $output) { declare(ticks=1); + + if ($this->container['task-manager.logger.configuration']['enabled']) { + $file = $this->container['task-manager.log-file.factory']->forManager(); + $this->container['task-manager.logger']->pushHandler(new RotatingFileHandler($file, $this->container['task-manager.logger.configuration']['max-files'], $this->container['task-manager.logger.configuration']['level'])); + } + $this->container['signal-handler']->register([SIGINT, SIGTERM], [$this, 'signalHandler']); $this->container['task-manager']->addSubscriber(new LockFileSubscriber($this->container['task-manager.logger'], $this->container['root.path'].'/tmp/locks')); $this->container['task-manager']->start(); diff --git a/lib/Alchemy/Phrasea/Command/Task/TaskRun.php b/lib/Alchemy/Phrasea/Command/Task/TaskRun.php index 5a2a13a2a9..81491ee376 100644 --- a/lib/Alchemy/Phrasea/Command/Task/TaskRun.php +++ b/lib/Alchemy/Phrasea/Command/Task/TaskRun.php @@ -20,6 +20,7 @@ use Alchemy\TaskManager\Event\JobSubscriber\LockFileSubscriber; use Alchemy\TaskManager\Event\JobSubscriber\MemoryLimitSubscriber; use Alchemy\TaskManager\Event\JobSubscriber\SignalControlledSubscriber; use Alchemy\TaskManager\Event\JobSubscriber\StopSignalSubscriber; +use Monolog\Handler\RotatingFileHandler; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputArgument; @@ -51,6 +52,11 @@ class TaskRun extends Command $job = $this->container['task-manager.job-factory']->create($task->getJobId()); $logger = $this->container['task-manager.logger']; + if ($this->container['task-manager.logger.configuration']['enabled']) { + $file = $this->container['task-manager.log-file.factory']->forTask($task); + $logger->pushHandler(new RotatingFileHandler($file, $this->container['task-manager.logger.configuration']['max-files'], $this->container['task-manager.logger.configuration']['level'])); + } + $job->addSubscriber(new LockFileSubscriber('task-'.$task->getId(), $logger, $this->container['root.path'].'/tmp/locks')); $job->addSubscriber(new StopSignalSubscriber($this->container['signal-handler'], $logger)); diff --git a/lib/Alchemy/Phrasea/Core/CLIProvider/TaskManagerServiceProvider.php b/lib/Alchemy/Phrasea/Core/CLIProvider/TaskManagerServiceProvider.php index 52d1216383..ea85adabcc 100644 --- a/lib/Alchemy/Phrasea/Core/CLIProvider/TaskManagerServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/CLIProvider/TaskManagerServiceProvider.php @@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Core\CLIProvider; use Alchemy\TaskManager\TaskManager; use Alchemy\Phrasea\TaskManager\TaskList; use Monolog\Handler\NullHandler; +use Monolog\Logger; use Silex\Application; use Silex\ServiceProviderInterface; use Symfony\Component\Process\PhpExecutableFinder; @@ -44,6 +45,18 @@ class TaskManagerServiceProvider implements ServiceProviderInterface ); }); + $app['task-manager.logger.configuration'] = $app->share(function (Application $app) { + $conf = array_replace([ + 'enabled' => true, + 'level' => 'INFO', + 'max-files' => 10, + ], $app['conf']->get(['main', 'task-manager', 'logger'], [])); + + $conf['level'] = defined('Monolog\\Logger::'.$conf['level']) ? constant('Monolog\\Logger::'.$conf['level']) : Logger::INFO; + + return $conf; + }); + $app['task-manager.task-list'] = $app->share(function (Application $app) { $conf = $app['conf']->get(['registry', 'executables', 'php-conf-path']); $finder = new PhpExecutableFinder(); diff --git a/lib/classes/patch/383alpha5a.php b/lib/classes/patch/383alpha5a.php new file mode 100644 index 0000000000..87896eb463 --- /dev/null +++ b/lib/classes/patch/383alpha5a.php @@ -0,0 +1,63 @@ +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['main']['task-manager']['logger'] = array( + 'enabled' => true, + 'max-files' => 10, + 'level' => 'INFO', + ); + + $app['phraseanet.configuration']->setConfig($config); + + return true; + } +} diff --git a/lib/conf.d/configuration.yml b/lib/conf.d/configuration.yml index 3cafee1617..445451e7c0 100644 --- a/lib/conf.d/configuration.yml +++ b/lib/conf.d/configuration.yml @@ -28,6 +28,11 @@ main: search-engine: type: Alchemy\Phrasea\SearchEngine\Phrasea\PhraseaEngine options: [] + task-manager: + logger: + max-files: 10 + enabled: true + level: INFO session: type: 'file' options: [] diff --git a/templates/mobile/common/index.html.twig b/templates/mobile/common/index.html.twig index bd3344b7b7..834a332023 100644 --- a/templates/mobile/common/index.html.twig +++ b/templates/mobile/common/index.html.twig @@ -4,6 +4,7 @@ + {{ app['conf'].get(['registry', 'general', 'title']) }} - {{ module_name }} diff --git a/templates/mobile/common/thumbnail.html.twig b/templates/mobile/common/thumbnail.html.twig index e3854a2772..fbcd30ac36 100644 --- a/templates/mobile/common/thumbnail.html.twig +++ b/templates/mobile/common/thumbnail.html.twig @@ -34,7 +34,7 @@ {% set random = thumbnail.get_random() %}
{% elseif record_type == 'FLEXPAPER' %} @@ -79,8 +79,8 @@ {% if record_type == 'VIDEO_MP4' or record_type == 'VIDEO_FLV' %} {% set random = thumbnail.get_random() %}
-
{% elseif record_type == 'FLEXPAPER' %} diff --git a/tests/Alchemy/Tests/Phrasea/Command/Plugin/RemovePluginTest.php b/tests/Alchemy/Tests/Phrasea/Command/Plugin/RemovePluginTest.php index 29baa854b6..ccc6e5f638 100644 --- a/tests/Alchemy/Tests/Phrasea/Command/Plugin/RemovePluginTest.php +++ b/tests/Alchemy/Tests/Phrasea/Command/Plugin/RemovePluginTest.php @@ -12,15 +12,31 @@ class RemovePluginTest extends PluginCommandTestCase $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); $input->expects($this->once()) - ->method('getArgument') - ->with($this->equalTo('name')) - ->will($this->returnValue($name)); + ->method('getArgument') + ->with($this->equalTo('name')) + ->will($this->returnValue($name)); + $input->expects($this->any()) + ->method('getOption') + ->will($this->returnCallback(function ($option) { + if ($option === 'keep-config') { + return false; + } + })); $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); $command = new RemovePlugin(); $command->setContainer(self::$DI['cli']); + self::$DI['cli']['plugins.manager'] = $this->getMockBuilder('Alchemy\Phrasea\Plugin\PluginManager') + ->disableOriginalConstructor() + ->getMock(); + + self::$DI['cli']['plugins.manager']->expects($this->once()) + ->method('hasPlugin') + ->with('test-plugin') + ->will($this->returnValue(true)); + self::$DI['cli']['filesystem'] = $this->createFilesystemMock(); self::$DI['cli']['filesystem']->expects($this->at(0)) ->method('remove') @@ -33,5 +49,60 @@ class RemovePluginTest extends PluginCommandTestCase $result = $command->execute($input, $output); $this->assertSame(0, $result); + + $conf = self::$DI['cli']['phraseanet.configuration']->getConfig(); + $this->assertArrayNotHasKey('test-plugin', $conf['plugins']); + } + + public function testExecuteWithoutRemoveConfig() + { + $name = 'test-plugin'; + + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->once()) + ->method('getArgument') + ->with($this->equalTo('name')) + ->will($this->returnValue($name)); + $input->expects($this->any()) + ->method('getOption') + ->will($this->returnCallback(function ($option) { + if ($option === 'keep-config') { + return true; + } + })); + + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $command = new RemovePlugin(); + $command->setContainer(self::$DI['cli']); + + $data = $this->addPluginData(); + + self::$DI['cli']['filesystem'] = $this->createFilesystemMock(); + self::$DI['cli']['filesystem']->expects($this->at(0)) + ->method('remove') + ->with(self::$DI['cli']['root.path'].'/www/plugins/'.$name); + + self::$DI['cli']['filesystem']->expects($this->at(1)) + ->method('remove') + ->with(self::$DI['cli']['plugins.directory'].'/'.$name); + + $result = $command->execute($input, $output); + + $this->assertSame(0, $result); + + $conf = self::$DI['cli']['phraseanet.configuration']->getConfig(); + $this->assertSame($data, $conf['plugins']['test-plugin']); + } + + private function addPluginData() + { + $data = array('key' => 'value'); + + $conf = self::$DI['cli']['phraseanet.configuration']->getConfig(); + $conf['plugins']['test-plugin'] = $data; + self::$DI['cli']['phraseanet.configuration']->setConfig($conf); + + return $data; } }