diff --git a/bin/setup b/bin/setup
index b43d1fae58..0bb44c9508 100755
--- a/bin/setup
+++ b/bin/setup
@@ -20,6 +20,7 @@ use Alchemy\Phrasea\Core\Version;
use Alchemy\Phrasea\Command\UpgradeDBDatas;
use Alchemy\Phrasea\Command\Setup\Install;
use Alchemy\Phrasea\CLI;
+use Alchemy\Phrasea\Command\Setup\CheckEnvironment;
require_once __DIR__ . '/../vendor/autoload.php';
@@ -59,7 +60,7 @@ try {
$app->command(new UpgradeDBDatas('system:upgrade-datas'));
}
- $app->command(new \module_console_systemConfigCheck('check:system'));
+ $app->command(new CheckEnvironment('check:system'));
$app->command(new Install('system:install'));
$result_code = is_int($app->run()) ? : 1;
diff --git a/lib/Alchemy/Phrasea/Application/Setup.php b/lib/Alchemy/Phrasea/Application/Setup.php
index a7c12bb41b..6e4d443885 100644
--- a/lib/Alchemy/Phrasea/Application/Setup.php
+++ b/lib/Alchemy/Phrasea/Application/Setup.php
@@ -11,16 +11,29 @@
namespace Alchemy\Phrasea\Application;
-use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Controller\Setup\Installer;
use Alchemy\Phrasea\Controller\Utils\ConnectionTest;
use Alchemy\Phrasea\Controller\Utils\PathFileTest;
+use Silex\Application as SilexApplication;
+use Symfony\Component\HttpFoundation\Response;
return call_user_func(function() {
- $app = new Application();
+ $app = new SilexApplication();
- $app->get('/', function(PhraseaApplication $app) {
+ $app['debug'] = true;
+
+ $app['twig'] = $app->share(function (SilexApplication $app) {
+ $ld_path = array(__DIR__ . '/../../../../templates/web');
+ $loader = new \Twig_Loader_Filesystem($ld_path);
+
+ $twig = new \Twig_Environment($loader);
+ $twig->addExtension(new \Twig_Extensions_Extension_I18n());
+
+ return $twig;
+ });
+
+ $app->get('/', function(SilexApplication $app) {
if (!$app['phraseanet.configuration-tester']->isBlank()) {
return $app->redirect('/login/');
}
@@ -33,12 +46,12 @@ return call_user_func(function() {
$app->mount('/connection_test', new ConnectionTest());
$app->error(function($e) use ($app) {
- if ($e instanceof \Exception_Setup_PhraseaAlreadyInstalled) {
- return $app->redirect('/login/');
- }
+ if ($e instanceof \Exception_Setup_PhraseaAlreadyInstalled) {
+ return $app->redirect('/login/');
+ }
- return new Response('Internal Server Error', 500, array('X-Status-Code' => 500));
- });
+ return new Response('Internal Server Error', 500, array('X-Status-Code' => 500));
+ });
return $app;
});
diff --git a/lib/Alchemy/Phrasea/Command/Setup/CheckEnvironment.php b/lib/Alchemy/Phrasea/Command/Setup/CheckEnvironment.php
new file mode 100644
index 0000000000..2757c2dd78
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Setup/CheckEnvironment.php
@@ -0,0 +1,91 @@
+setDescription("Check environment");
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ foreach(array(
+ new BinariesRequirements(),
+ new FilesystemRequirements(),
+ new LocalesRequirements(),
+ new PhraseaRequirements(),
+ new PhpRequirements(),
+ new SystemRequirements(),
+ ) as $collection) {
+
+ $output->writeln('');
+ $output->writeln($collection->getName() . ' requirements : ');
+ $output->writeln('');
+
+ foreach ($collection->getRequirements() as $requirement) {
+
+ $result = $requirement->isFulfilled() ? 'OK ' : ($requirement->isOptional() ? 'WARNING ' : 'ERROR ');
+ $output->write(' ' . $result);
+
+ $output->writeln($requirement->getTestMessage());
+
+ if (!$requirement->isFulfilled()) {
+ $output->writeln(" " . $requirement->getHelpText());
+ $output->writeln('');
+ }
+ }
+
+ $output->writeln('');
+ $output->writeln($collection->getName() . ' recommendations : ');
+ $output->writeln('');
+
+ foreach ($collection->getRecommendations() as $requirement) {
+
+ $result = $requirement->isFulfilled() ? 'OK ' : ($requirement->isOptional() ? 'WARNING ' : 'ERROR ');
+ $output->write(' ' . $result);
+
+ $output->writeln($requirement->getTestMessage());
+
+ if (!$requirement->isFulfilled()) {
+ $output->writeln(" " . $requirement->getHelpText());
+ $output->writeln('');
+ }
+ }
+ }
+
+ return;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Controller/Admin/Dashboard.php b/lib/Alchemy/Phrasea/Controller/Admin/Dashboard.php
index f370e465d9..18b7e633af 100644
--- a/lib/Alchemy/Phrasea/Controller/Admin/Dashboard.php
+++ b/lib/Alchemy/Phrasea/Controller/Admin/Dashboard.php
@@ -140,15 +140,6 @@ class Dashboard implements ControllerProviderInterface
'cache_flushed' => $request->query->get('flush_cache') === 'ok',
'admins' => \User_Adapter::get_sys_admins($app),
'email_status' => $emailStatus,
- 'search_engine_status' => $app['phraseanet.SE']->getStatus(),
- 'php_version_constraints' => \setup::check_php_version(),
- 'writability_constraints' => \setup::check_writability($app['phraseanet.registry']),
- 'binaries_constraints' => \setup::check_binaries($app['phraseanet.registry']),
- 'php_extension_constraints' => \setup::check_php_extension(),
- 'cache_constraints' => \setup::check_cache_server(),
- 'phrasea_constraints' => \setup::check_phrasea(),
- 'cache_opcode_constraints' => \setup::check_cache_opcode(),
- 'php_configuration_constraints' => \setup::check_php_configuration(),
);
return $app['twig']->render('admin/dashboard.html.twig', $parameters);
diff --git a/lib/Alchemy/Phrasea/Controller/Setup/Installer.php b/lib/Alchemy/Phrasea/Controller/Setup/Installer.php
index e4a45202d0..17ad414911 100644
--- a/lib/Alchemy/Phrasea/Controller/Setup/Installer.php
+++ b/lib/Alchemy/Phrasea/Controller/Setup/Installer.php
@@ -38,64 +38,62 @@ class Installer implements ControllerProviderInterface
public function rootInstaller(Application $app, Request $request)
{
- $php_constraint = \setup::check_php_version();
- $writability_constraints = \setup::check_writability(new \Setup_Registry());
- $extension_constraints = \setup::check_php_extension();
- $opcode_constraints = \setup::check_cache_opcode();
- $php_conf_constraints = \setup::check_php_configuration();
- $locales_constraints = \setup::check_system_locales($app);
-
+// $php_constraint = \setup::check_php_version();
+// $writability_constraints = \setup::check_writability(new \Setup_Registry());
+// $extension_constraints = \setup::check_php_extension();
+// $opcode_constraints = \setup::check_cache_opcode();
+// $php_conf_constraints = \setup::check_php_configuration();
+// $locales_constraints = \setup::check_system_locales($app);
+//
+// $constraints_coll = array(
+// 'php_constraint' => $php_constraint
+// , 'writability_constraints' => $writability_constraints
+// , 'extension_constraints' => $extension_constraints
+// , 'opcode_constraints' => $opcode_constraints
+// , 'php_conf_constraints' => $php_conf_constraints
+// , 'locales_constraints' => $locales_constraints
+// );
$constraints_coll = array(
- 'php_constraint' => $php_constraint
- , 'writability_constraints' => $writability_constraints
- , 'extension_constraints' => $extension_constraints
- , 'opcode_constraints' => $opcode_constraints
- , 'php_conf_constraints' => $php_conf_constraints
- , 'locales_constraints' => $locales_constraints
+ new \Alchemy\Phrasea\Setup\Requirements\BinariesRequirements(),
+ new \Alchemy\Phrasea\Setup\Requirements\FilesystemRequirements(),
+ new \Alchemy\Phrasea\Setup\Requirements\LocalesRequirements(),
+ new \Alchemy\Phrasea\Setup\Requirements\PhpRequirements(),
+ new \Alchemy\Phrasea\Setup\Requirements\PhraseaRequirements(),
+ new \Alchemy\Phrasea\Setup\Requirements\SystemRequirements(),
);
- $redirect = true;
+// $redirect = true;
- foreach ($constraints_coll as $key => $constraints) {
- $unset = true;
- foreach ($constraints as $constraint) {
- if (!$constraint->is_ok() && $constraint->is_blocker())
- $redirect = $unset = false;
- }
- if ($unset === true) {
- unset($constraints_coll[$key]);
- }
- }
+// foreach ($constraints_coll as $key => $constraints) {
+// $unset = true;
+// foreach ($constraints as $constraint) {
+// if (!$constraint->is_ok() && $constraint->is_blocker())
+// $redirect = $unset = false;
+// }
+// if ($unset === true) {
+// unset($constraints_coll[$key]);
+// }
+// }
- if ($redirect) {
- return $app->redirect('/setup/installer/step2/');
- }
+// if ($redirect) {
+// return $app->redirect('/setup/installer/step2/');
+// }
- $app['twig.loader.filesystem']->setPaths(array(
- __DIR__ . '/../../../../../templates/web'
+// $app['twig.loader.filesystem']->setPaths(array(
+// __DIR__ . '/../../../../../templates/web'
+// ));
+
+ return $app['twig']->render('/setup/index.html.twig', array(
+ 'locale' => $app['locale'],
+ 'available_locales' => $app->getAvailableLanguages(),
+ 'current_servername' => $request->getScheme() . '://' . $request->getHttpHost() . '/',
+ 'constraints' => $constraints_coll,
));
-
- return $app['twig']->render(
- '/setup/index.html.twig'
- , array_merge($constraints_coll, array(
- 'locale' => $app['locale']
- , 'available_locales' => $app->getAvailableLanguages()
- , 'version_number' => $app['phraseanet.version']->getNumber()
- , 'version_name' => $app['phraseanet.version']->getName()
- , 'current_servername' => $request->getScheme() . '://' . $request->getHttpHost() . '/'
- ))
- );
}
public function getInstallForm(Application $app, Request $request)
{
\phrasea::use_i18n($app['locale']);
- $ld_path = array(__DIR__ . '/../../../../../templates/web');
- $loader = new \Twig_Loader_Filesystem($ld_path);
-
- $twig = new \Twig_Environment($loader);
- $twig->addExtension(new \Twig_Extensions_Extension_I18n());
-
$warnings = array();
$php_constraint = \setup::check_php_version();
@@ -127,20 +125,20 @@ class Installer implements ControllerProviderInterface
$warnings[] = _('It is not recommended to install Phraseanet without HTTPS support');
}
- return $twig->render(
- '/setup/step2.html.twig'
- , array(
- 'locale' => $app['locale']
- , 'available_locales' => $app->getAvailableLanguages()
- , 'available_templates' => array('en', 'fr')
- , 'version_number' => $app['phraseanet.version']->getNumber()
- , 'version_name' => $app['phraseanet.version']->getName()
- , 'warnings' => $warnings
- , 'error' => $request->query->get('error')
- , 'current_servername' => $request->getScheme() . '://' . $request->getHttpHost() . '/'
- , 'discovered_binaries' => \setup::discover_binaries()
- , 'rootpath' => dirname(dirname(dirname(dirname(__DIR__)))) . '/'
- ));
+ return $app['twig']->render(
+ '/setup/step2.html.twig'
+ , array(
+ 'locale' => $app['locale']
+ , 'available_locales' => $app->getAvailableLanguages()
+ , 'available_templates' => array('en', 'fr')
+ , 'version_number' => $app['phraseanet.version']->getNumber()
+ , 'version_name' => $app['phraseanet.version']->getName()
+ , 'warnings' => $warnings
+ , 'error' => $request->query->get('error')
+ , 'current_servername' => $request->getScheme() . '://' . $request->getHttpHost() . '/'
+ , 'discovered_binaries' => \setup::discover_binaries()
+ , 'rootpath' => dirname(dirname(dirname(dirname(__DIR__)))) . '/'
+ ));
}
public function doInstall(Application $app, Request $request)
diff --git a/lib/Alchemy/Phrasea/Setup/ConfigurationTester.php b/lib/Alchemy/Phrasea/Setup/ConfigurationTester.php
index b2d682dc51..bd06cabe59 100644
--- a/lib/Alchemy/Phrasea/Setup/ConfigurationTester.php
+++ b/lib/Alchemy/Phrasea/Setup/ConfigurationTester.php
@@ -12,17 +12,28 @@
namespace Alchemy\Phrasea\Setup;
use Alchemy\Phrasea\Application;
-use Alchemy\Phrasea\Setup\System\ProbeInterface as SystemProbeInterface;
use Alchemy\Phrasea\Setup\Version\Probe\Probe31;
use Alchemy\Phrasea\Setup\Version\Probe\Probe35;
use Alchemy\Phrasea\Setup\Version\Probe\ProbeInterface as VersionProbeInterface;
+use Alchemy\Phrasea\Setup\Probe\BinariesProbe;
+use Alchemy\Phrasea\Setup\Probe\CacheServerProbe;
+use Alchemy\Phrasea\Setup\Probe\OpcodeCacheProbe;
+use Alchemy\Phrasea\Setup\Probe\FilesystemProbe;
+use Alchemy\Phrasea\Setup\Probe\LocalesProbe;
+use Alchemy\Phrasea\Setup\Probe\PhpProbe;
+use Alchemy\Phrasea\Setup\Probe\PhraseaProbe;
+use Alchemy\Phrasea\Setup\Probe\SearchEngineProbe;
+use Alchemy\Phrasea\Setup\Probe\SystemProbe;
class ConfigurationTester
{
private $app;
- private $probes;
+ private $requirements;
private $versionProbes;
+ const PROD_ENV = 'prod';
+ const DEV_ENV = 'dev';
+
public function __construct(Application $app)
{
$this->app = $app;
@@ -33,9 +44,25 @@ class ConfigurationTester
);
}
- public function registerProbe(SystemProbeInterface $probe)
+ public function getRequirements()
{
- $this->probes[] = $probe;
+ if ($this->requirements) {
+ return $this->requirements;
+ }
+
+ $this->requirements = array(
+ BinariesProbe::create($this->app),
+ CacheServerProbe::create($this->app),
+ OpcodeCacheProbe::create($this->app),
+ FilesystemProbe::create($this->app),
+ LocalesProbe::create($this->app),
+ PhpProbe::create($this->app),
+ PhraseaProbe::create($this->app),
+ SearchEngineProbe::create($this->app),
+ SystemProbe::create($this->app),
+ );
+
+ return $this->requirements;
}
public function registerVersionProbe(VersionProbeInterface $probe)
@@ -96,6 +123,7 @@ class ConfigurationTester
}
/**
+ * Returns true if a major migration script can be executed
*
* @return type
*/
diff --git a/lib/Alchemy/Phrasea/Setup/Probe/BinariesProbe.php b/lib/Alchemy/Phrasea/Setup/Probe/BinariesProbe.php
new file mode 100644
index 0000000000..2b50b822e9
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Probe/BinariesProbe.php
@@ -0,0 +1,38 @@
+ $registry->get('php_binary'),
+ 'convert_binary' => $registry->get('convert_binary'),
+ 'pdf2swf_binary' => $registry->get('pdf2swf_binary'),
+ 'unoconv_binary' => $registry->get('unoconv_binary'),
+ 'swf_extract_binary' => $registry->get('swf_extract_binary'),
+ 'swf_render_binary' => $registry->get('swf_render_binary'),
+ 'mp4box_binary' => $registry->get('mp4box_binary'),
+ 'pdftotext_binary' => $registry->get('pdftotext_binary'),
+ 'composite_binary' => $registry->get('composite_binary'),
+ 'ffmpeg_binary' => $registry->get('ffmpeg_binary'),
+ 'ffprobe_binary' => $registry->get('ffprobe_binary'),
+ )));
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return BinariesProbe
+ */
+ public static function create(Application $app)
+ {
+ return new static($app['phraseanet.registry']);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Probe/FilesystemProbe.php b/lib/Alchemy/Phrasea/Setup/Probe/FilesystemProbe.php
new file mode 100644
index 0000000000..4b1ef848af
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Probe/FilesystemProbe.php
@@ -0,0 +1,55 @@
+addRecommendation(
+ "00" === substr(sprintf('%o', fileperms($path)), -2),
+ "$path should not be readable or writeable for other users, current mode is (".substr(sprintf('%o', fileperms($path)), -4).")",
+ "Change the permissions of the \"$path\" file to 0600"
+ );
+ }
+
+ $path = array();
+
+ if ($registry->is_set('GV_base_datapath_noweb')) {
+ $paths[] = $registry->get('GV_base_datapath_noweb');
+ }
+
+ foreach ($paths as $path) {
+ $this->addRequirement(
+ is_writable($path),
+ "$path directory must be writable",
+ "Change the permissions of the \"$path\" directory so that the web server can write into it."
+ );
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return FilesystemProbe
+ */
+ public static function create(Application $app)
+ {
+ return new static($app['phraseanet.registry']);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Probe/LocalesProbe.php b/lib/Alchemy/Phrasea/Setup/Probe/LocalesProbe.php
new file mode 100644
index 0000000000..ff14859106
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Probe/LocalesProbe.php
@@ -0,0 +1,24 @@
+setName('Search Engine');
+
+ foreach ($searchEngine->getStatus() as $infos)
+ {
+ $this->addInformation($infos[0], $infos[1]);
+ }
+ }
+
+ public static function create(Application $app)
+ {
+ return new static($app['phraseanet.SE']);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Probe/SystemProbe.php b/lib/Alchemy/Phrasea/Setup/Probe/SystemProbe.php
new file mode 100644
index 0000000000..8768a9f448
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Probe/SystemProbe.php
@@ -0,0 +1,19 @@
+setName('Binaries');
+
+ $finder = new ExecutableFinder();
+
+ $phpCLI = isset($binaries['php_binary']) ? $binaries['php_binary'] : $finder->find('php');
+
+ $this->addRequirement(
+ null !== $phpCLI && is_executable($phpCLI),
+ 'PHP CLI is required to run Phraseanet task manager',
+ 'Please reinstall PHP with CLI support'
+ );
+
+ $indexer = $finder->find('phraseanet_indexer');
+
+ $this->addRecommendation(
+ null !== $indexer && is_executable($indexer),
+ 'Phraseanet Indexer is required to use Phrasea search-engine',
+ 'Please install latest Phraseanet Indexer (https://github.com/alchemy-fr/Phraseanet-Indexer)'
+ );
+
+ $convert = isset($binaries['convert_binary']) ? $binaries['convert_binary'] : $finder->find('convert');
+
+ $this->addRequirement(
+ null !== $convert && is_executable($convert),
+ 'ImageMagick Convert is required',
+ 'Please install ImageMagick'
+ );
+
+ if (null !== $convert) {
+ $output = null;
+ exec($convert . ' --version', $output);
+ $data = sscanf($output[0], 'Version: ImageMagick %d.%d.%d');
+ $version = sprintf('%d.%d.%d', $data[0], $data[1], $data[2]);
+
+ $this->addRequirement(
+ version_compare('6.2.9', $version, '<'),
+ 'Convert version 6.2.9 or higher is required ('.$version.' provided)',
+ 'Please update to a more recent version'
+ );
+ }
+
+ $composite = isset($binaries['composite_binary']) ? $binaries['composite_binary'] : $finder->find('composite');
+
+ $this->addRequirement(
+ null !== $composite && is_executable($composite),
+ 'ImageMagick Composite is required',
+ 'Please install ImageMagick'
+ );
+
+ if (null !== $composite) {
+ $output = null;
+ exec($composite . ' --version', $output);
+ $data = sscanf($output[0], 'Version: ImageMagick %d.%d.%d');
+ $version = sprintf('%d.%d.%d', $data[0], $data[1], $data[2]);
+
+ $this->addRequirement(
+ version_compare('6.2.9', $version, '<'),
+ 'Composite version 6.2.9 or higher is required ('.$version.' provided)',
+ 'Please update to a more recent version.'
+ );
+ }
+
+ $pdf2swf = isset($binaries['pdf2swf_binary']) ? $binaries['pdf2swf_binary'] : $finder->find('pdf2swf');
+
+ $this->addRecommendation(
+ null !== $pdf2swf && is_executable($pdf2swf),
+ 'SWFTools are required for documents (Word, Excel, PDF, etc...) support',
+ 'Please install SWFTools (http://www.swftools.org/)'
+ );
+
+ if (null !== $pdf2swf) {
+ $output = null;
+ exec($pdf2swf . ' --version', $output);
+ $data = sscanf($output[0], 'pdf2swf - part of swftools %d.%d.%d');
+ $version = sprintf('%d.%d.%d', $data[0], $data[1], $data[2]);
+
+ $this->addRecommendation(
+ version_compare('0.9.0', $version, '<='),
+ 'SWFTools (pdf2swf) version 0.9.0 or higher is required ('.$version.' provided)',
+ 'Please update to a more recent version.'
+ );
+ }
+
+ $unoconv = isset($binaries['unoconv_binary']) ? $binaries['unoconv_binary'] : $finder->find('unoconv');
+
+ $this->addRecommendation(
+ null !== $unoconv && is_executable($unoconv),
+ 'Unoconv is required for documents (Word, Excel, etc...) support',
+ 'Please install Unoconv'
+ );
+
+ if (null !== $unoconv) {
+ $output = null;
+ exec($unoconv . ' --version', $output);
+ $data = sscanf($output[0], 'unoconv %d.%d');
+ $version = sprintf('%d.%d', $data[0], $data[1]);
+
+ $this->addRecommendation(
+ version_compare('0.5', $version, '<='),
+ 'Unoconv version 0.5 or higher is required ('.$version.' provided)',
+ 'Please update to a more recent version.'
+ );
+ }
+
+ $swfextract = isset($binaries['swf_extract_binary']) ? $binaries['swf_extract_binary'] : $finder->find('swfextract');
+
+ $this->addRecommendation(
+ null !== $swfextract && is_executable($swfextract),
+ 'SWFTools (swfextract) are required for flash files support',
+ 'Please install SWFTools (http://www.swftools.org/)'
+ );
+
+ if (null !== $swfextract) {
+ $output = null;
+ exec($swfextract . ' --version', $output);
+ $data = sscanf($output[0], 'swfextract - part of swftools %d.%d.%d');
+ $version = sprintf('%d.%d.%d', $data[0], $data[1], $data[2]);
+
+ $this->addRecommendation(
+ version_compare('0.9.0', $version, '<='),
+ 'SWFTools (swfextract) version 0.9.0 or higher is required ('.$version.' provided)',
+ 'Please update to a more recent version.'
+ );
+ }
+
+ $swfrender = isset($binaries['swf_render_binary']) ? $binaries['swf_render_binary'] : $finder->find('swfrender');
+
+ $this->addRecommendation(
+ null !== $swfrender && is_executable($swfrender),
+ 'SWFTools (swfrender) are required for flash files support',
+ 'Please install SWFTools (http://www.swftools.org/)'
+ );
+
+ if (null !== $swfrender) {
+ $output = null;
+ exec($swfrender . ' --version', $output);
+ $data = sscanf($output[0], 'swfrender - part of swftools %d.%d.%d');
+ $version = sprintf('%d.%d.%d', $data[0], $data[1], $data[2]);
+
+ $this->addRecommendation(
+ version_compare('0.9.0', $version, '<='),
+ 'SWFTools (swfrender) version 0.9.0 or higher is required ('.$version.' provided)',
+ 'Please update to a more recent version.'
+ );
+ }
+
+ $mp4box = isset($binaries['mp4box_binary']) ? $binaries['mp4box_binary'] : $finder->find('MP4Box');
+
+ $this->addRecommendation(
+ null !== $mp4box && is_executable($mp4box),
+ 'MP4Box is required for video support',
+ 'Please install MP4Box'
+ );
+
+ if (null !== $mp4box) {
+ $output = null;
+ exec($mp4box . ' -version', $output);
+ $data = sscanf($output[0], 'MP4Box - GPAC version %d.%d.%d');
+ $version = sprintf('%d.%d.%d', $data[0], $data[1], $data[2]);
+
+ $this->addRecommendation(
+ version_compare('0.4.0', $version, '<='),
+ 'MP4Box version 0.4.0 or higher is required ('.$version.' provided)',
+ 'Please update to a more recent version.'
+ );
+ }
+
+ $pdftotext = isset($binaries['pdftotext_binary']) ? $binaries['pdftotext_binary'] : $finder->find('pdftotext');
+
+ $this->addRecommendation(
+ null !== $pdftotext && is_executable($pdftotext),
+ 'XPDF is required for PDF indexation',
+ 'Please install XPDF'
+ );
+
+ $ffmpeg = isset($binaries['ffmpeg_binary']) ? $binaries['ffmpeg_binary'] : $finder->find('ffmpeg');
+
+ if (null === $ffmpeg) {
+ $ffmpeg = $finder->find('avconv');
+ }
+
+ $this->addRecommendation(
+ null !== $ffmpeg && is_executable($ffmpeg),
+ 'FFMpeg (or libav-tools) is required for Video processing',
+ 'Please install FFMpeg (or libav-tools)'
+ );
+
+ $ffprobe = isset($binaries['ffprobe_binary']) ? $binaries['ffprobe_binary'] : $finder->find('ffprobe');
+
+ if (null === $ffprobe) {
+ $ffprobe = $finder->find('avprobe');
+ }
+
+ $this->addRecommendation(
+ null !== $ffprobe && is_executable($ffprobe),
+ 'FFProbe (or avprobe) is required for Video processing',
+ 'Please install FFProbe (or avprobe)'
+ );
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Requirements/FilesystemRequirements.php b/lib/Alchemy/Phrasea/Setup/Requirements/FilesystemRequirements.php
new file mode 100644
index 0000000000..23d9c43253
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Requirements/FilesystemRequirements.php
@@ -0,0 +1,43 @@
+setName('Filesystem');
+
+ $paths = array(
+ $baseDir . '/config',
+ $baseDir . '/config/stamp',
+ $baseDir . '/config/status',
+ $baseDir . '/config/minilogos',
+ $baseDir . '/config/templates',
+ $baseDir . '/config/topics',
+ $baseDir . '/config/wm',
+ $baseDir . '/logs',
+ $baseDir . '/tmp',
+ $baseDir . '/www/custom',
+ $baseDir . '/tmp/locks',
+ $baseDir . '/tmp/cache_twig',
+ $baseDir . '/tmp/cache_minify',
+ $baseDir . '/tmp/lazaret',
+ $baseDir . '/tmp/desc_tmp',
+ $baseDir . '/tmp/download',
+ $baseDir . '/tmp/batches'
+ );
+
+ foreach ($paths as $path) {
+ $this->addRequirement(
+ is_writable($path),
+ "$path directory must be writable",
+ "Change the permissions of the \"$path\" directory so that the web server can write into it."
+ );
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Requirements/LocalesRequirements.php b/lib/Alchemy/Phrasea/Setup/Requirements/LocalesRequirements.php
new file mode 100644
index 0000000000..ce7a024652
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Requirements/LocalesRequirements.php
@@ -0,0 +1,63 @@
+setName('Locales');
+
+ $this->addRequirement(
+ class_exists('Locale'),
+ 'intl extension should be available',
+ 'Install and enable the intl extension (used for validators).'
+ );
+
+ if (function_exists('_')) {
+ foreach (PhraseaApplication::getAvailableLanguages() as $code => $language_name){
+ \phrasea::use_i18n($code, 'test');
+
+ $this->addRecommendation(
+ 'test' === _('test::test'),
+ sprintf('Locale %s (%s) should be supported', $language_name, $code),
+ 'Install support for locale ' . $code . ' (' . $language_name . ').'
+ );
+
+ \phrasea::use_i18n($locale);
+ }
+ }
+
+ if (class_exists('Collator')) {
+ $this->addRecommendation(
+ null !== new \Collator('fr_FR'),
+ 'intl extension should be correctly configured',
+ 'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.'
+ );
+ }
+
+ if (class_exists('Locale')) {
+ if (defined('INTL_ICU_VERSION')) {
+ $version = INTL_ICU_VERSION;
+ } else {
+ $reflector = new ReflectionExtension('intl');
+
+ ob_start();
+ $reflector->info();
+ $output = strip_tags(ob_get_clean());
+
+ preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches);
+ $version = $matches[1];
+ }
+
+ $this->addRecommendation(
+ version_compare($version, '4.0', '>='),
+ 'intl ICU version should be at least 4+',
+ 'Upgrade your intl extension with a newer ICU version (4+).'
+ );
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Requirements/PhpRequirements.php b/lib/Alchemy/Phrasea/Setup/Requirements/PhpRequirements.php
new file mode 100644
index 0000000000..ef0452270a
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Requirements/PhpRequirements.php
@@ -0,0 +1,67 @@
+setName('PHP');
+
+ $this->addPhpIniRequirement(
+ 'date.timezone', true, false,
+ 'date.timezone setting must be set',
+ 'Set the "date.timezone" setting in php.ini* (like Europe/Paris).'
+ );
+
+ $this->addPhpIniRequirement('detect_unicode', false);
+
+ if (extension_loaded('suhosin')) {
+ $this->addPhpIniRequirement(
+ 'suhosin.executor.include.whitelist',
+ create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'),
+ false,
+ 'suhosin.executor.include.whitelist must be configured correctly in php.ini',
+ 'Add "phar" to suhosin.executor.include.whitelist in php.ini*.'
+ );
+ }
+
+ if (extension_loaded('xdebug')) {
+ $this->addPhpIniRequirement(
+ 'xdebug.show_exception_trace', false, true
+ );
+
+ $this->addPhpIniRequirement(
+ 'xdebug.scream', false, true
+ );
+
+ $this->addPhpIniRecommendation(
+ 'xdebug.max_nesting_level',
+ create_function('$cfgValue', 'return $cfgValue > 100;'),
+ true,
+ 'xdebug.max_nesting_level should be above 100 in php.ini',
+ 'Set "xdebug.max_nesting_level" to e.g. "250" in php.ini* to stop Xdebug\'s infinite recursion protection erroneously throwing a fatal error in your project.'
+ );
+ }
+
+ $this->addPhpIniRequirement('safe_mode', false, true);
+ $this->addPhpIniRequirement('file_uploads', true, true);
+ $this->addPhpIniRequirement('session.cache_limiter', '');
+ $this->addPhpIniRequirement('default_charset', 'UTF-8', true, 'Default charset should be UTF-8', 'Set default_charset to UTF-8 in php.ini');
+ $this->addPhpIniRequirement('magic_quotes_gpc', false, true);
+ $this->addPhpIniRequirement('magic_quotes_runtime', false, true);
+
+ $this->addPhpIniRecommendation('short_open_tag', false);
+ $this->addPhpIniRecommendation('register_globals', false, true);
+ $this->addPhpIniRecommendation('session.auto_start', false);
+ $this->addPhpIniRecommendation('display_errors', false, true);
+ $this->addPhpIniRecommendation('display_startup_errors', false, true);
+ $this->addPhpIniRecommendation('allow_url_fopen', true, true);
+ $this->addPhpIniRecommendation('session.hash_bits_per_character', '6', true, 'session.hash_bits_per_character should be at least 6', 'Set session.hash_bits_per_character to 6 in php.ini');
+ $this->addPhpIniRecommendation('session.hash_function', true, true);
+ $this->addPhpIniRecommendation('session.use_only_cookies', true, true);
+ $this->addPhpIniRecommendation('session.use_cookies', true, true);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Requirements/PhraseaRequirements.php b/lib/Alchemy/Phrasea/Setup/Requirements/PhraseaRequirements.php
new file mode 100644
index 0000000000..d2d9754d68
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Requirements/PhraseaRequirements.php
@@ -0,0 +1,34 @@
+setName('Phrasea');
+
+ $this->addRecommendation(
+ function_exists('phrasea_fetch_results'),
+ 'phrasea extension is required to use Phrasea search-engine',
+ 'Install and enable the phrasea extension to enable Phrasea search-engine (https://github.com/alchemy-fr/Phraseanet-Extension).'
+ );
+
+ if (function_exists('phrasea_fetch_results')) {
+ $infos = phrasea_info();
+
+ $this->addRequirement(
+ version_compare($infos['version'], '1.21.0.1', '>='),
+ 'phrasea extension version 1.21.0.1 is required (version ' . $infos['version'] . ' installed)',
+ 'Update phrasea extension to the latest stable (https://github.com/alchemy-fr/Phraseanet-Extension).'
+ );
+ $this->addRequirement(
+ true === $infos['temp_writable'],
+ 'phrasea extension should be able to write in its temporary directory (current is ' . $infos['temp_dir'] . ')',
+ 'Change directory ' . $infos['temp_dir'] . ' mode so phrasea extension could write to it'
+ );
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Requirements/SystemRequirements.php b/lib/Alchemy/Phrasea/Setup/Requirements/SystemRequirements.php
new file mode 100644
index 0000000000..5361248cfe
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Requirements/SystemRequirements.php
@@ -0,0 +1,331 @@
+setName('System');
+
+ $this->addRequirement(
+ version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>='),
+ sprintf('PHP version must be at least %s (%s installed)', self::REQUIRED_PHP_VERSION, $installedPhpVersion),
+ sprintf('You are running PHP version "%s", but Phraseanet needs at least PHP "%s" to run.
+ Before using Phraseanet, upgrade your PHP installation, preferably to the latest version.',
+ $installedPhpVersion, self::REQUIRED_PHP_VERSION),
+ sprintf('Install PHP %s or newer (installed version is %s)', self::REQUIRED_PHP_VERSION, $installedPhpVersion)
+ );
+
+ $this->addRequirement(
+ version_compare($installedPhpVersion, '5.3.16', '!='),
+ 'PHP version must not be 5.3.16 as Phraseanet won\'t work properly with it',
+ 'Install PHP 5.3.17 or newer (or downgrade to an earlier PHP version)'
+ );
+
+ $this->addRequirement(
+ is_dir($baseDir.'/vendor/composer'),
+ 'Vendor libraries must be installed',
+ 'Vendor libraries are missing. Install composer following instructions from http://getcomposer.org/. ' .
+ 'Then run "php composer.phar install" to install them.'
+ );
+
+ $this->addPhpIniRequirement(
+ 'date.timezone', true, false,
+ 'date.timezone setting must be set',
+ 'Set the "date.timezone" setting in php.ini* (like Europe/Paris).'
+ );
+
+ if (version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>=')) {
+ $timezones = array();
+ foreach (\DateTimeZone::listAbbreviations() as $abbreviations) {
+ foreach ($abbreviations as $abbreviation) {
+ $timezones[$abbreviation['timezone_id']] = true;
+ }
+ }
+
+ $this->addRequirement(
+ isset($timezones[date_default_timezone_get()]),
+ sprintf('Configured default timezone "%s" must be supported by your installation of PHP', date_default_timezone_get()),
+ 'Your default timezone is not supported by PHP. Check for typos in your php.ini file and have a look at the list of deprecated timezones at http://php.net/manual/en/timezones.others.php.'
+ );
+ }
+
+ $this->addRequirement(
+ function_exists('json_encode'),
+ 'json_encode() must be available',
+ 'Install and enable the JSON extension.'
+ );
+
+ $this->addRequirement(
+ function_exists('session_start'),
+ 'session_start() must be available',
+ 'Install and enable the session extension.'
+ );
+
+ $this->addRequirement(
+ function_exists('ctype_alpha'),
+ 'ctype_alpha() must be available',
+ 'Install and enable the ctype extension.'
+ );
+
+ $this->addRequirement(
+ function_exists('token_get_all'),
+ 'token_get_all() must be available',
+ 'Install and enable the Tokenizer extension.'
+ );
+
+ $this->addRequirement(
+ function_exists('simplexml_import_dom'),
+ 'simplexml_import_dom() must be available',
+ 'Install and enable the SimpleXML extension.'
+ );
+
+ if (function_exists('apc_store') && ini_get('apc.enabled')) {
+ if (version_compare($installedPhpVersion, '5.4.0', '>=')) {
+ $this->addRequirement(
+ version_compare(phpversion('apc'), '3.1.13', '>='),
+ 'APC version must be at least 3.1.13 when using PHP 5.4',
+ 'Upgrade your APC extension (3.1.13+).'
+ );
+ } else {
+ $this->addRequirement(
+ version_compare(phpversion('apc'), '3.0.17', '>='),
+ 'APC version must be at least 3.0.17',
+ 'Upgrade your APC extension (3.0.17+).'
+ );
+ }
+ }
+
+ $this->addPhpIniRequirement('detect_unicode', false);
+
+ if (extension_loaded('suhosin')) {
+ $this->addPhpIniRequirement(
+ 'suhosin.executor.include.whitelist',
+ create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'),
+ false,
+ 'suhosin.executor.include.whitelist must be configured correctly in php.ini',
+ 'Add "phar" to suhosin.executor.include.whitelist in php.ini*.'
+ );
+ }
+
+ if (extension_loaded('xdebug')) {
+ $this->addPhpIniRequirement(
+ 'xdebug.show_exception_trace', false, true
+ );
+
+ $this->addPhpIniRequirement(
+ 'xdebug.scream', false, true
+ );
+
+ $this->addPhpIniRecommendation(
+ 'xdebug.max_nesting_level',
+ create_function('$cfgValue', 'return $cfgValue > 100;'),
+ true,
+ 'xdebug.max_nesting_level should be above 100 in php.ini',
+ 'Set "xdebug.max_nesting_level" to e.g. "250" in php.ini* to stop Xdebug\'s infinite recursion protection erroneously throwing a fatal error in your project.'
+ );
+ }
+
+ $pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null;
+
+ $this->addRequirement(
+ null !== $pcreVersion,
+ 'PCRE extension must be available',
+ 'Install the PCRE extension (version 8.0+).'
+ );
+
+ $this->addRecommendation(
+ version_compare($installedPhpVersion, '5.3.4', '>='),
+ 'You should use at least PHP 5.3.4 due to PHP bug #52083 in earlier versions',
+ 'Your project might malfunction randomly due to PHP bug #52083 ("Notice: Trying to get property of non-object"). Install PHP 5.3.4 or newer.'
+ );
+
+ $this->addRecommendation(
+ version_compare($installedPhpVersion, '5.3.8', '>='),
+ 'When using annotations you should have at least PHP 5.3.8 due to PHP bug #55156',
+ 'Install PHP 5.3.8 or newer if your project uses annotations.'
+ );
+
+ $this->addRecommendation(
+ version_compare($installedPhpVersion, '5.4.0', '!='),
+ 'You should not use PHP 5.4.0 due to the PHP bug #61453',
+ 'Your project might not work properly due to the PHP bug #61453 ("Cannot dump definitions which have method calls"). Install PHP 5.4.1 or newer.'
+ );
+
+ if (null !== $pcreVersion) {
+ $this->addRecommendation(
+ $pcreVersion >= 8.0,
+ sprintf('PCRE extension should be at least version 8.0 (%s installed)', $pcreVersion),
+ 'PCRE 8.0+ is preconfigured in PHP since 5.3.2 but you are using an outdated version of it. Phraseanet probably works anyway but it is recommended to upgrade your PCRE extension.'
+ );
+ }
+
+ $this->addRequirement(
+ class_exists('DomDocument'),
+ 'PHP-XML module should be installed',
+ 'Install and enable the PHP-XML module.'
+ );
+
+ $this->addRequirement(
+ function_exists('mb_strlen'),
+ 'mb_strlen() should be available',
+ 'Install and enable the mbstring extension.'
+ );
+
+ $this->addRequirement(
+ function_exists('iconv'),
+ 'iconv() should be available',
+ 'Install and enable the iconv extension.'
+ );
+
+ $this->addRequirement(
+ function_exists('exif_read_data'),
+ 'exif extension is required',
+ 'Install and enable the exif extension to enable FTP exports.'
+ );
+
+ $this->addRequirement(
+ function_exists('curl_init'),
+ 'curl extension is required',
+ 'Install and enable the curl extension.'
+ );
+
+ $this->addRequirement(
+ function_exists('gd_info'),
+ 'gd extension is required',
+ 'Install and enable the gd extension.'
+ );
+
+ $this->addRequirement(
+ function_exists('hash_hmac'),
+ 'hash extension is required',
+ 'Install and enable the hash extension.'
+ );
+
+ if ('cli' === php_sapi_name() && !defined('PHP_WINDOWS_VERSION_BUILD')) {
+ $this->addRecommendation(
+ function_exists('pcntl_fork'),
+ 'pcntl extension is recommended in unix environments',
+ 'Install and enable the pcntl extension to enable process fork.'
+ );
+ }
+
+ $this->addRequirement(
+ function_exists('proc_open'),
+ 'proc_* functions are required',
+ 'Enable the proc_c* functions.'
+ );
+
+ if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
+ $this->addRecommendation(
+ function_exists('posix_uname'),
+ 'Posix extension is recommended for task manager',
+ 'Install and enable the posix extension to enable process fork.'
+ );
+ }
+
+ $this->addRequirement(
+ function_exists('socket_connect'),
+ 'Socket extension is required for task manager',
+ 'Install and enable the socket extension.'
+ );
+
+ $this->addRequirement(
+ class_exists('ZipArchive'),
+ 'Zip extension is required for download',
+ 'Install and enable the zip extension.'
+ );
+
+ $this->addRecommendation(
+ extension_loaded('twig'),
+ 'Twig extension is strongly recommended in production',
+ 'Install and enable the twig extension.'
+ );
+
+ $this->addRecommendation(
+ class_exists('Imagick') || class_exists('Gmagick'),
+ 'Imagick or Gmagick extension is strongly recommended for image processing',
+ 'Install and enable the gmagick or imagick extension.'
+ );
+
+ $this->addRecommendation(
+ class_exists('Gmagick'),
+ 'Gmagick extension is required for video processing (animated thumbnail generation)',
+ 'Install and enable the gmagick extension.'
+ );
+
+ $this->addRecommendation(
+ function_exists('finfo_open'),
+ 'Fileinfo extension is recommended',
+ 'Install and enable the fileinfo extension to enable file detection.'
+ );
+
+ $this->addRequirement(
+ function_exists('utf8_decode'),
+ 'utf8_decode() should be available',
+ 'Install and enable the XML extension.'
+ );
+
+
+ if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
+ $this->addRecommendation(
+ function_exists('posix_isatty'),
+ 'posix_isatty() should be available',
+ 'Install and enable the php_posix extension (used to colorize the CLI output).'
+ );
+ }
+
+ $this->addRecommendation(
+ function_exists('ftp_fget'),
+ 'ftp extension is required for FTP export',
+ 'Install and enable the ftp extension to enable FTP exports.'
+ );
+
+ $accelerator =
+ (function_exists('apc_store') && ini_get('apc.enabled'))
+ ||
+ function_exists('eaccelerator_put') && ini_get('eaccelerator.enable')
+ ||
+ function_exists('xcache_set')
+ ;
+
+ $this->addRecommendation(
+ $accelerator,
+ 'a PHP accelerator should be installed',
+ 'Install and enable a PHP accelerator like APC (highly recommended).'
+ );
+
+ $this->addPhpIniRecommendation('short_open_tag', false);
+
+ $this->addPhpIniRecommendation('magic_quotes_gpc', false, true);
+
+ $this->addPhpIniRecommendation('register_globals', false, true);
+
+ $this->addPhpIniRecommendation('session.auto_start', false);
+
+ $this->addRequirement(
+ class_exists('PDO'),
+ 'PDO should be installed',
+ 'Install PDO (mandatory for Doctrine).'
+ );
+
+ if (class_exists('PDO')) {
+ $drivers = \PDO::getAvailableDrivers();
+ $this->addRequirement(
+ in_array('mysql', $drivers),
+ sprintf('PDO should have MySQL driver installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'),
+ 'Install PDO MySQL driver.'
+ );
+ }
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/System/Information.php b/lib/Alchemy/Phrasea/Setup/System/Information.php
new file mode 100644
index 0000000000..901a82b9bf
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/System/Information.php
@@ -0,0 +1,25 @@
+name = $name;
+ $this->value = $value;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function getValue()
+ {
+ return $this->value;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/System/InformationInterface.php b/lib/Alchemy/Phrasea/Setup/System/InformationInterface.php
new file mode 100644
index 0000000000..46b27561dd
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/System/InformationInterface.php
@@ -0,0 +1,9 @@
+
+ */
+class PhpIniRequirement extends Requirement
+{
+ /**
+ * Constructor that initializes the requirement.
+ *
+ * @param string $cfgName The configuration name used for ini_get()
+ * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false,
+ or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
+ * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
+ This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
+ Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
+ * @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived)
+ * @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a Boolean a default help is derived)
+ * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
+ * @param Boolean $optional Whether this is only an optional recommendation not a mandatory requirement
+ */
+ public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false)
+ {
+ $cfgValue = ini_get($cfgName);
+
+ if (is_callable($evaluation)) {
+ if (null === $testMessage || null === $helpHtml) {
+ throw new InvalidArgumentException('You must provide the parameters testMessage and helpHtml for a callback evaluation.');
+ }
+
+ $fulfilled = call_user_func($evaluation, $cfgValue);
+ } else {
+ if (null === $testMessage) {
+ $testMessage = sprintf('%s %s be %s in php.ini',
+ $cfgName,
+ $optional ? 'should' : 'must',
+ $evaluation ? 'enabled' : 'disabled'
+ );
+ }
+
+ if (null === $helpHtml) {
+ $helpHtml = sprintf('Set %s to %s in php.ini*.',
+ $cfgName,
+ $evaluation ? 'on' : 'off'
+ );
+ }
+
+ $fulfilled = $evaluation == $cfgValue;
+ }
+
+ parent::__construct($fulfilled || ($approveCfgAbsence && false === $cfgValue), $testMessage, $helpHtml, $helpText, $optional);
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/System/ProbeInterface.php b/lib/Alchemy/Phrasea/Setup/System/ProbeInterface.php
deleted file mode 100644
index 5d89c27e92..0000000000
--- a/lib/Alchemy/Phrasea/Setup/System/ProbeInterface.php
+++ /dev/null
@@ -1,16 +0,0 @@
-
+ */
+class Requirement implements RequirementInterface
+{
+ private $fulfilled;
+ private $testMessage;
+ private $helpText;
+ private $helpHtml;
+ private $optional;
+
+ /**
+ * Constructor that initializes the requirement.
+ *
+ * @param Boolean $fulfilled Whether the requirement is fulfilled
+ * @param string $testMessage The message for testing the requirement
+ * @param string $helpHtml The help text formatted in HTML for resolving the problem
+ * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
+ * @param Boolean $optional Whether this is only an optional recommendation not a mandatory requirement
+ */
+ public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false)
+ {
+ $this->fulfilled = (Boolean) $fulfilled;
+ $this->testMessage = (string) $testMessage;
+ $this->helpHtml = (string) $helpHtml;
+ $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText;
+ $this->optional = (Boolean) $optional;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isFulfilled()
+ {
+ return $this->fulfilled;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTestMessage()
+ {
+ return $this->testMessage;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getHelpText()
+ {
+ return $this->helpText;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getHelpHtml()
+ {
+ return $this->helpHtml;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isOptional()
+ {
+ return $this->optional;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/System/RequirementCollection.php b/lib/Alchemy/Phrasea/Setup/System/RequirementCollection.php
new file mode 100644
index 0000000000..e771c2d5b3
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/System/RequirementCollection.php
@@ -0,0 +1,193 @@
+
+ */
+class RequirementCollection implements RequirementCollectionInterface
+{
+ private $requirements = array();
+ private $informations = array();
+ private $name;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ public function addInformation($name, $value)
+ {
+ $this->informations[] = new Information($name, $value);
+
+ return $this;
+ }
+
+ public function getInformations()
+ {
+ return $this->informations;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->requirements);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add(Requirement $requirement)
+ {
+ $this->requirements[] = $requirement;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null)
+ {
+ $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, false));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null)
+ {
+ $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, true));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null)
+ {
+ $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, false));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null)
+ {
+ $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, true));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addCollection(RequirementCollection $collection)
+ {
+ $this->requirements = array_merge($this->requirements, $collection->all());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ return $this->requirements;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getRequirements()
+ {
+ $array = array();
+ foreach ($this->requirements as $req) {
+ if (!$req->isOptional()) {
+ $array[] = $req;
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFailedRequirements()
+ {
+ $array = array();
+ foreach ($this->requirements as $req) {
+ if (!$req->isFulfilled() && !$req->isOptional()) {
+ $array[] = $req;
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getRecommendations()
+ {
+ $array = array();
+ foreach ($this->requirements as $req) {
+ if ($req->isOptional()) {
+ $array[] = $req;
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFailedRecommendations()
+ {
+ $array = array();
+ foreach ($this->requirements as $req) {
+ if (!$req->isFulfilled() && $req->isOptional()) {
+ $array[] = $req;
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasPhpIniConfigIssue()
+ {
+ foreach ($this->requirements as $req) {
+ if (!$req->isFulfilled() && $req instanceof PhpIniRequirement) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPhpIniConfigPath()
+ {
+ return get_cfg_var('cfg_file_path');
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/System/RequirementCollectionInterface.php b/lib/Alchemy/Phrasea/Setup/System/RequirementCollectionInterface.php
new file mode 100644
index 0000000000..19ef288f08
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/System/RequirementCollectionInterface.php
@@ -0,0 +1,146 @@
+
+ */
+interface RequirementCollectionInterface extends \IteratorAggregate
+{
+ /**
+ * Get the name of the requirement collection
+ *
+ * @return String
+ */
+ public function getName();
+
+ /**
+ * Set a name to the requirement collection
+ *
+ * @param String $name
+ *
+ * @return RequirementCollectionInterface
+ */
+ public function setName($name);
+
+ public function addInformation($name, $value);
+
+ public function getInformations();
+
+ /**
+ * Adds a Requirement.
+ *
+ * @param Requirement $requirement A Requirement instance
+ */
+ public function add(Requirement $requirement);
+
+ /**
+ * Adds a mandatory requirement.
+ *
+ * @param Boolean $fulfilled Whether the requirement is fulfilled
+ * @param string $testMessage The message for testing the requirement
+ * @param string $helpHtml The help text formatted in HTML for resolving the problem
+ * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
+ */
+ public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null);
+
+ /**
+ * Adds an optional recommendation.
+ *
+ * @param Boolean $fulfilled Whether the recommendation is fulfilled
+ * @param string $testMessage The message for testing the recommendation
+ * @param string $helpHtml The help text formatted in HTML for resolving the problem
+ * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
+ */
+ public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null);
+
+ /**
+ * Adds a mandatory requirement in form of a php.ini configuration.
+ *
+ * @param string $cfgName The configuration name used for ini_get()
+ * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false,
+ or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
+ * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
+ This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
+ Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
+ * @param string $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived)
+ * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a Boolean a default help is derived)
+ * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
+ */
+ public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null);
+
+ /**
+ * Adds an optional recommendation in form of a php.ini configuration.
+ *
+ * @param string $cfgName The configuration name used for ini_get()
+ * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false,
+ or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
+ * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
+ This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
+ Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
+ * @param string $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived)
+ * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a Boolean a default help is derived)
+ * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
+ */
+ public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null);
+
+ /**
+ * Adds a requirement collection to the current set of requirements.
+ *
+ * @param RequirementCollection $collection A RequirementCollection instance
+ */
+ public function addCollection(RequirementCollection $collection);
+
+ /**
+ * Returns both requirements and recommendations.
+ *
+ * @return array Array of Requirement instances
+ */
+ public function all();
+
+ /**
+ * Returns all mandatory requirements.
+ *
+ * @return array Array of Requirement instances
+ */
+ public function getRequirements();
+
+ /**
+ * Returns the mandatory requirements that were not met.
+ *
+ * @return array Array of Requirement instances
+ */
+ public function getFailedRequirements();
+
+ /**
+ * Returns all optional recommmendations.
+ *
+ * @return array Array of Requirement instances
+ */
+ public function getRecommendations();
+
+ /**
+ * Returns the recommendations that were not met.
+ *
+ * @return array Array of Requirement instances
+ */
+ public function getFailedRecommendations();
+
+ /**
+ * Returns whether a php.ini configuration is not correct.
+ *
+ * @return Boolean php.ini configuration problem?
+ */
+ public function hasPhpIniConfigIssue();
+
+ /**
+ * Returns the PHP configuration file (php.ini) path.
+ *
+ * @return string|false php.ini file path
+ */
+ public function getPhpIniConfigPath();
+}
diff --git a/lib/Alchemy/Phrasea/Setup/System/RequirementInterface.php b/lib/Alchemy/Phrasea/Setup/System/RequirementInterface.php
new file mode 100644
index 0000000000..35a66d7765
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/System/RequirementInterface.php
@@ -0,0 +1,50 @@
+app = $app;
}
+ /**
+ * {@inheritdoc}
+ */
public function isMigrable()
{
/**
@@ -33,6 +36,9 @@ class Probe31 implements ProbeInterface
&& is_file(__DIR__ . "/../../../../../../config/_GV.php");
}
+ /**
+ * {@inheritdoc}
+ */
public function getMigration()
{
return new Migration31($this->app);
diff --git a/lib/Alchemy/Phrasea/Setup/Version/Probe/Probe35.php b/lib/Alchemy/Phrasea/Setup/Version/Probe/Probe35.php
index 50b1a7ec03..363d6c1dca 100644
--- a/lib/Alchemy/Phrasea/Setup/Version/Probe/Probe35.php
+++ b/lib/Alchemy/Phrasea/Setup/Version/Probe/Probe35.php
@@ -23,6 +23,9 @@ class Probe35 implements ProbeInterface
$this->app = $app;
}
+ /**
+ * {@inheritdoc}
+ */
public function isMigrable()
{
/**
@@ -33,6 +36,9 @@ class Probe35 implements ProbeInterface
&& is_file(__DIR__ . "/../../../../../../config/config.inc");
}
+ /**
+ * {@inheritdoc}
+ */
public function getMigration()
{
return new Migration35($this->app);
diff --git a/lib/Alchemy/Phrasea/Setup/Version/Probe/ProbeInterface.php b/lib/Alchemy/Phrasea/Setup/Version/Probe/ProbeInterface.php
index 5a4478a407..fb706c95c1 100644
--- a/lib/Alchemy/Phrasea/Setup/Version/Probe/ProbeInterface.php
+++ b/lib/Alchemy/Phrasea/Setup/Version/Probe/ProbeInterface.php
@@ -11,9 +11,17 @@
namespace Alchemy\Phrasea\Setup\Version\Probe;
+use Alchemy\Phrasea\Setup\Version\Migration\MigrationInterface;
+
interface ProbeInterface
{
+ /**
+ * @return Boolean
+ */
public function isMigrable();
+ /**
+ * @return MigrationInterface
+ */
public function getMigration();
}
diff --git a/lib/classes/User/Adapter.php b/lib/classes/User/Adapter.php
index 9a5350dcf8..66b294e5c7 100644
--- a/lib/classes/User/Adapter.php
+++ b/lib/classes/User/Adapter.php
@@ -362,7 +362,7 @@ class User_Adapter implements User_Interface, cache_cacheableInterface
if (!isset(self::$_instance[$id])) {
try {
self::$_instance[$id] = $app['phraseanet.appbox']->get_data_from_cache('_user_' . $id);
- self::$_instance[$id]->set_app($app['phraseanet.appbox']);
+ self::$_instance[$id]->set_app($app);
} catch (Exception $e) {
self::$_instance[$id] = new self($id, $app);
$app['phraseanet.appbox']->set_data_to_cache(self::$_instance[$id], '_user_' . $id);
@@ -1727,7 +1727,7 @@ class User_Adapter implements User_Interface, cache_cacheableInterface
{
$vars = array();
foreach ($this as $key => $value) {
- if (in_array($key, array('ACL', 'appbox')))
+ if (in_array($key, array('ACL', 'app')))
continue;
$vars[] = $key;
}
diff --git a/lib/classes/module/console/systemConfigCheck.php b/lib/classes/module/console/systemConfigCheck.php
deleted file mode 100644
index a5083febef..0000000000
--- a/lib/classes/module/console/systemConfigCheck.php
+++ /dev/null
@@ -1,109 +0,0 @@
-setDescription('Check the configuration');
-
- return $this;
- }
-
- protected function doExecute(InputInterface $input, OutputInterface $output)
- {
- if ( ! function_exists('_')) {
- $output->writeln('YOU MUST ENABLE GETTEXT SUPPORT TO USE PHRASEANET');
- $output->writeln('Canceled');
-
- return 1;
- }
-
- $ok = true;
-
- if ($this->container['phraseanet.configuration-tester']->isInstalled()) {
- $registry = $this->container['phraseanet.registry'];
-
- $output->writeln(_('*** CHECK BINARY CONFIGURATION ***'));
- $ok = $this->processConstraints(setup::check_binaries($this->container['phraseanet.registry']), $output) && $ok;
- $output->writeln("");
- } else {
- $registry = new Setup_Registry();
- }
-
- $output->writeln(_('*** FILESYSTEM CONFIGURATION ***'));
- $ok = $this->processConstraints(setup::check_writability($registry), $output) && $ok;
- $output->writeln("");
- $output->writeln(_('*** CHECK CACHE OPCODE ***'));
- $ok = $this->processConstraints(setup::check_cache_opcode(), $output) && $ok;
- $output->writeln("");
- $output->writeln(_('*** CHECK CACHE SERVER ***'));
- $ok = $this->processConstraints(setup::check_cache_server(), $output) && $ok;
- $output->writeln("");
- $output->writeln(_('*** CHECK PHP CONFIGURATION ***'));
- $ok = $this->processConstraints(setup::check_php_configuration(), $output) && $ok;
- $output->writeln("");
- $output->writeln(_('*** CHECK PHP EXTENSIONS ***'));
- $ok = $this->processConstraints(setup::check_php_extension(), $output) && $ok;
- $output->writeln("");
- $output->writeln(_('*** CHECK PHRASEA ***'));
- $ok = $this->processConstraints(setup::check_phrasea(), $output) && $ok;
- $output->writeln("");
- $output->writeln(_('*** CHECK SYSTEM LOCALES ***'));
- $ok = $this->processConstraints(setup::check_system_locales($this->container), $output) && $ok;
- $output->writeln("");
-
- $output->write('Finished !', true);
-
- return (int) ! $ok;
- }
-
- protected function processConstraints(Setup_ConstraintsIterator $constraints, OutputInterface $output)
- {
- $hasError = false;
- foreach ($constraints as $constraint) {
- if ( ! $this->processConstraint($constraint, $output)) {
- $hasError = true;
- }
- }
-
- return ! $hasError;
- }
-
- protected function processConstraint(Setup_Constraint $constraint, OutputInterface $output)
- {
- $ok = true;
- if ($constraint->is_ok()) {
- $output->writeln("\t\t" . $constraint->get_message() . '');
- } elseif ($constraint->is_blocker()) {
- $output->writeln("\t!!!\t" . $constraint->get_message() . '');
- $ok = false;
- } else {
- $output->writeln("\t/!\\\t" . $constraint->get_message() . '');
- }
-
- return $ok;
- }
-}
diff --git a/templates/web/admin/dashboard.html.twig b/templates/web/admin/dashboard.html.twig
index 4687baa52b..de8efb6e47 100644
--- a/templates/web/admin/dashboard.html.twig
+++ b/templates/web/admin/dashboard.html.twig
@@ -1,11 +1,44 @@
-{% macro board_sub_section(sub_section_title, constraints_type) %}
-
{{ sub_section_title }}
+{% macro board_sub_section(requirements) %}
+ {{ requirements.getName() }}
{% endmacro %}
@@ -90,7 +123,7 @@
{% trans 'setup:: Reglages generaux' %}
{% trans 'setup::Votre configuration' %}
-
+
{% trans 'setup::Tests d\'envois d\'emails' %}
- {% for constraint in php_version_constraints %}
-
{{ constraint.get_name() }}
-
- -
- {{ constraint.get_message() }}
-
-
- {% endfor %}
-
- {% set sub_section_title %}
- {% trans 'setup::Filesystem configuration' %}
- {% endset %}
- {{ _self.board_sub_section(sub_section_title, writability_constraints) }}
-
- {% set sub_section_title %}
- {% trans 'setup::Executables' %}
- {% endset %}
- {{ _self.board_sub_section(sub_section_title, binaries_constraints) }}
-
- {% set sub_section_title %}
- {% trans 'setup::PHP extensions' %}
- {% endset %}
- {{ _self.board_sub_section(sub_section_title, php_extension_constraints) }}
-
- {% set sub_section_title %}
- {% trans 'setup::Serveur de cache' %}
- {% endset %}
- {{ _self.board_sub_section(sub_section_title, cache_constraints) }}
-
-
-
-
-
- {% set sub_section_title %}
- {% trans 'Phrasea Module' %}
- {% endset %}
- {{ _self.board_sub_section(sub_section_title, phrasea_constraints) }}
-
- {% set sub_section_title %}
- {% trans 'setup::Serveur de cache' %}
- {% endset %}
- {{ _self.board_sub_section(sub_section_title, cache_opcode_constraints) }}
-
-
{% trans 'setup:: Serveur Memcached' %}
-
- {% if app['cache'].isServer() %}
- {% set stats = app['cache'].getStats() %}
- - {% trans 'setup::Serveur actif sur %s' %} {{app['phraseanet.registry'].get('GV_cache_server_host')}} : {{app['phraseanet.registry'].get('GV_cache_server_port')}}
-
- {% for name, stat in stats%}
- {{ name }} | {{ stat }} |
- {% endfor %}
-
- {% else %}
- - {% trans 'setup::Aucun serveur memcached rattache.' %}
- {% endif %}
-
{% if app['cache'].isServer() %}
{% endif %}
-
{% trans 'OPCode cache' %}
-
- {% if app['opcode-cache'].getName() == 'array' %}
- - {% trans 'Array opcode cache is activated, but phrasea strongly recommand the use of APC or Xcache in production' %}
- {% else %}
- - {{ app['opcode-cache'].getName() }}
- {% endif %}
-
-
- {% if search_engine_status %}
-
{% trans 'setup::Etat du moteur de recherche' %}
-
- {% for value in search_engine_status %}
- - {{ value[0] }} : {{ value[1] }}
- {% endfor %}
-
- {% else %}
-
{% trans 'setup::Sphinx confguration' %}
-
- - {% trans 'Search Engine not available' %}
-
- {% endif %}
-
- {% set sub_section_title %}
- {% trans 'PHP Configuration' %}
- {% endset %}
- {{ _self.board_sub_section(sub_section_title, php_configuration_constraints) }}
-
+ {% for requirements in app['phraseanet.configuration-tester'].getRequirements() %}
+ {{ _self.board_sub_section(requirements) }}
+ {% endfor %}
diff --git a/www/skins/admin/css/Main.css b/www/skins/admin/css/Main.css
index 98454063cd..52ab03bccc 100644
--- a/www/skins/admin/css/Main.css
+++ b/www/skins/admin/css/Main.css
@@ -212,43 +212,36 @@ div.switch_right.unchecked {
.board_section #mail_checker {
margin: 0 0 20px 15px;
}
-.board_section div[class^="section_"] {
- width: 400px;
+.board_section div[class="section"] {
height: auto;
}
-.board_section div[class^="section_"] h2 {
+.board_section div[class="section"] h2 {
line-height: 24px;
}
-.board_section div[class^="section_"] ul.setup {
- width: 360px;
+.board_section div[class="section"] ul.setup {
border: 1px solid #cccccc;
list-style-type: none;
}
-.board_section div[class^="section_"] ul.setup li {
+.board_section div[class="section"] ul.setup li {
padding: 2px 5px 2px 30px;
- background-image: url(/skins/icons/ok.png);
background-repeat: no-repeat;
background-position: 5px center;
}
-.board_section div[class^="section_"] ul.setup li.non-blocker {
+.board_section div[class="section"] ul.setup li.good-enough {
+ background-image: url(/skins/icons/ok.png);
+}
+.board_section div[class="section"] ul.setup li.non-blocker {
background-image: url(/skins/icons/alert.png);
}
-.board_section div[class^="section_"] ul.setup li.blocker {
+.board_section div[class="section"] ul.setup li.blocker {
background-image: url(/skins/icons/delete.png);
}
-.board_section div[class^="section_"] ul.setup li:hover {
+.board_section div[class="section"] ul.setup li:hover {
background-color: #fffbcd;
}
-.board_section div[class^="section_"] #cache_flusher {
+.board_section div[class="section"] #cache_flusher {
margin: -10px 0 20px 0;
}
-.board_section .section_left {
- float: left;
-}
-.board_section .section_right {
- margin-left: 430px;
- padding-top: 1px;
-}
.board_section #flush_button {
width: 362px;
margin-left: 15px;
diff --git a/www/skins/admin/less/Main.less b/www/skins/admin/less/Main.less
index fe29a9f365..280a728b31 100644
--- a/www/skins/admin/less/Main.less
+++ b/www/skins/admin/less/Main.less
@@ -254,21 +254,21 @@ div.switch_right {
ul, #mail_checker {
margin: 0 0 20px 15px;
}
- div[class^="section_"] {
- width: 400px;
+ div[class="section"] {
height: auto;
h2 {
line-height: 24px;
}
ul.setup {
- width: 360px;
border: 1px solid @grey;
list-style-type: none;
li {
padding: 2px 5px 2px 30px;
- background-image: url(/skins/icons/ok.png);
background-repeat: no-repeat;
background-position: 5px center;
+ &.good-enough {
+ background-image: url(/skins/icons/ok.png);
+ }
&.non-blocker {
background-image: url(/skins/icons/alert.png);
}
@@ -284,13 +284,6 @@ div.switch_right {
margin: -10px 0 20px 0;
}
}
- .section_left {
- float: left;
- }
- .section_right {
- margin-left: 430px;
- padding-top: 1px;
- }
#flush_button {
width: 362px;
margin-left: 15px;