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;
}
}