Merge branch '3.8'

Conflicts:
	config/configuration.sample.yml
	lib/Alchemy/Phrasea/Core/Version.php
	lib/classes/module/console/schedulerStart.php
	lib/classes/module/console/taskrun.php
	lib/conf.d/configuration.yml
	templates/mobile/common/index.html.twig
This commit is contained in:
Nicolas Le Goff
2014-02-03 19:02:22 +01:00
15 changed files with 222 additions and 10 deletions

View File

@@ -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'));

View File

@@ -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: []

View File

@@ -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) {

View File

@@ -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('<error> /!\ </error> <comment>Warning</comment>, this command is deprecated and will be removed as of Phraseanet 3.9, please use <info>bin/setup %s</info> instead <error> /!\ </error>', $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);

View File

@@ -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');

View File

@@ -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()) {

View File

@@ -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 <comment>%s</comment>, aborting', $name));
return 0;
}
$output->write("Removing public assets...");
$this->container['plugins.assets-manager']->remove($name);
$output->writeln(" <comment>OK</comment>");
@@ -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;
}
}

View File

@@ -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();

View File

@@ -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));

View File

@@ -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();

View File

@@ -0,0 +1,63 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
class patch_383alpha5a implements patchInterface
{
/** @var string */
private $release = '3.8.3-alpha.5';
/** @var array */
private $concern = array(base::APPLICATION_BOX);
/**
* {@inheritdoc}
*/
public function get_release()
{
return $this->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;
}
}

View File

@@ -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: []

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="viewport" content="initial-scale=1">
<title>{{ app['conf'].get(['registry', 'general', 'title']) }} - {{ module_name }} </title>
<link rel="stylesheet" href="{{ path('minifier', { 'f' : 'assets/jquery-mobile/jquery.mobile.css' }) }}" />
<script src="{{ path('minifier', { 'f' : 'assets/jquery/jquery.js' }) }}"></script>

View File

@@ -34,7 +34,7 @@
{% set random = thumbnail.get_random() %}
<div class="record record_video imgTips" style="width:{{d_width|round}}px;height:{{d_height|round}}px;top:{{top|round}}px;">
<video type="video/mp4" controls="controls" style="width:{{d_width|round}}px;height:{{d_height|round}}px;" autoplay="autoplay">
<source src="{{thumbnail.get_url()}}" type="video/mp4"></source>
<source src="{{thumbnail.get_url()}}" type="video/mp4" />
</video>
</div>
{% elseif record_type == 'FLEXPAPER' %}
@@ -79,8 +79,8 @@
{% if record_type == 'VIDEO_MP4' or record_type == 'VIDEO_FLV' %}
{% set random = thumbnail.get_random() %}
<div class="record record_video imgTips">
<video type="video/mp4" controls="controls" style="height:100%;" autoplay="autoplay">
<source src="{{thumbnail.get_url()}}" type="video/mp4"></source>
<video type="video/mp4" controls="controls" style="height:80%;width:80%;max-height: 80%;max-width:80%; margin: 0 auto" autoplay="autoplay">
<source src="{{thumbnail.get_url()}}" type="video/mp4" />
</video>
</div>
{% elseif record_type == 'FLEXPAPER' %}

View File

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