diff --git a/config/global.ini.php b/config/global.ini.php index 0ac565592471cc2f5b21e72c67dfb686e67f9bc3..526f8b3e243bb7de1d6cba7d4adeef141f08f6b0 100644 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -720,6 +720,7 @@ password = ; Proxy password: optional; if specified, username is mandatory Plugins[] = CorePluginsAdmin Plugins[] = CoreAdminHome Plugins[] = CoreHome +Plugins[] = Diagnostics Plugins[] = CoreVisualizations Plugins[] = Proxy Plugins[] = API diff --git a/core/CliMulti/CliPhp.php b/core/CliMulti/CliPhp.php index 9ee5b191b1b7c50af37bcfb852d795c9b77cd1d1..3bf7b3888e839eb76962548244ab94fee157bbee 100644 --- a/core/CliMulti/CliPhp.php +++ b/core/CliMulti/CliPhp.php @@ -9,7 +9,6 @@ namespace Piwik\CliMulti; use Piwik\CliMulti; use Piwik\Common; -use Piwik\Plugins\Installation\SystemCheck; class CliPhp { @@ -67,8 +66,9 @@ class CliPhp private function isValidPhpVersion($bin) { + global $piwik_minimumPHPVersion; $cliVersion = $this->getPhpVersion($bin); - $isCliVersionValid = SystemCheck::isPhpVersionValid($cliVersion); + $isCliVersionValid = version_compare($piwik_minimumPHPVersion, $cliVersion) <= 0; return $isCliVersionValid; } diff --git a/core/Http.php b/core/Http.php index ffd0bf438304a74ba6b67acb8742eb29913879a7..82809c7fdb72c409a9c231ed8ec0d8905865e6d2 100644 --- a/core/Http.php +++ b/core/Http.php @@ -22,7 +22,7 @@ class Http /** * Returns the "best" available transport method for {@link sendHttpRequest()} calls. * - * @return string Either `'curl'`, `'fopen'` or `'socket'`. + * @return string|null Either curl, fopen, socket or null if no method is supported. * @api */ public static function getTransportMethod() diff --git a/core/Updates/2.13.0-b3.php b/core/Updates/2.13.0-b3.php new file mode 100644 index 0000000000000000000000000000000000000000..db519eea45719c5d49348d72671849b89fce5993 --- /dev/null +++ b/core/Updates/2.13.0-b3.php @@ -0,0 +1,26 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Updates; + +use Piwik\Config; +use Piwik\Updates; + +class Updates_2_13_0_b3 extends Updates +{ + public function doUpdate() + { + $pluginManager = \Piwik\Plugin\Manager::getInstance(); + + try { + $pluginManager->activatePlugin('Diagnostics'); + } catch(\Exception $e) { + } + } +} diff --git a/core/Version.php b/core/Version.php index b5921a9617b514bcf6214db7a9f819286226fc5f..1b63ba15cf88f9e72fc8d983a1e0959f598dba61 100644 --- a/core/Version.php +++ b/core/Version.php @@ -20,7 +20,7 @@ final class Version * The current Piwik version. * @var string */ - const VERSION = '2.13.0-b2'; + const VERSION = '2.13.0-b3'; public function isStableVersion($version) { diff --git a/plugins/CoreUpdater/Diagnostic/HttpsUpdateCheck.php b/plugins/CoreUpdater/Diagnostic/HttpsUpdateCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..f07e00449930a23428eb53c35cd82971640bd461 --- /dev/null +++ b/plugins/CoreUpdater/Diagnostic/HttpsUpdateCheck.php @@ -0,0 +1,38 @@ +<?php + +namespace Piwik\Plugins\CoreUpdater\Diagnostic; + +use Piwik\Config; +use Piwik\Plugins\CoreUpdater; +use Piwik\Plugins\Diagnostics\Diagnostic\Diagnostic; +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; +use Piwik\Translation\Translator; + +/** + * Check the HTTPS update. + */ +class HttpsUpdateCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckUpdateHttps'); + + if (CoreUpdater\Controller::isUpdatingOverHttps()) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK)); + } + + $comment = $this->translator->translate('Installation_SystemCheckUpdateHttpsNotSupported'); + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment)); + } +} diff --git a/plugins/CoreUpdater/config/config.php b/plugins/CoreUpdater/config/config.php index 0a69ab1b9dd737d9deced49d78e7e1f17a2122a6..d058a1ed4d98ec974861651d811dfc0611ae6fe7 100644 --- a/plugins/CoreUpdater/config/config.php +++ b/plugins/CoreUpdater/config/config.php @@ -3,4 +3,8 @@ return array( 'Piwik\Plugins\CoreUpdater\Updater' => DI\object() ->constructorParameter('tmpPath', DI\get('path.tmp')), + + 'diagnostics.optional' => DI\add(array( + DI\link('Piwik\Plugins\CoreUpdater\Diagnostic\HttpsUpdateCheck'), + )), ); diff --git a/plugins/Diagnostics/Commands/Run.php b/plugins/Diagnostics/Commands/Run.php new file mode 100644 index 0000000000000000000000000000000000000000..8014ac24ccdf9ace485fb58f7652126821dd1531 --- /dev/null +++ b/plugins/Diagnostics/Commands/Run.php @@ -0,0 +1,98 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Diagnostics\Commands; + +use Piwik\Container\StaticContainer; +use Piwik\Plugin\ConsoleCommand; +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResultItem; +use Piwik\Plugins\Diagnostics\DiagnosticService; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Run the diagnostics. + */ +class Run extends ConsoleCommand +{ + /** + * @var DiagnosticService + */ + private $diagnosticService; + + public function __construct() + { + // Replace this with dependency injection once available + $this->diagnosticService = StaticContainer::get('Piwik\Plugins\Diagnostics\DiagnosticService'); + + parent::__construct(); + } + + protected function configure() + { + $this->setName('diagnostics:run') + ->setDescription('Run diagnostics to check that Piwik is installed and runs correctly') + ->addOption('all', null, InputOption::VALUE_NONE, 'Show all diagnostics, including those that passed with success'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $showAll = $input->getOption('all'); + + $report = $this->diagnosticService->runDiagnostics(); + + foreach ($report->getAllResults() as $result) { + $items = $result->getItems(); + + if (! $showAll && ($result->getStatus() === DiagnosticResult::STATUS_OK)) { + continue; + } + + if (count($items) === 1) { + $output->writeln($result->getLabel() . ': ' . $this->formatItem($items[0]), OutputInterface::OUTPUT_PLAIN); + continue; + } + + $output->writeln($result->getLabel() . ':'); + foreach ($items as $item) { + $output->writeln("\t- " . $this->formatItem($item), OutputInterface::OUTPUT_PLAIN); + } + } + + if ($report->hasWarnings()) { + $output->writeln(sprintf('<comment>%d warnings detected</comment>', $report->getWarningCount())); + } + if ($report->hasErrors()) { + $output->writeln(sprintf('<error>%d errors detected</error>', $report->getErrorCount())); + return 1; + } + + return 0; + } + + private function formatItem(DiagnosticResultItem $item) + { + if ($item->getStatus() === DiagnosticResult::STATUS_ERROR) { + $tag = 'error'; + } elseif ($item->getStatus() === DiagnosticResult::STATUS_WARNING) { + $tag = 'comment'; + } else { + $tag = 'info'; + } + + return sprintf( + '<%s>%s %s</%s>', + $tag, + strtoupper($item->getStatus()), + preg_replace('/\<br\s*\/?\>/i', "\n", $item->getComment()), + $tag + ); + } +} diff --git a/plugins/Diagnostics/Diagnostic/CronArchivingCheck.php b/plugins/Diagnostics/Diagnostic/CronArchivingCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..b1c96f82716849d21e2f48654cc59c179a4741a0 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/CronArchivingCheck.php @@ -0,0 +1,42 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\CliMulti; +use Piwik\Config; +use Piwik\Http; +use Piwik\Translation\Translator; +use Piwik\Url; + +/** + * Check if cron archiving can run through CLI. + */ +class CronArchivingCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckCronArchiveProcess'); + $comment = $this->translator->translate('Installation_SystemCheckCronArchiveProcessCLI') . ': '; + + $process = new CliMulti(); + + if ($process->supportsAsync()) { + $comment .= $this->translator->translate('General_Ok'); + } else { + $comment .= $this->translator->translate('Installation_NotSupported') + . ' ' . $this->translator->translate('Goals_Optional'); + } + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK, $comment)); + } +} diff --git a/plugins/Diagnostics/Diagnostic/DbAdapterCheck.php b/plugins/Diagnostics/Diagnostic/DbAdapterCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..71edf6540b79ca777e922fd95f1090d4b19ba1bd --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/DbAdapterCheck.php @@ -0,0 +1,105 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Db\Adapter; +use Piwik\SettingsServer; +use Piwik\Translation\Translator; + +/** + * Check supported DB adapters are available. + */ +class DbAdapterCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $results = array(); + $results[] = $this->checkPdo(); + $results = array_merge($results, $this->checkDbAdapters()); + + return $results; + } + + private function checkPdo() + { + $label = 'PDO ' . $this->translator->translate('Installation_Extension'); + + if (in_array('PDO', $this->getPhpExtensionsLoaded())) { + $status = DiagnosticResult::STATUS_OK; + } else { + $status = DiagnosticResult::STATUS_WARNING; + } + + return DiagnosticResult::singleResult($label, $status); + } + + private function checkDbAdapters() + { + $results = array(); + $adapters = Adapter::getAdapters(); + + foreach ($adapters as $adapter => $port) { + $label = $adapter . ' ' . $this->translator->translate('Installation_Extension'); + + $results[] = DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK); + } + + if (empty($adapters)) { + $label = $this->translator->translate('Installation_SystemCheckDatabaseExtensions'); + $comment = $this->translator->translate('Installation_SystemCheckDatabaseHelp'); + + $result = DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_ERROR, $comment); + $result->setLongErrorMessage($this->getLongErrorMessage()); + + $results[] = $result; + } + + return $results; + } + + private function getPhpExtensionsLoaded() + { + return @get_loaded_extensions(); + } + + private function getLongErrorMessage() + { + $message = '<p>'; + + if (SettingsServer::isWindows()) { + $message .= $this->translator->translate( + 'Installation_SystemCheckWinPdoAndMysqliHelp', + array('<br /><br /><code>extension=php_mysqli.dll</code><br /><code>extension=php_pdo.dll</code><br /><code>extension=php_pdo_mysql.dll</code><br />') + ); + } else { + $message .= $this->translator->translate( + 'Installation_SystemCheckPdoAndMysqliHelp', + array( + '<br /><br /><code>--with-mysqli</code><br /><code>--with-pdo-mysql</code><br /><br />', + '<br /><br /><code>extension=mysqli.so</code><br /><code>extension=pdo.so</code><br /><code>extension=pdo_mysql.so</code><br />' + ) + ); + } + + $message .= $this->translator->translate('Installation_RestartWebServer') . '<br/><br/>'; + $message .= $this->translator->translate('Installation_SystemCheckPhpPdoAndMysqli', array( + '<a style="color:red" href="http://php.net/pdo">', + '</a>', + '<a style="color:red" href="http://php.net/mysqli">', + '</a>', + )); + $message .= '</p>'; + + return $message; + } +} diff --git a/plugins/Diagnostics/Diagnostic/Diagnostic.php b/plugins/Diagnostics/Diagnostic/Diagnostic.php new file mode 100644 index 0000000000000000000000000000000000000000..39c9be4c39db645332e7dcd117dfdafa15687d48 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/Diagnostic.php @@ -0,0 +1,44 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +/** + * Performs a diagnostic on the system or Piwik. + * + * Example: + * + * class MyDiagnostic implements Diagnostic + * { + * public function execute() + * { + * $results = array(); + * + * // First check (error) + * $status = testSomethingIsOk() ? DiagnosticResult::STATUS_OK : DiagnosticResult::STATUS_ERROR; + * $results[] = DiagnosticResult::singleResult('First check', $status); + * + * // Second check (warning) + * $status = testSomethingElseIsOk() ? DiagnosticResult::STATUS_OK : DiagnosticResult::STATUS_WARNING; + * $results[] = DiagnosticResult::singleResult('Second check', $status); + * + * return $results; + * } + * } + * + * Diagnostics are loaded with dependency injection support. + * + * @api + */ +interface Diagnostic +{ + /** + * @return DiagnosticResult[] + */ + public function execute(); +} diff --git a/plugins/Diagnostics/Diagnostic/DiagnosticResult.php b/plugins/Diagnostics/Diagnostic/DiagnosticResult.php new file mode 100644 index 0000000000000000000000000000000000000000..68c543676310e4b75b5b74b18eeeb0bc845fc8ae --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/DiagnosticResult.php @@ -0,0 +1,121 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +/** + * The result of a diagnostic. + * + * @api + */ +class DiagnosticResult +{ + const STATUS_ERROR = 'error'; + const STATUS_WARNING = 'warning'; + const STATUS_OK = 'ok'; + + /** + * @var string + */ + private $label; + + /** + * @var string + */ + private $longErrorMessage = ''; + + /** + * @var DiagnosticResultItem[] + */ + private $items = array(); + + public function __construct($label) + { + $this->label = $label; + } + + /** + * @param string $label + * @param string $status + * @param string $comment + * @return DiagnosticResult + */ + public static function singleResult($label, $status, $comment = '') + { + $result = new self($label); + $result->addItem(new DiagnosticResultItem($status, $comment)); + return $result; + } + + /** + * @return string + */ + public function getLabel() + { + return $this->label; + } + + /** + * @return DiagnosticResultItem[] + */ + public function getItems() + { + return $this->items; + } + + public function addItem(DiagnosticResultItem $item) + { + $this->items[] = $item; + } + + /** + * @param DiagnosticResultItem[] $items + */ + public function setItems(array $items) + { + $this->items = $items; + } + + /** + * @return string + */ + public function getLongErrorMessage() + { + return $this->longErrorMessage; + } + + /** + * @param string $longErrorMessage + */ + public function setLongErrorMessage($longErrorMessage) + { + $this->longErrorMessage = $longErrorMessage; + } + + /** + * Returns the worst status of the items. + * + * @return string One of the `STATUS_*` constants. + */ + public function getStatus() + { + $status = self::STATUS_OK; + + foreach ($this->getItems() as $item) { + if ($item->getStatus() === self::STATUS_ERROR) { + return self::STATUS_ERROR; + } + + if ($item->getStatus() === self::STATUS_WARNING) { + $status = self::STATUS_WARNING; + } + } + + return $status; + } +} diff --git a/plugins/Diagnostics/Diagnostic/DiagnosticResultItem.php b/plugins/Diagnostics/Diagnostic/DiagnosticResultItem.php new file mode 100644 index 0000000000000000000000000000000000000000..a39cb6dac413712d4595c1a571a212fc565d9146 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/DiagnosticResultItem.php @@ -0,0 +1,49 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +/** + * @api + */ +class DiagnosticResultItem +{ + /** + * @var string + */ + private $status; + + /** + * Optional comment about the item. + * + * @var string + */ + private $comment; + + public function __construct($status, $comment = '') + { + $this->status = $status; + $this->comment = $comment; + } + + /** + * @return string + */ + public function getStatus() + { + return $this->status; + } + + /** + * @return string + */ + public function getComment() + { + return $this->comment; + } +} diff --git a/plugins/Diagnostics/Diagnostic/FileIntegrityCheck.php b/plugins/Diagnostics/Diagnostic/FileIntegrityCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..c6730bc52e6fd6c6bbf5db1b2582092cd9111e53 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/FileIntegrityCheck.php @@ -0,0 +1,51 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Filechecks; +use Piwik\Translation\Translator; + +/** + * Check the files integrity. + */ +class FileIntegrityCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckFileIntegrity'); + + $messages = Filechecks::getFileIntegrityInformation(); + $ok = array_shift($messages); + + if (empty($messages)) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK)); + } + + if ($ok) { + $status = DiagnosticResult::STATUS_WARNING; + return array(DiagnosticResult::singleResult($label, $status, $messages[0])); + } + + $comment = $this->translator->translate('General_FileIntegrityWarningExplanation'); + + // Keep only the 20 first lines else it becomes unmanageable + if (count($messages) > 20) { + $messages = array_slice($messages, 0, 20); + $messages[] = '...'; + } + $comment .= '<br/><br/><pre style="overflow-x: scroll;max-width: 600px;">' + . implode("\n", $messages) . '</pre>'; + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_ERROR, $comment)); + } +} diff --git a/plugins/Diagnostics/Diagnostic/GdExtensionCheck.php b/plugins/Diagnostics/Diagnostic/GdExtensionCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..00bb10a19d2692734fd97f2d6497eb510d60bbe6 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/GdExtensionCheck.php @@ -0,0 +1,40 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Config; +use Piwik\SettingsServer; +use Piwik\Translation\Translator; + +/** + * Check that the GD extension is installed and the correct version. + */ +class GdExtensionCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckGDFreeType'); + + if (SettingsServer::isGdExtensionEnabled()) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK)); + } + + $comment = sprintf( + '%s<br />%s', + $this->translator->translate('Installation_SystemCheckGDFreeType'), + $this->translator->translate('Installation_SystemCheckGDHelp') + ); + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment)); + } +} diff --git a/plugins/Diagnostics/Diagnostic/HttpClientCheck.php b/plugins/Diagnostics/Diagnostic/HttpClientCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..998831a794e29893d078860111f1883c60abbaf4 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/HttpClientCheck.php @@ -0,0 +1,45 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Config; +use Piwik\Filechecks; +use Piwik\Http; +use Piwik\Translation\Translator; + +/** + * Check that Piwik's HTTP client can work correctly. + */ +class HttpClientCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckOpenURL'); + + $httpMethod = Http::getTransportMethod(); + + if ($httpMethod) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK, $httpMethod)); + } + + $canAutoUpdate = Filechecks::canAutoUpdate(); + + $comment = $this->translator->translate('Installation_SystemCheckOpenURLHelp'); + + if (! $canAutoUpdate) { + $comment .= '<br/>' . $this->translator->translate('Installation_SystemCheckAutoUpdateHelp'); + } + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment)); + } +} diff --git a/plugins/Diagnostics/Diagnostic/LoadDataInfileCheck.php b/plugins/Diagnostics/Diagnostic/LoadDataInfileCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..a6caab824c2d07d530dd01b5d7bf1a5adcf9ab9c --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/LoadDataInfileCheck.php @@ -0,0 +1,81 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Common; +use Piwik\Config; +use Piwik\Db; +use Piwik\Translation\Translator; + +/** + * Check if Piwik can use LOAD DATA INFILE. + */ +class LoadDataInfileCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $isPiwikInstalling = !Config::getInstance()->existsLocalConfig(); + if ($isPiwikInstalling) { + // Skip the diagnostic if Piwik is being installed + return array(); + } + + $label = $this->translator->translate('Installation_DatabaseAbilities'); + + $optionTable = Common::prefixTable('option'); + $testOptionNames = array('test_system_check1', 'test_system_check2'); + + $loadDataInfile = false; + $errorMessage = null; + try { + $loadDataInfile = Db\BatchInsert::tableInsertBatch( + $optionTable, + array('option_name', 'option_value'), + array( + array($testOptionNames[0], '1'), + array($testOptionNames[1], '2'), + ), + $throwException = true + ); + } catch (\Exception $ex) { + $errorMessage = str_replace("\n", "<br/>", $ex->getMessage()); + } + + // delete the temporary rows that were created + Db::exec("DELETE FROM `$optionTable` WHERE option_name IN ('" . implode("','", $testOptionNames) . "')"); + + if ($loadDataInfile) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK, 'LOAD DATA INFILE')); + } + + $comment = sprintf( + 'LOAD DATA INFILE<br/>%s<br/>%s', + $this->translator->translate('Installation_LoadDataInfileUnavailableHelp', array( + 'LOAD DATA INFILE', + 'FILE', + )), + $this->translator->translate('Installation_LoadDataInfileRecommended') + ); + + if ($errorMessage) { + $comment .= sprintf( + '<br/><strong>%s:</strong> %s<br/>%s', + $this->translator->translate('General_Error'), + $errorMessage, + 'Troubleshooting: <a target="_blank" href="?module=Proxy&action=redirect&url=http://piwik.org/faq/troubleshooting/%23faq_194">FAQ on piwik.org</a>' + ); + } + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment)); + } +} diff --git a/plugins/Diagnostics/Diagnostic/MemoryLimitCheck.php b/plugins/Diagnostics/Diagnostic/MemoryLimitCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..8f1ba1cf388a47350424dfc48ddae015a9b47382 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/MemoryLimitCheck.php @@ -0,0 +1,52 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Config; +use Piwik\SettingsServer; +use Piwik\Translation\Translator; + +/** + * Check that the memory limit is enough. + */ +class MemoryLimitCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + /** + * @var int + */ + private $minimumMemoryLimit; + + public function __construct(Translator $translator, $minimumMemoryLimit) + { + $this->translator = $translator; + $this->minimumMemoryLimit = $minimumMemoryLimit; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckMemoryLimit'); + + SettingsServer::raiseMemoryLimitIfNecessary(); + + $memoryLimit = SettingsServer::getMemoryLimitValue(); + $comment = $memoryLimit . 'M'; + + if ($memoryLimit >= $this->minimumMemoryLimit) { + $status = DiagnosticResult::STATUS_OK; + } else { + $status = DiagnosticResult::STATUS_WARNING; + $comment .= sprintf( + '<br />%s<br />%s', + $this->translator->translate('Installation_SystemCheckMemoryLimitHelp'), + $this->translator->translate('Installation_RestartWebServer') + ); + } + + return array(DiagnosticResult::singleResult($label, $status, $comment)); + } +} diff --git a/plugins/Diagnostics/Diagnostic/NfsDiskCheck.php b/plugins/Diagnostics/Diagnostic/NfsDiskCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..b563ae3d4a29d9b9373e8032148fd48765e9bbe4 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/NfsDiskCheck.php @@ -0,0 +1,50 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Config; +use Piwik\Filesystem; +use Piwik\Translation\Translator; + +/** + * Checks if the filesystem Piwik stores sessions in is NFS or not. + * + * This check is done in order to avoid using file based sessions on NFS system, + * since on such a filesystem file locking can make file based sessions incredibly slow. + */ +class NfsDiskCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_Filesystem'); + + if (! Filesystem::checkIfFileSystemIsNFS()) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK)); + } + + $isPiwikInstalling = !Config::getInstance()->existsLocalConfig(); + if ($isPiwikInstalling) { + $help = 'Installation_NfsFilesystemWarningSuffixInstall'; + } else { + $help = 'Installation_NfsFilesystemWarningSuffixAdmin'; + } + + $comment = sprintf( + '%s<br />%s', + $this->translator->translate('Installation_NfsFilesystemWarning'), + $this->translator->translate($help) + ); + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment)); + } +} diff --git a/plugins/Diagnostics/Diagnostic/PageSpeedCheck.php b/plugins/Diagnostics/Diagnostic/PageSpeedCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..f7444b8823446d3cca140ddece682fb1fad3be17 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/PageSpeedCheck.php @@ -0,0 +1,75 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Config; +use Piwik\Http; +use Piwik\Translation\Translator; +use Piwik\Url; +use Psr\Log\LoggerInterface; + +/** + * Check that mod_pagespeed is not enabled. + */ +class PageSpeedCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + /** + * @var LoggerInterface + */ + private $logger; + + public function __construct(Translator $translator, LoggerInterface $logger) + { + $this->translator = $translator; + $this->logger = $logger; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckPageSpeedDisabled'); + + if (! $this->isPageSpeedEnabled()) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK)); + } + + $comment = $this->translator->translate('Installation_SystemCheckPageSpeedWarn', array( + '(eg. Apache, Nginx or IIS)', + )); + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment)); + } + + private function isPageSpeedEnabled() + { + $url = Url::getCurrentUrlWithoutQueryString() . '?module=Installation&action=getEmptyPageForSystemCheck'; + + try { + $page = Http::sendHttpRequest($url, + $timeout = 1, + $userAgent = null, + $destinationPath = null, + $followDepth = 0, + $acceptLanguage = false, + $byteRange = false, + + // Return headers + $getExtendedInfo = true + ); + } catch(\Exception $e) { + $this->logger->info('Unable to test if mod_pagespeed is enabled: the request to {url} failed', array( + 'url' => $url, + )); + // If the test failed, we assume Page speed is not enabled + return false; + } + + $headers = $page['headers']; + + return isset($headers['X-Mod-Pagespeed']) || isset($headers['X-Page-Speed']); + } +} diff --git a/plugins/Diagnostics/Diagnostic/PhpExtensionsCheck.php b/plugins/Diagnostics/Diagnostic/PhpExtensionsCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..417aee17103f38f9d3a61c51ba81e6cbdb15f196 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/PhpExtensionsCheck.php @@ -0,0 +1,88 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Translation\Translator; + +/** + * Check the PHP extensions. + */ +class PhpExtensionsCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckExtensions'); + + $result = new DiagnosticResult($label); + $longErrorMessage = ''; + + $requiredExtensions = $this->getRequiredExtensions(); + + foreach ($requiredExtensions as $extension) { + if (! in_array($extension, $this->getPhpExtensionsLoaded())) { + $status = DiagnosticResult::STATUS_ERROR; + $comment = $extension . ': ' . $this->translator->translate('Installation_RestartWebServer'); + $longErrorMessage .= '<p>' . $this->getHelpMessage($extension) . '</p>'; + } else { + $status = DiagnosticResult::STATUS_OK; + $comment = $extension; + } + + $result->addItem(new DiagnosticResultItem($status, $comment)); + } + + $result->setLongErrorMessage($longErrorMessage); + + return array($result); + } + + /** + * @return string[] + */ + private function getRequiredExtensions() + { + $requiredExtensions = array( + 'zlib', + 'SPL', + 'iconv', + 'json', + 'mbstring', + ); + + if (! defined('HHVM_VERSION')) { + // HHVM provides the required subset of Reflection but lists Reflections as missing + $requiredExtensions[] = 'Reflection'; + } + + return $requiredExtensions; + } + + private function getPhpExtensionsLoaded() + { + return @get_loaded_extensions(); + } + + private function getHelpMessage($missingExtension) + { + $messages = array( + 'zlib' => 'Installation_SystemCheckZlibHelp', + 'SPL' => 'Installation_SystemCheckSplHelp', + 'iconv' => 'Installation_SystemCheckIconvHelp', + 'json' => 'Installation_SystemCheckWarnJsonHelp', + 'mbstring' => 'Installation_SystemCheckMbstringHelp', + 'Reflection' => 'Required extension that is built in PHP, see http://www.php.net/manual/en/book.reflection.php', + ); + + return $this->translator->translate($messages[$missingExtension]); + } +} diff --git a/plugins/Diagnostics/Diagnostic/PhpFunctionsCheck.php b/plugins/Diagnostics/Diagnostic/PhpFunctionsCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..6288d33cbdf816e807ac27faec5a9c0efca841e8 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/PhpFunctionsCheck.php @@ -0,0 +1,106 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Translation\Translator; + +/** + * Check the enabled PHP functions. + */ +class PhpFunctionsCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckFunctions'); + + $result = new DiagnosticResult($label); + + foreach ($this->getRequiredFunctions() as $function) { + if (! $this->functionExists($function)) { + $status = DiagnosticResult::STATUS_ERROR; + $comment = sprintf( + '%s <br/><br/><em>%s</em><br/><em>%s</em><br/>', + $function, + $this->getHelpMessage($function), + $this->translator->translate('Installation_RestartWebServer') + ); + } else { + $status = DiagnosticResult::STATUS_OK; + $comment = $function; + } + + $result->addItem(new DiagnosticResultItem($status, $comment)); + } + + return array($result); + } + + /** + * @return string[] + */ + private function getRequiredFunctions() + { + return array( + 'debug_backtrace', + 'create_function', + 'eval', + 'gzcompress', + 'gzuncompress', + 'pack', + ); + } + + /** + * Tests if a function exists. Also handles the case where a function is disabled via Suhosin. + * + * @param string $function + * @return bool + */ + private function functionExists($function) + { + // eval() is a language construct + if ($function == 'eval') { + // does not check suhosin.executor.eval.whitelist (or blacklist) + if (extension_loaded('suhosin')) { + return @ini_get("suhosin.executor.disable_eval") != "1"; + } + return true; + } + + $exists = function_exists($function); + + if (extension_loaded('suhosin')) { + $blacklist = @ini_get("suhosin.executor.func.blacklist"); + if (!empty($blacklist)) { + $blacklistFunctions = array_map('strtolower', array_map('trim', explode(',', $blacklist))); + return $exists && !in_array($function, $blacklistFunctions); + } + } + + return $exists; + } + + private function getHelpMessage($missingFunction) + { + $messages = array( + 'debug_backtrace' => 'Installation_SystemCheckDebugBacktraceHelp', + 'create_function' => 'Installation_SystemCheckCreateFunctionHelp', + 'eval' => 'Installation_SystemCheckEvalHelp', + 'gzcompress' => 'Installation_SystemCheckGzcompressHelp', + 'gzuncompress' => 'Installation_SystemCheckGzuncompressHelp', + 'pack' => 'Installation_SystemCheckPackHelp', + ); + + return $this->translator->translate($messages[$missingFunction]); + } +} diff --git a/plugins/Diagnostics/Diagnostic/PhpSettingsCheck.php b/plugins/Diagnostics/Diagnostic/PhpSettingsCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..6760c1d6be06a6311425dbbebe9a9ef86ccb7042 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/PhpSettingsCheck.php @@ -0,0 +1,75 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Translation\Translator; + +/** + * Check some PHP INI settings. + */ +class PhpSettingsCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckSettings'); + + $result = new DiagnosticResult($label); + + foreach ($this->getRequiredSettings() as $setting) { + list($settingName, $requiredValue) = explode('=', $setting); + + $currentValue = (int) ini_get($settingName); + + if ($currentValue != $requiredValue) { + $status = DiagnosticResult::STATUS_ERROR; + $comment = sprintf( + '%s <br/><br/><em>%s</em><br/><em>%s</em><br/>', + $setting, + $this->translator->translate('Installation_SystemCheckPhpSetting', array($setting)), + $this->translator->translate('Installation_RestartWebServer') + ); + } else { + $status = DiagnosticResult::STATUS_OK; + $comment = $setting; + } + + $result->addItem(new DiagnosticResultItem($status, $comment)); + } + + return array($result); + } + + /** + * @return string[] + */ + private function getRequiredSettings() + { + $requiredSettings = array( + // setting = required value + // Note: value must be an integer only + 'session.auto_start=0', + ); + + if ($this->isPhpVersionAtLeast56()) { + // always_populate_raw_post_data must be -1 + $requiredSettings[] = 'always_populate_raw_post_data=-1'; + } + + return $requiredSettings; + } + + private function isPhpVersionAtLeast56() + { + return version_compare(PHP_VERSION, '5.6', '>='); + } +} diff --git a/plugins/Diagnostics/Diagnostic/PhpVersionCheck.php b/plugins/Diagnostics/Diagnostic/PhpVersionCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..e7010804a0fce1e3965ea7f370360998fdce076c --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/PhpVersionCheck.php @@ -0,0 +1,53 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Translation\Translator; + +/** + * Check the PHP version. + */ +class PhpVersionCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + global $piwik_minimumPHPVersion; + + $actualVersion = PHP_VERSION; + + $label = sprintf( + '%s >= %s', + $this->translator->translate('Installation_SystemCheckPhp'), + $piwik_minimumPHPVersion + ); + + if ($this->isPhpVersionValid($piwik_minimumPHPVersion, $actualVersion)) { + $status = DiagnosticResult::STATUS_OK; + $comment = $actualVersion; + } else { + $status = DiagnosticResult::STATUS_ERROR; + $comment = sprintf( + '%s: %s', + $this->translator->translate('General_Error'), + $this->translator->translate('General_Required', array($label)) + ); + } + + return array(DiagnosticResult::singleResult($label, $status, $comment)); + } + + private function isPhpVersionValid($requiredVersion, $actualVersion) + { + return version_compare($requiredVersion, $actualVersion) <= 0; + } +} diff --git a/plugins/Diagnostics/Diagnostic/RecommendedExtensionsCheck.php b/plugins/Diagnostics/Diagnostic/RecommendedExtensionsCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..ebbb74c933cc52fe7d0dd6030067e5de3846c18c --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/RecommendedExtensionsCheck.php @@ -0,0 +1,72 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Translation\Translator; + +/** + * Check the PHP extensions that are not required but recommended. + */ +class RecommendedExtensionsCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckOtherExtensions'); + + $result = new DiagnosticResult($label); + + foreach ($this->getRecommendedExtensions() as $extension) { + if (! in_array($extension, $this->getPhpExtensionsLoaded())) { + $status = DiagnosticResult::STATUS_WARNING; + $comment = $extension . '<br/>' . $this->getHelpMessage($extension); + } else { + $status = DiagnosticResult::STATUS_OK; + $comment = $extension; + } + + $result->addItem(new DiagnosticResultItem($status, $comment)); + } + + return array($result); + } + + /** + * @return string[] + */ + private function getRecommendedExtensions() + { + return array( + 'json', + 'libxml', + 'dom', + 'SimpleXML', + ); + } + + private function getHelpMessage($missingExtension) + { + $messages = array( + 'json' => 'Installation_SystemCheckWarnJsonHelp', + 'libxml' => 'Installation_SystemCheckWarnLibXmlHelp', + 'dom' => 'Installation_SystemCheckWarnDomHelp', + 'SimpleXML' => 'Installation_SystemCheckWarnSimpleXMLHelp', + ); + + return $this->translator->translate($messages[$missingExtension]); + } + + private function getPhpExtensionsLoaded() + { + return @get_loaded_extensions(); + } +} diff --git a/plugins/Diagnostics/Diagnostic/RecommendedFunctionsCheck.php b/plugins/Diagnostics/Diagnostic/RecommendedFunctionsCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..1db14ade8c83ccb5eb5e257a7c17df8df217c79b --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/RecommendedFunctionsCheck.php @@ -0,0 +1,99 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Translation\Translator; + +/** + * Check the PHP functions that are not required but recommended. + */ +class RecommendedFunctionsCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckOtherFunctions'); + + $result = new DiagnosticResult($label); + + foreach ($this->getRecommendedFunctions() as $function) { + if (! $this->functionExists($function)) { + $status = DiagnosticResult::STATUS_WARNING; + $comment = $function . '<br/>' . $this->getHelpMessage($function); + } else { + $status = DiagnosticResult::STATUS_OK; + $comment = $function; + } + + $result->addItem(new DiagnosticResultItem($status, $comment)); + } + + return array($result); + } + + /** + * @return string[] + */ + private function getRecommendedFunctions() + { + return array( + 'set_time_limit', + 'mail', + 'parse_ini_file', + 'glob', + 'gzopen', + ); + } + + private function getHelpMessage($function) + { + $messages = array( + 'set_time_limit' => 'Installation_SystemCheckTimeLimitHelp', + 'mail' => 'Installation_SystemCheckMailHelp', + 'parse_ini_file' => 'Installation_SystemCheckParseIniFileHelp', + 'glob' => 'Installation_SystemCheckGlobHelp', + 'gzopen' => 'Installation_SystemCheckZlibHelp', + ); + + return $this->translator->translate($messages[$function]); + } + + /** + * Tests if a function exists. Also handles the case where a function is disabled via Suhosin. + * + * @param string $function + * @return bool + */ + private function functionExists($function) + { + // eval() is a language construct + if ($function == 'eval') { + // does not check suhosin.executor.eval.whitelist (or blacklist) + if (extension_loaded('suhosin')) { + return @ini_get("suhosin.executor.disable_eval") != "1"; + } + return true; + } + + $exists = function_exists($function); + + if (extension_loaded('suhosin')) { + $blacklist = @ini_get("suhosin.executor.func.blacklist"); + if (!empty($blacklist)) { + $blacklistFunctions = array_map('strtolower', array_map('trim', explode(',', $blacklist))); + return $exists && !in_array($function, $blacklistFunctions); + } + } + + return $exists; + } +} diff --git a/plugins/Diagnostics/Diagnostic/TimezoneCheck.php b/plugins/Diagnostics/Diagnostic/TimezoneCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..dfecd498bd824022b74753194fa927602ed1b9b0 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/TimezoneCheck.php @@ -0,0 +1,40 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Config; +use Piwik\SettingsServer; +use Piwik\Translation\Translator; + +/** + * Check that the PHP timezone setting is set. + */ +class TimezoneCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('SitesManager_Timezone'); + + if (SettingsServer::isTimezoneSupportEnabled()) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK)); + } + + $comment = sprintf( + '%s<br />%s.', + $this->translator->translate('SitesManager_AdvancedTimezoneSupportNotFound'), + '<a href="http://php.net/manual/en/datetime.installation.php" rel="noreferrer" target="_blank">Timezone PHP documentation</a>' + ); + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment)); + } +} diff --git a/plugins/Diagnostics/Diagnostic/TrackerCheck.php b/plugins/Diagnostics/Diagnostic/TrackerCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..1d2a95febf23624a2fa068077d3340bf5a005231 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/TrackerCheck.php @@ -0,0 +1,44 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Common; +use Piwik\Translation\Translator; + +/** + * Check that the tracker is working correctly. + */ +class TrackerCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckTracker'); + + $trackerStatus = Common::getRequestVar('trackerStatus', 0, 'int'); + + if ($trackerStatus == 0) { + $status = DiagnosticResult::STATUS_OK; + $comment = ''; + } else { + $status = DiagnosticResult::STATUS_WARNING; + $comment = sprintf( + '%s<br />%s<br />%s', + $trackerStatus, + $this->translator->translate('Installation_SystemCheckTrackerHelp'), + $this->translator->translate('Installation_RestartWebServer') + ); + } + + return array(DiagnosticResult::singleResult($label, $status, $comment)); + } +} diff --git a/plugins/Diagnostics/Diagnostic/WriteAccessCheck.php b/plugins/Diagnostics/Diagnostic/WriteAccessCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..5a291ce4bbf5a8a395f03af952dc8bf570ec12be --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/WriteAccessCheck.php @@ -0,0 +1,87 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Container\StaticContainer; +use Piwik\DbHelper; +use Piwik\Filechecks; +use Piwik\Translation\Translator; + +/** + * Check the permissions for some directories. + */ +class WriteAccessCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('Installation_SystemCheckWriteDirs'); + + $result = new DiagnosticResult($label); + + $directories = Filechecks::checkDirectoriesWritable($this->getDirectories()); + + $error = false; + foreach ($directories as $directory => $isWritable) { + if ($isWritable) { + $status = DiagnosticResult::STATUS_OK; + } else { + $status = DiagnosticResult::STATUS_ERROR; + $error = true; + } + + $result->addItem(new DiagnosticResultItem($status, $directory)); + } + + if ($error) { + $longErrorMessage = $this->translator->translate('Installation_SystemCheckWriteDirsHelp'); + $longErrorMessage .= '<ul>'; + foreach ($directories as $directory => $isWritable) { + if (! $isWritable) { + $longErrorMessage .= sprintf('<li><pre>chmod a+w %s</pre></li>', $directory); + } + } + $longErrorMessage .= '</ul>'; + $result->setLongErrorMessage($longErrorMessage); + } + + return array($result); + } + + /** + * @return string[] + */ + private function getDirectories() + { + // TODO dependency injection + $tmpPath = StaticContainer::get('path.tmp'); + + $directoriesToCheck = array( + $tmpPath, + $tmpPath . '/assets/', + $tmpPath . '/cache/', + $tmpPath . '/climulti/', + $tmpPath . '/latest/', + $tmpPath . '/logs/', + $tmpPath . '/sessions/', + $tmpPath . '/tcpdf/', + $tmpPath . '/templates_c/', + ); + + if (! DbHelper::isInstalled()) { + // at install, need /config to be writable (so we can create config.ini.php) + $directoriesToCheck[] = '/config/'; + } + + return $directoriesToCheck; + } +} diff --git a/plugins/Diagnostics/DiagnosticReport.php b/plugins/Diagnostics/DiagnosticReport.php new file mode 100644 index 0000000000000000000000000000000000000000..3fc3ae7e75066ffaf58f034649e633cfad938f85 --- /dev/null +++ b/plugins/Diagnostics/DiagnosticReport.php @@ -0,0 +1,115 @@ +<?php + +namespace Piwik\Plugins\Diagnostics; + +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; + +/** + * A diagnostic report contains all the results of all the diagnostics. + */ +class DiagnosticReport +{ + /** + * @var DiagnosticResult[] + */ + private $mandatoryDiagnosticResults; + + /** + * @var DiagnosticResult[] + */ + private $optionalDiagnosticResults; + + /** + * @var int|null + */ + private $errorCount; + + /** + * @var int|null + */ + private $warningCount; + + /** + * @param DiagnosticResult[] $mandatoryDiagnosticResults + * @param DiagnosticResult[] $optionalDiagnosticResults + */ + public function __construct(array $mandatoryDiagnosticResults, array $optionalDiagnosticResults) + { + $this->mandatoryDiagnosticResults = $mandatoryDiagnosticResults; + $this->optionalDiagnosticResults = $optionalDiagnosticResults; + } + + /** + * @return bool + */ + public function hasErrors() + { + return $this->getErrorCount() > 0; + } + + /** + * @return bool + */ + public function hasWarnings() + { + return $this->getWarningCount() > 0; + } + + /** + * @return int + */ + public function getErrorCount() + { + if ($this->errorCount === null) { + $this->errorCount = 0; + foreach ($this->getAllResults() as $result) { + if ($result->getStatus() === DiagnosticResult::STATUS_ERROR) { + $this->errorCount++; + } + } + } + + return $this->errorCount; + } + + /** + * @return int + */ + public function getWarningCount() + { + if ($this->warningCount === null) { + $this->warningCount = 0; + foreach ($this->getAllResults() as $result) { + if ($result->getStatus() === DiagnosticResult::STATUS_WARNING) { + $this->warningCount++; + } + } + } + + return $this->warningCount; + } + + /** + * @return DiagnosticResult[] + */ + public function getAllResults() + { + return array_merge($this->mandatoryDiagnosticResults, $this->optionalDiagnosticResults); + } + + /** + * @return DiagnosticResult[] + */ + public function getMandatoryDiagnosticResults() + { + return $this->mandatoryDiagnosticResults; + } + + /** + * @return DiagnosticResult[] + */ + public function getOptionalDiagnosticResults() + { + return $this->optionalDiagnosticResults; + } +} diff --git a/plugins/Diagnostics/DiagnosticService.php b/plugins/Diagnostics/DiagnosticService.php new file mode 100644 index 0000000000000000000000000000000000000000..3251f42d3bf343b6e98784fe6afc41654c7d2a73 --- /dev/null +++ b/plugins/Diagnostics/DiagnosticService.php @@ -0,0 +1,60 @@ +<?php + +namespace Piwik\Plugins\Diagnostics; + +use Piwik\Plugins\Diagnostics\Diagnostic\Diagnostic; +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; + +/** + * Runs the Piwik diagnostics. + * + * @api + */ +class DiagnosticService +{ + /** + * @var Diagnostic[] + */ + private $mandatoryDiagnostics; + + /** + * @var Diagnostic[] + */ + private $optionalDiagnostics; + + /** + * @param Diagnostic[] $mandatoryDiagnostics + * @param Diagnostic[] $optionalDiagnostics + */ + public function __construct(array $mandatoryDiagnostics, array $optionalDiagnostics) + { + $this->mandatoryDiagnostics = $mandatoryDiagnostics; + $this->optionalDiagnostics = $optionalDiagnostics; + } + + /** + * @return DiagnosticReport + */ + public function runDiagnostics() + { + return new DiagnosticReport( + $this->run($this->mandatoryDiagnostics), + $this->run($this->optionalDiagnostics) + ); + } + + /** + * @param Diagnostic[] $diagnostics + * @return DiagnosticResult[] + */ + private function run(array $diagnostics) + { + $results = array(); + + foreach ($diagnostics as $diagnostic) { + $results = array_merge($results, $diagnostic->execute()); + } + + return $results; + } +} diff --git a/plugins/Diagnostics/Diagnostics.php b/plugins/Diagnostics/Diagnostics.php new file mode 100644 index 0000000000000000000000000000000000000000..f69d1f45d128a5bc0eb829b5c38e62c84cb41e1f --- /dev/null +++ b/plugins/Diagnostics/Diagnostics.php @@ -0,0 +1,15 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Diagnostics; + +use Piwik\Plugin; + +class Diagnostics extends Plugin +{ +} diff --git a/plugins/Diagnostics/Test/Mock/DiagnosticWithError.php b/plugins/Diagnostics/Test/Mock/DiagnosticWithError.php new file mode 100644 index 0000000000000000000000000000000000000000..bc7d6db966214167c1d56509ef561a8885d4de38 --- /dev/null +++ b/plugins/Diagnostics/Test/Mock/DiagnosticWithError.php @@ -0,0 +1,22 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Diagnostics\Test\Mock; + +use Piwik\Plugins\Diagnostics\Diagnostic\Diagnostic; +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; + +class DiagnosticWithError implements Diagnostic +{ + public function execute() + { + return array( + DiagnosticResult::singleResult('Error', DiagnosticResult::STATUS_ERROR, 'Comment'), + ); + } +} diff --git a/plugins/Diagnostics/Test/Mock/DiagnosticWithWarning.php b/plugins/Diagnostics/Test/Mock/DiagnosticWithWarning.php new file mode 100644 index 0000000000000000000000000000000000000000..a7e891500509199dccb223392332955641fa276c --- /dev/null +++ b/plugins/Diagnostics/Test/Mock/DiagnosticWithWarning.php @@ -0,0 +1,22 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Diagnostics\Test\Mock; + +use Piwik\Plugins\Diagnostics\Diagnostic\Diagnostic; +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; + +class DiagnosticWithWarning implements Diagnostic +{ + public function execute() + { + return array( + DiagnosticResult::singleResult('Warning', DiagnosticResult::STATUS_WARNING, 'Comment'), + ); + } +} diff --git a/plugins/Diagnostics/Test/Unit/Diagnostic/DiagnosticResultTest.php b/plugins/Diagnostics/Test/Unit/Diagnostic/DiagnosticResultTest.php new file mode 100644 index 0000000000000000000000000000000000000000..efddeb74f2adc1ff24434f72a9907c62220367d7 --- /dev/null +++ b/plugins/Diagnostics/Test/Unit/Diagnostic/DiagnosticResultTest.php @@ -0,0 +1,37 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Diagnostics\Test\Unit\Diagnostic; + +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResultItem; + +class DiagnosticResultTest extends \PHPUnit_Framework_TestCase +{ + public function test_getStatus_shouldReturnTheWorstStatus() + { + $result = new DiagnosticResult('Label'); + + $this->assertEquals(DiagnosticResult::STATUS_OK, $result->getStatus()); + + $result->addItem(new DiagnosticResultItem(DiagnosticResult::STATUS_WARNING)); + $this->assertEquals(DiagnosticResult::STATUS_WARNING, $result->getStatus()); + + $result->addItem(new DiagnosticResultItem(DiagnosticResult::STATUS_ERROR)); + $this->assertEquals(DiagnosticResult::STATUS_ERROR, $result->getStatus()); + } + + public function test_singleResult_shouldReturnAResultWithASingleItem() + { + $result = DiagnosticResult::singleResult('Label', DiagnosticResult::STATUS_ERROR); + + $this->assertInstanceOf('Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult', $result); + $this->assertEquals('Label', $result->getLabel()); + $this->assertEquals(DiagnosticResult::STATUS_ERROR, $result->getStatus()); + } +} diff --git a/plugins/Diagnostics/Test/Unit/DiagnosticReportTest.php b/plugins/Diagnostics/Test/Unit/DiagnosticReportTest.php new file mode 100644 index 0000000000000000000000000000000000000000..bdd673b3c0fa6a4ea3f07cebc5e1be3fdb9438ae --- /dev/null +++ b/plugins/Diagnostics/Test/Unit/DiagnosticReportTest.php @@ -0,0 +1,47 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Diagnostics\Test\Unit; + +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; +use Piwik\Plugins\Diagnostics\DiagnosticReport; + +class DiagnosticReportTest extends \PHPUnit_Framework_TestCase +{ + public function test_shouldComputeErrorAndWarningCount() + { + $report = new DiagnosticReport( + array(DiagnosticResult::singleResult('Error', DiagnosticResult::STATUS_ERROR, 'Comment')), + array(DiagnosticResult::singleResult('Warning', DiagnosticResult::STATUS_WARNING, 'Comment')) + ); + + $this->assertEquals(1, $report->getErrorCount()); + $this->assertTrue($report->hasErrors()); + $this->assertEquals(1, $report->getWarningCount()); + $this->assertTrue($report->hasWarnings()); + + $report = new DiagnosticReport(array(), array()); + + $this->assertEquals(0, $report->getErrorCount()); + $this->assertFalse($report->hasErrors()); + $this->assertEquals(0, $report->getWarningCount()); + $this->assertFalse($report->hasWarnings()); + } + + public function test_getAllResults() + { + $report = new DiagnosticReport( + array(DiagnosticResult::singleResult('Error', DiagnosticResult::STATUS_ERROR, 'Comment')), + array(DiagnosticResult::singleResult('Warning', DiagnosticResult::STATUS_WARNING, 'Comment')) + ); + + $this->assertCount(1, $report->getMandatoryDiagnosticResults()); + $this->assertCount(1, $report->getOptionalDiagnosticResults()); + $this->assertCount(2, $report->getAllResults()); + } +} diff --git a/plugins/Diagnostics/Test/Unit/DiagnosticServiceTest.php b/plugins/Diagnostics/Test/Unit/DiagnosticServiceTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c4375dd648ed3089f688956009c0e7bbb089a2ee --- /dev/null +++ b/plugins/Diagnostics/Test/Unit/DiagnosticServiceTest.php @@ -0,0 +1,39 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Diagnostics\Test\Unit; + +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; +use Piwik\Plugins\Diagnostics\DiagnosticService; +use Piwik\Plugins\Diagnostics\Test\Mock\DiagnosticWithError; +use Piwik\Plugins\Diagnostics\Test\Mock\DiagnosticWithWarning; + +class DiagnosticServiceTest extends \PHPUnit_Framework_TestCase +{ + public function test_runDiagnostics() + { + $mandatoryDiagnostics = array( + new DiagnosticWithError(), + ); + $optionalDiagnostics = array( + new DiagnosticWithWarning(), + ); + + $service = new DiagnosticService($mandatoryDiagnostics, $optionalDiagnostics); + + $report = $service->runDiagnostics(); + + $results = $report->getAllResults(); + + $this->assertCount(2, $results); + $this->assertEquals('Error', $results[0]->getLabel()); + $this->assertEquals(DiagnosticResult::STATUS_ERROR, $results[0]->getStatus()); + $this->assertEquals('Warning', $results[1]->getLabel()); + $this->assertEquals(DiagnosticResult::STATUS_WARNING, $results[1]->getStatus()); + } +} diff --git a/plugins/Diagnostics/config/config.php b/plugins/Diagnostics/config/config.php new file mode 100644 index 0000000000000000000000000000000000000000..1ec0898a55f7f79b8185828c30b80c0e963236e8 --- /dev/null +++ b/plugins/Diagnostics/config/config.php @@ -0,0 +1,34 @@ +<?php + +return array( + // Diagnostics for everything that is required for Piwik to run + 'diagnostics.required' => array( + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\PhpVersionCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\DbAdapterCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\PhpExtensionsCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\PhpFunctionsCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\PhpSettingsCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\WriteAccessCheck'), + ), + // Diagnostics for recommended features + 'diagnostics.optional' => array( + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\FileIntegrityCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\TrackerCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\MemoryLimitCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\TimezoneCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\HttpClientCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\PageSpeedCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\GdExtensionCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\RecommendedExtensionsCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\RecommendedFunctionsCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\NfsDiskCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\CronArchivingCheck'), + DI\link('Piwik\Plugins\Diagnostics\Diagnostic\LoadDataInfileCheck'), + ), + + 'Piwik\Plugins\Diagnostics\DiagnosticService' => DI\object() + ->constructor(DI\link('diagnostics.required'), DI\link('diagnostics.optional')), + + 'Piwik\Plugins\Diagnostics\Diagnostic\MemoryLimitCheck' => DI\object() + ->constructorParameter('minimumMemoryLimit', DI\link('ini.General.minimum_memory_limit')), +); diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php index cdce51d111a41dbe4e0069aa9dee8d0c25d7ac86..c533ac07fca75ae607146a5aea2e85a3122546a9 100644 --- a/plugins/Installation/Controller.php +++ b/plugins/Installation/Controller.php @@ -23,9 +23,8 @@ use Piwik\Http; use Piwik\Option; use Piwik\Piwik; use Piwik\Plugin\Manager; -use Piwik\Plugins\CoreUpdater\CoreUpdater; +use Piwik\Plugins\Diagnostics\DiagnosticService; use Piwik\Plugins\LanguagesManager\LanguagesManager; -use Piwik\Plugins\PrivacyManager\IPAnonymizer; use Piwik\Plugins\SitesManager\API as APISitesManager; use Piwik\Plugins\UserCountry\LocationProvider; use Piwik\Plugins\UsersManager\API as APIUsersManager; @@ -112,15 +111,11 @@ class Controller extends \Piwik\Plugin\ControllerAdmin __FUNCTION__ ); - $view->duringInstall = true; + /** @var DiagnosticService $diagnosticService */ + $diagnosticService = StaticContainer::get('Piwik\Plugins\Diagnostics\DiagnosticService'); + $view->diagnosticReport = $diagnosticService->runDiagnostics(); - $this->setupSystemCheckView($view); - - $view->showNextStep = !$view->problemWithSomeDirectories - && $view->infos['phpVersion_ok'] - && count($view->infos['adapters']) - && !count($view->infos['missing_extensions']) - && !count($view->infos['missing_functions']); + $view->showNextStep = !$view->diagnosticReport->hasErrors(); // On the system check page, if all is green, display Next link at the top $view->showNextStepAtTop = $view->showNextStep; @@ -445,15 +440,6 @@ class Controller extends \Piwik\Plugin\ControllerAdmin return 'Hello, world!'; } - /** - * Get system information - */ - public static function getSystemInformation() - { - $systemCheck = new SystemCheck(); - return $systemCheck->getSystemInformation(); - } - /** * This controller action renders an admin tab that runs the installation * system check, so people can see if there are any issues w/ their running @@ -472,13 +458,9 @@ class Controller extends \Piwik\Plugin\ControllerAdmin ); $this->setBasicVariablesView($view); - $view->duringInstall = false; - - $this->setupSystemCheckView($view); - - $infos = $view->infos; - $infos['extra'] = SystemCheck::performAdminPageOnlySystemCheck(); - $view->infos = $infos; + /** @var DiagnosticService $diagnosticService */ + $diagnosticService = StaticContainer::get('Piwik\Plugins\Diagnostics\DiagnosticService'); + $view->diagnosticReport = $diagnosticService->runDiagnostics(); return $view->render(); } @@ -645,20 +627,6 @@ class Controller extends \Piwik\Plugin\ControllerAdmin } } - /** - * Utility function, sets up a view that will display system check info. - * - * @param View $view - */ - private function setupSystemCheckView($view) - { - $view->infos = self::getSystemInformation(); - - $view->helpMessages = $this->getSystemCheckHelpMessages(); - - $view->problemWithSomeDirectories = (false !== array_search(false, $view->infos['directories'])); - } - private function createSuperUser($login, $password, $email) { $self = $this; @@ -746,44 +714,4 @@ class Controller extends \Piwik\Plugin\ControllerAdmin return $result; }); } - - /** - * @return array - */ - private function getSystemCheckHelpMessages() - { - $helpMessages = array( - // Extensions - 'zlib' => 'Installation_SystemCheckZlibHelp', - 'gzopen' => 'Installation_SystemCheckZlibHelp', - 'SPL' => 'Installation_SystemCheckSplHelp', - 'iconv' => 'Installation_SystemCheckIconvHelp', - 'mbstring' => 'Installation_SystemCheckMbstringHelp', - 'Reflection' => 'Required extension that is built in PHP, see http://www.php.net/manual/en/book.reflection.php', - 'json' => 'Installation_SystemCheckWarnJsonHelp', - 'libxml' => 'Installation_SystemCheckWarnLibXmlHelp', - 'dom' => 'Installation_SystemCheckWarnDomHelp', - 'SimpleXML' => 'Installation_SystemCheckWarnSimpleXMLHelp', - - // Functions - 'set_time_limit' => 'Installation_SystemCheckTimeLimitHelp', - 'mail' => 'Installation_SystemCheckMailHelp', - 'parse_ini_file' => 'Installation_SystemCheckParseIniFileHelp', - 'glob' => 'Installation_SystemCheckGlobHelp', - 'debug_backtrace' => 'Installation_SystemCheckDebugBacktraceHelp', - 'create_function' => 'Installation_SystemCheckCreateFunctionHelp', - 'eval' => 'Installation_SystemCheckEvalHelp', - 'gzcompress' => 'Installation_SystemCheckGzcompressHelp', - 'gzuncompress' => 'Installation_SystemCheckGzuncompressHelp', - 'pack' => 'Installation_SystemCheckPackHelp', - 'php5-json' => 'Installation_SystemCheckJsonHelp', - ); - - // Add standard message for required PHP.ini settings - $requiredSettings = SystemCheck::getRequiredPhpSettings(); - foreach($requiredSettings as $requiredSetting) { - $helpMessages[$requiredSetting] = Piwik::translate('Installation_SystemCheckPhpSetting', $requiredSetting); - } - return $helpMessages; - } } diff --git a/plugins/Installation/SystemCheck.php b/plugins/Installation/SystemCheck.php deleted file mode 100644 index 93ed1ac75f162843772dda856b6c26beb4468818..0000000000000000000000000000000000000000 --- a/plugins/Installation/SystemCheck.php +++ /dev/null @@ -1,520 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Installation; - -use Piwik\CliMulti; -use Piwik\Common; -use Piwik\Config; -use Piwik\Container\StaticContainer; -use Piwik\Db; -use Piwik\Db\Adapter; -use Piwik\DbHelper; -use Piwik\Filechecks; -use Piwik\Filesystem; -use Piwik\Http; -use Piwik\Piwik; -use Piwik\Plugins\CoreUpdater; -use Piwik\Plugins\UserCountry\LocationProvider; -use Piwik\SettingsServer; -use Piwik\Url; - -class SystemCheck -{ - /** - * Get system information - */ - public static function getSystemInformation() - { - global $piwik_minimumPHPVersion; - - $infos = array(); - - $infos['directories'] = self::getDirectoriesWritableStatus(); - $infos['can_auto_update'] = Filechecks::canAutoUpdate(); - - self::initServerFilesForSecurity(); - - $infos['phpVersion_minimum'] = $piwik_minimumPHPVersion; - $infos['phpVersion'] = PHP_VERSION; - $infos['phpVersion_ok'] = self::isPhpVersionValid($infos['phpVersion']); - - // critical errors - $infos['needed_extensions'] = self::getRequiredExtensions(); - $infos['missing_extensions'] = self::getRequiredExtensionsMissing(); - - $infos['pdo_ok'] = self::isPhpExtensionLoaded('PDO'); - $infos['adapters'] = Adapter::getAdapters(); - - $infos['needed_functions'] = self::getRequiredFunctions(); - $infos['missing_functions'] = self::getRequiredFunctionsMissing(); - - // warnings - $infos['desired_extensions'] = self::getRecommendedExtensions(); - $infos['missing_desired_extensions'] = self::getRecommendedExtensionsMissing(); - - $infos['desired_functions'] = self::getRecommendedFunctions(); - $infos['missing_desired_functions'] = self::getRecommendedFunctionsMissing(); - - $infos['needed_settings'] = self::getRequiredPhpSettings(); - $infos['missing_settings'] = self::getMissingPhpSettings(); - - $infos['pagespeed_module_disabled_ok'] = self::isPageSpeedDisabled(); - - $infos['openurl'] = Http::getTransportMethod(); - $infos['gd_ok'] = SettingsServer::isGdExtensionEnabled(); - $infos['serverVersion'] = addslashes(isset($_SERVER['SERVER_SOFTWARE']) ?: ''); - $infos['serverOs'] = @php_uname(); - $infos['serverTime'] = date('H:i:s'); - - $infos['memoryMinimum'] = self::getMinimumRecommendedMemoryLimit(); - $infos['memory_ok'] = true; - $infos['memoryCurrent'] = ''; - - SettingsServer::raiseMemoryLimitIfNecessary(); - if (($memoryValue = SettingsServer::getMemoryLimitValue()) > 0) { - $infos['memoryCurrent'] = $memoryValue . 'M'; - $infos['memory_ok'] = $memoryValue >= self::getMinimumRecommendedMemoryLimit(); - } - - $infos['isWindows'] = SettingsServer::isWindows(); - - $integrityInfo = Filechecks::getFileIntegrityInformation(); - $infos['integrity'] = $integrityInfo[0]; - - $infos['integrityErrorMessages'] = array(); - if (isset($integrityInfo[1])) { - if ($infos['integrity'] == false) { - $infos['integrityErrorMessages'][] = Piwik::translate('General_FileIntegrityWarningExplanation'); - } - $infos['integrityErrorMessages'] = array_merge($infos['integrityErrorMessages'], array_slice($integrityInfo, 1)); - } - - $infos['timezone'] = SettingsServer::isTimezoneSupportEnabled(); - - $process = new CliMulti(); - $infos['cli_process_ok'] = $process->supportsAsync(); - - $infos['tracker_status'] = Common::getRequestVar('trackerStatus', 0, 'int'); - - $infos['is_nfs'] = Filesystem::checkIfFileSystemIsNFS(); - - $infos['https_update'] = CoreUpdater\Controller::isUpdatingOverHttps(); - - $infos = self::enrichSystemChecks($infos); - - return $infos; - } - - /** - * This can be overriden to provide a Customised System Check. - * - * @api - * @param $infos - * @return mixed - */ - public static function enrichSystemChecks($infos) - { - // determine whether there are any errors/warnings from the checks done above - $infos['has_errors'] = false; - $infos['has_warnings'] = false; - if (in_array(0, $infos['directories']) // if a directory is not writable - || !$infos['phpVersion_ok'] - || !empty($infos['missing_extensions']) - || empty($infos['adapters']) - || !empty($infos['missing_functions']) - ) { - $infos['has_errors'] = true; - } - - if ( !empty($infos['missing_desired_extensions']) - || !empty($infos['missing_desired_functions']) - || !empty($infos['missing_settings']) - || !$infos['pagespeed_module_disabled_ok'] - || !$infos['gd_ok'] - || !$infos['memory_ok'] - || !empty($infos['integrityErrorMessages']) - || !$infos['timezone'] // if timezone support isn't available - || $infos['tracker_status'] != 0 - || $infos['is_nfs'] - ) { - $infos['has_warnings'] = true; - } - return $infos; - } - - /** - * @return array - */ - protected static function getDirectoriesShouldBeWritable() - { - $tmpPath = StaticContainer::get('path.tmp'); - - $directoriesToCheck = array( - $tmpPath, - $tmpPath . '/assets/', - $tmpPath . '/cache/', - $tmpPath . '/climulti/', - $tmpPath . '/latest/', - $tmpPath . '/logs/', - $tmpPath . '/sessions/', - $tmpPath . '/tcpdf/', - $tmpPath . '/templates_c/', - ); - - if (!DbHelper::isInstalled()) { - // at install, need /config to be writable (so we can create config.ini.php) - $directoriesToCheck[] = '/config/'; - } - return $directoriesToCheck; - } - - /** - * @return array - */ - protected static function getRequiredFunctions() - { - return array( - 'debug_backtrace', - 'create_function', - 'eval', - 'gzcompress', - 'gzuncompress', - 'pack', - ); - } - - /** - * @return array - */ - protected static function getRecommendedExtensions() - { - return array( - 'json', - 'libxml', - 'dom', - 'SimpleXML', - ); - } - - /** - * @return array - */ - protected static function getRecommendedFunctions() - { - return array( - 'set_time_limit', - 'mail', - 'parse_ini_file', - 'glob', - 'gzopen', - ); - } - - /** - * @return array - */ - protected static function getRequiredExtensions() - { - $requiredExtensions = array( - 'zlib', - 'SPL', - 'iconv', - 'json', - 'mbstring', - ); - - if (!defined('HHVM_VERSION')) { - // HHVM provides the required subset of Reflection but lists Reflections as missing - $requiredExtensions[] = 'Reflection'; - } - - return $requiredExtensions; - } - - /** - * Performs extra system checks for the 'System Check' admin page. These - * checks are not performed during Installation. - * - * The following checks are performed: - * - Check for whether LOAD DATA INFILE can be used. The result of the check - * is stored in $result['load_data_infile_available']. The error message is - * stored in $result['load_data_infile_error']. - * - * - Check whether geo location is setup correctly - * - * @return array - */ - public static function performAdminPageOnlySystemCheck() - { - $result = array(); - self::checkLoadDataInfile($result); - self::checkGeolocation($result); - return $result; - } - - /** - * Test if function exists. Also handles case where function is disabled via Suhosin. - * - * @param string $functionName Function name - * @return bool True if function exists (not disabled); False otherwise. - */ - protected static function functionExists($functionName) - { - // eval() is a language construct - if ($functionName == 'eval') { - // does not check suhosin.executor.eval.whitelist (or blacklist) - if (extension_loaded('suhosin')) { - return @ini_get("suhosin.executor.disable_eval") != "1"; - } - return true; - } - - $exists = function_exists($functionName); - if (extension_loaded('suhosin')) { - $blacklist = @ini_get("suhosin.executor.func.blacklist"); - if (!empty($blacklist)) { - $blacklistFunctions = array_map('strtolower', array_map('trim', explode(',', $blacklist))); - return $exists && !in_array($functionName, $blacklistFunctions); - } - } - return $exists; - } - - private static function checkGeolocation(&$result) - { - $currentProviderId = LocationProvider::getCurrentProviderId(); - $allProviders = LocationProvider::getAllProviderInfo(); - $isRecommendedProvider = in_array($currentProviderId, array( LocationProvider\GeoIp\Php::ID, $currentProviderId == LocationProvider\GeoIp\Pecl::ID)); - $isProviderInstalled = ($allProviders[$currentProviderId]['status'] == LocationProvider::INSTALLED); - - $result['geolocation_using_non_recommended'] = $result['geolocation_ok'] = false; - if ($isRecommendedProvider && $isProviderInstalled) { - $result['geolocation_ok'] = true; - } elseif ($isProviderInstalled) { - $result['geolocation_using_non_recommended'] = true; - } - } - - private static function checkLoadDataInfile(&$result) - { - // check if LOAD DATA INFILE works - $optionTable = Common::prefixTable('option'); - $testOptionNames = array('test_system_check1', 'test_system_check2'); - - $result['load_data_infile_available'] = false; - try { - $result['load_data_infile_available'] = \Piwik\Db\BatchInsert::tableInsertBatch( - $optionTable, - array('option_name', 'option_value'), - array( - array($testOptionNames[0], '1'), - array($testOptionNames[1], '2'), - ), - $throwException = true - ); - } catch (\Exception $ex) { - $result['load_data_infile_error'] = str_replace("\n", "<br/>", $ex->getMessage()); - } - - // delete the temporary rows that were created - Db::exec("DELETE FROM `$optionTable` WHERE option_name IN ('" . implode("','", $testOptionNames) . "')"); - } - - protected static function initServerFilesForSecurity() - { - ServerFilesGenerator::createWebConfigFiles(); - ServerFilesGenerator::createHtAccessFiles(); - ServerFilesGenerator::createWebRootFiles(); - } - - /** - * @param string $phpVersion - * @return bool - */ - public static function isPhpVersionValid($phpVersion) - { - global $piwik_minimumPHPVersion; - return version_compare($piwik_minimumPHPVersion, $phpVersion) <= 0; - } - - /** - * @return array - */ - protected static function getDirectoriesWritableStatus() - { - $directoriesToCheck = self::getDirectoriesShouldBeWritable(); - $directoriesWritableStatus = Filechecks::checkDirectoriesWritable($directoriesToCheck); - return $directoriesWritableStatus; - } - - /** - * @return array - */ - protected static function getLoadedExtensions() - { - static $extensions = null; - - if(is_null($extensions)) { - $extensions = @get_loaded_extensions(); - } - return $extensions; - } - - - /** - * @param $needed_extension - * @return bool - */ - protected static function isPhpExtensionLoaded($needed_extension) - { - return in_array($needed_extension, self::getLoadedExtensions()); - } - - /** - * @return array - */ - protected static function getRequiredExtensionsMissing() - { - $missingExtensions = array(); - foreach (self::getRequiredExtensions() as $requiredExtension) { - if (!self::isPhpExtensionLoaded($requiredExtension)) { - $missingExtensions[] = $requiredExtension; - } - } - - // Special case for mbstring - if (!function_exists('mb_get_info') - || ((int)ini_get('mbstring.func_overload')) != 0) { - $missingExtensions[] = 'mbstring'; - } - - return $missingExtensions; - } - - /** - * @return array - */ - protected static function getRecommendedExtensionsMissing() - { - return array_diff(self::getRecommendedExtensions(), self::getLoadedExtensions()); - } - - /** - * @return array - */ - protected static function getRecommendedFunctionsMissing() - { - return self::getFunctionsMissing(self::getRecommendedFunctions()); - } - - /** - * @return array - */ - protected static function getRequiredFunctionsMissing() - { - return self::getFunctionsMissing(self::getRequiredFunctions()); - } - - protected static function getFunctionsMissing($functionsToTestFor) - { - $missingFunctions = array(); - foreach ($functionsToTestFor as $function) { - if (!self::functionExists($function)) { - $missingFunctions[] = $function; - } - } - return $missingFunctions; - } - - /** - * @return mixed - */ - protected static function getMinimumRecommendedMemoryLimit() - { - return Config::getInstance()->General['minimum_memory_limit']; - } - - private static function isPhpVersionAtLeast56() - { - return version_compare( PHP_VERSION, '5.6', '>='); - } - - /** - * @return array - */ - public static function getRequiredPhpSettings() - { - $requiredPhpSettings = array( - // setting = required value - // Note: value must be an integer only - 'session.auto_start=0', - ); - - if (self::isPhpVersionAtLeast56()) { - // always_populate_raw_post_data must be -1 - $requiredPhpSettings[] = 'always_populate_raw_post_data=-1'; - } - return $requiredPhpSettings; - } - - /** - * @return array - */ - protected static function getMissingPhpSettings() - { - $missingPhpSettings = array(); - foreach(self::getRequiredPhpSettings() as $requiredSetting) { - list($requiredSettingName, $requiredSettingValue) = explode('=', $requiredSetting); - - $currentValue = ini_get($requiredSettingName); - $currentValue = (int)$currentValue; - - if($currentValue != $requiredSettingValue) { - $missingPhpSettings[] = $requiredSetting; - } - } - return $missingPhpSettings; - } - - protected static function isPageSpeedDisabled() - { - $url = Url::getCurrentUrlWithoutQueryString() . '?module=Installation&action=getEmptyPageForSystemCheck'; - - try { - $page = Http::sendHttpRequest($url, - $timeout = 1, - $userAgent = null, - $destinationPath = null, - $followDepth = 0, - $acceptLanguage = false, - $byteRange = false, - - // Return headers - $getExtendedInfo = true - ); - } catch(\Exception $e) { - - // If the test failed, we assume Page speed is not enabled - return true; - } - - $headers = $page['headers']; - - return !self::isPageSpeedHeaderFound($headers); - } - - /** - * @param $headers - * @return bool - */ - protected static function isPageSpeedHeaderFound($headers) - { - return isset($headers['X-Mod-Pagespeed']) || isset($headers['X-Page-Speed']); - } -} \ No newline at end of file diff --git a/plugins/Installation/lang/en.json b/plugins/Installation/lang/en.json index 2d3afbf8c92ee76aa12f10efaf865b76c14f2e49..689760ef0ebd58709d2c92c050974f94b9a68d9b 100644 --- a/plugins/Installation/lang/en.json +++ b/plugins/Installation/lang/en.json @@ -65,6 +65,7 @@ "SystemCheck": "System Check", "SystemCheckAutoUpdateHelp": "Note: Piwik's One Click update requires write-permissions to the Piwik folder and its contents.", "SystemCheckCreateFunctionHelp": "Piwik uses anonymous functions for callbacks.", + "SystemCheckDatabaseExtensions": "MySQL extensions", "SystemCheckDatabaseHelp": "Piwik requires either the mysqli extension or both the PDO and pdo_mysql extensions.", "SystemCheckDebugBacktraceHelp": "View::factory won't be able to create views for the calling module.", "SystemCheckError": "An error occured - must be fixed before you proceed", diff --git a/plugins/Installation/templates/_integrityDetails.twig b/plugins/Installation/templates/_integrityDetails.twig index b285bb46f9ea6e7fcc50cafbd6178be979da3346..ddf9712ec0e07ddfa659e9199b41bfae93e34680 100644 --- a/plugins/Installation/templates/_integrityDetails.twig +++ b/plugins/Installation/templates/_integrityDetails.twig @@ -1,6 +1,3 @@ -{% if warningMessages is not defined %} - {% set warningMessages=infos.integrityErrorMessages %} -{% endif %} <div id="integrity-results" title="{{ 'Installation_SystemCheckFileIntegrity'|translate }}" style="display:none; font-size: 62.5%;"> <table> {% for msg in warningMessages %} diff --git a/plugins/Installation/templates/_systemCheckSection.twig b/plugins/Installation/templates/_systemCheckSection.twig index b56db9b6726e8e6cf2da6b8b8926abc44a610f38..c1f8c14332c723f6a646a648299bcd4dc86c97a4 100755 --- a/plugins/Installation/templates/_systemCheckSection.twig +++ b/plugins/Installation/templates/_systemCheckSection.twig @@ -1,372 +1,50 @@ -{% set ok %}<img src='plugins/Morpheus/images/ok.png' />{% endset %} -{% set error %}<img src='plugins/Morpheus/images/error.png' />{% endset %} -{% set warning %}<img src='plugins/Morpheus/images/warning.png' />{% endset %} -{% set link %}<img src='plugins/Morpheus/images/link.gif' />{% endset %} +{% import _self as local %} <table class="infosServer" id="systemCheckRequired"> - <tr> - {% set MinPHP %}{{ 'Installation_SystemCheckPhp'|translate }} >= {{ infos.phpVersion_minimum }}{% endset %} - <td class="label">{{ MinPHP }}</td> - - <td> - {% if infos.phpVersion_ok %} - {{ ok }} {{ infos.phpVersion }} - {% else %} - {{ error }} <span class="err">{{ 'General_Error'|translate }}: {{ 'General_Required'|translate(MinPHP)|raw }}</span> - {% endif %} - </td> - </tr> - <tr> - <td class="label">PDO {{ 'Installation_Extension'|translate }}</td> - <td> - {% if infos.pdo_ok %} - {{- ok -}} - {% else %} - - - {% endif %} - </td> - </tr> - {% for adapter, port in infos.adapters %} - <tr> - <td class="label">{{ adapter }} {{ 'Installation_Extension'|translate }}</td> - <td>{{ ok }}</td> - </tr> - {% endfor %} - {% if infos.adapters|length == 0 %} - <tr> - <td colspan="2" class="error" style="font-size: small;"> - {{ 'Installation_SystemCheckDatabaseHelp'|translate }} - <p> - {% if infos.isWindows %} - {{ 'Installation_SystemCheckWinPdoAndMysqliHelp'|translate("<br /><br /><code>extension=php_mysqli.dll</code><br /><code>extension=php_pdo.dll</code><br /><code>extension=php_pdo_mysql.dll</code><br />")|raw|nl2br }} - {% else %} - {{ 'Installation_SystemCheckPdoAndMysqliHelp'|translate("<br /><br /><code>--with-mysqli</code><br /><code>--with-pdo-mysql</code><br /><br />","<br /><br /><code>extension=mysqli.so</code><br /><code>extension=pdo.so</code><br /><code>extension=pdo_mysql.so</code><br />")|raw }} - {% endif %} - {{ 'Installation_RestartWebServer'|translate }} - <br/> - <br/> - {{ 'Installation_SystemCheckPhpPdoAndMysqli'|translate("<a style=\"color:red\" href=\"http:\/\/php.net\/pdo\">","<\/a>","<a style=\"color:red\" href=\"http:\/\/php.net\/mysqli\">","<\/a>")|raw|nl2br }} - </p> - </td> - </tr> - {% endif %} - <tr> - <td class="label">{{ 'Installation_SystemCheckExtensions'|translate }}</td> - <td> - {% for needed_extension in infos.needed_extensions %} - {% if needed_extension in infos.missing_extensions %} - {{ error }} - <br/>{{ 'Installation_RestartWebServer'|translate }} - {% else %} - {{ ok }} - {% endif %} - {{ needed_extension }} - <br/> - {% endfor %} - </td> - </tr> - {% if infos.missing_extensions|length > 0 %} - <tr> - <td colspan="2" class="error" style="font-size: small;"> - {% for missing_extension in infos.missing_extensions %} - <p> - <em>{{ helpMessages[missing_extension]|translate }}</em> - </p> - {% endfor %} - </td> - </tr> - {% endif %} - <tr> - <td class="label">{{ 'Installation_SystemCheckFunctions'|translate }}</td> - <td> - {% for needed_function in infos.needed_functions %} - {% if needed_function in infos.missing_functions %} - {{ error }} - <span class='err'>{{ needed_function }}</span> - <p> - <em> - {{ helpMessages[needed_function]|translate }} - <br/>{{ 'Installation_RestartWebServer'|translate }} - </em> - </p> - {% else %} - {{ ok }} {{ needed_function }} - <br/> - {% endif %} - {% endfor %} - </td> - </tr> - <tr> - <td class="label">{{ 'Installation_SystemCheckSettings'|translate }}</td> - <td> - {% for needed_setting in infos.needed_settings %} - {% if needed_setting in infos.missing_settings %} - {{ error }} - <span class='err'>{{ needed_setting }}</span> - <p> - <em> - {{ helpMessages[needed_setting]|translate }} - <br/>{{ 'Installation_RestartWebServer'|translate }} - </em> - </p> - {% else %} - {{ ok }} {{ needed_setting }} - <br/> - {% endif %} - {% endfor %} - </td> - </tr> - <tr> - <td valign="top"> - {{ 'Installation_SystemCheckWriteDirs'|translate }} - </td> - <td style="font-size: small;"> - {% for dir, bool in infos.directories %} - {% if bool %} - {{ ok }} - {% else %} - <span style="color:red;">{{ error }}</span> - {% endif %} - {{ dir }} - <br/> - {% endfor %} - </td> - </tr> - {% if problemWithSomeDirectories %} - <tr> - <td colspan="2" class="error"> - {{ 'Installation_SystemCheckWriteDirsHelp'|translate }}: - {% for dir,bool in infos.directories %} - <ul> - {% if not bool %} - <li> - <pre>chmod a+w {{ dir }}</pre> - </li> - {% endif %} - </ul> - {% endfor %} - </td> - </tr> - {% endif %} + {{ local.diagnosticTable(diagnosticReport.getMandatoryDiagnosticResults()) }} </table> -<br/> <h3>{{ 'Installation_Optional'|translate }}</h3> + <table class="infos" id="systemCheckOptional"> - <tr> - <td class="label">{{ 'Installation_SystemCheckFileIntegrity'|translate }}</td> - <td> - {% if infos.integrityErrorMessages is empty %} - {{ ok }} - {% else %} - {% if infos.integrity %} - {{ warning }} - <em>{{ infos.integrityErrorMessages[0] }}</em> - {% else %} - {{ error }} - <em>{{ infos.integrityErrorMessages[0] }}</em> - {% endif %} - {% if infos.integrityErrorMessages|length > 1 %} - <button id="more-results" class="ui-button ui-state-default ui-corner-all">{{ 'General_Details'|translate }}</button> - {% endif %} - {% endif %} - </td> - </tr> - <tr> - <td class="label">{{ 'Installation_SystemCheckTracker'|translate }}</td> - <td> - {% if infos.tracker_status == 0 %} - {{ ok }} - {% else %} - {{ warning }} - <span class="warn">{{ infos.tracker_status }} - <br/>{{ 'Installation_SystemCheckTrackerHelp'|translate }} </span> - <br/> - {{ 'Installation_RestartWebServer'|translate }} - {% endif %} - </td> - </tr> - <tr> - <td class="label">{{ 'Installation_SystemCheckMemoryLimit'|translate }}</td> - <td> - {% if infos.memory_ok %} - {{ ok }} {{ infos.memoryCurrent }} - {% else %} - {{ warning }} - <span class="warn">{{ infos.memoryCurrent }}</span> - <br/> - {{ 'Installation_SystemCheckMemoryLimitHelp'|translate }} - {{ 'Installation_RestartWebServer'|translate }} - {% endif %} - </td> - </tr> - <tr> - <td class="label">{{ 'SitesManager_Timezone'|translate }}</td> - <td> - {% if infos.timezone %} - {{ ok }} - {% else %} - {{ warning }} - <span class="warn">{{ 'SitesManager_AdvancedTimezoneSupportNotFound'|translate }} </span> - <br/> - <a href="http://php.net/manual/en/datetime.installation.php" rel="noreferrer" target="_blank">Timezone PHP documentation</a> - . - {% endif %} - </td> - </tr> - <tr> - <td class="label">{{ 'Installation_SystemCheckOpenURL'|translate }}</td> - <td> - {% if infos.openurl %} - {{ ok }} {{ infos.openurl }} - {% else %} - {{ warning }} - <span class="warn">{{ 'Installation_SystemCheckOpenURLHelp'|translate }}</span> - {% endif %} - {% if not infos.can_auto_update %} - <br/> - {{ warning }} <span class="warn">{{ 'Installation_SystemCheckAutoUpdateHelp'|translate }}</span> - {% endif %} - </td> - </tr> - <tr> - <td class="label">{{ 'Installation_SystemCheckPageSpeedDisabled'|translate }}</td> - <td> - {% if infos.pagespeed_module_disabled_ok %} - {{ ok }} - {% else %} - {{ warning }} <span class="warn">{{ 'Installation_SystemCheckPageSpeedWarn'|translate('(eg. Apache, Nginx or IIS)') }}</span> - {% endif %} - </td> - </tr> - <tr> - <td class="label">{{ 'Installation_SystemCheckGDFreeType'|translate }}</td> - <td> - {% if infos.gd_ok %} - {{ ok }} - {% else %} - {{ warning }} <span class="warn">{{ 'Installation_SystemCheckGDFreeType'|translate }} - <br/> - {{ 'Installation_SystemCheckGDHelp'|translate }} </span> - {% endif %} - </td> - </tr> - <tr> - <td class="label">{{ 'Installation_SystemCheckOtherExtensions'|translate }}</td> - <td> - {% for desired_extension in infos.desired_extensions %} - {% if desired_extension in infos.missing_desired_extensions %} - {{ warning }}<span class="warn">{{ desired_extension }}</span> - <p>{{ helpMessages[desired_extension]|translate }}</p> - {% else %} - {{ ok }} {{ desired_extension }} - <br/> - {% endif %} - {% endfor %} - </td> - </tr> - <tr> - <td class="label">{{ 'Installation_SystemCheckOtherFunctions'|translate }}</td> - <td> - {% for desired_function in infos.desired_functions %} - {% if desired_function in infos.missing_desired_functions %} - {{ warning }} - <span class="warn">{{ desired_function }}</span> - <p>{{ helpMessages[desired_function]|translate }}</p> - {% else %} - {{ ok }} {{ desired_function }} - <br/> - {% endif %} - {% endfor %} - </td> - </tr> - <tr> - <td class="label">{{ 'Installation_Filesystem'|translate }}</td> - <td> - {% if not infos.is_nfs %} - {{ ok }} {{ 'General_Ok'|translate }} - <br/> - {% else %} - {{ warning }} - <span class="warn">{{ 'Installation_NfsFilesystemWarning'|translate }}</span> - {% if duringInstall is not empty %} - <p>{{ 'Installation_NfsFilesystemWarningSuffixInstall'|translate }}</p> - {% else %} - <p>{{ 'Installation_NfsFilesystemWarningSuffixAdmin'|translate }}</p> - {% endif %} - {% endif %} - </td> - </tr> + {{ local.diagnosticTable(diagnosticReport.getOptionalDiagnosticResults()) }} +</table> - <tr> - <td class="label">{{ 'Installation_SystemCheckCronArchiveProcess'|translate }}</td> - <td> - {# Both results are OK, but we report the status to help troubleshoot #} - {% if infos.cli_process_ok %} - {{ ok }} {{ 'Installation_SystemCheckCronArchiveProcessCLI'|translate }}: {{ 'General_Ok'|translate }} - {% else %} - {{ ok }} {{ 'Installation_SystemCheckCronArchiveProcessCLI'|translate }}: - {{ 'Installation_NotSupported'|translate }} {{ 'Goals_Optional'|translate }} - {% endif %} - </td> - </tr> +{% macro diagnosticTable(results) %} - {% if duringInstall is empty %} - <tr> - <td class="label">{{ 'UserCountry_Geolocation'|translate }}</td> - <td> - {% if infos.extra.geolocation_ok %} - {{ ok }} {{ 'General_Ok'|translate }} - <br/> - {% elseif infos.extra.geolocation_using_non_recommended %} - {{ warning }} - <span class="warn">{{ 'UserCountry_GeoIpLocationProviderNotRecomnended'|translate }} - {{ 'UserCountry_GeoIpLocationProviderDesc_ServerBased2'|translate('<a href="http://piwik.org/docs/geo-locate/" rel="noreferrer" target="_blank">', '', '', '</a>')|raw }}</span> - <br/> - {% else %} - {{ warning }} - <span class="warn">{{ 'UserCountry_DefaultLocationProviderDesc1'|translate }} - {{ 'UserCountry_DefaultLocationProviderDesc2'|translate('<a href="http://piwik.org/docs/geo-locate/" rel="noreferrer" target="_blank">', '', '', '</a>')|raw }} </span> - </span> - {% endif %} - </td> - </tr> - {% endif %} - {% if infos.extra.load_data_infile_available is defined %} + {% set error = constant('Piwik\\Plugins\\Diagnostics\\Diagnostic\\DiagnosticResult::STATUS_ERROR') %} + {% set warning = constant('Piwik\\Plugins\\Diagnostics\\Diagnostic\\DiagnosticResult::STATUS_WARNING') %} + + {% set errorIcon %}<img src='plugins/Morpheus/images/error.png' />{% endset %} + {% set warningIcon %}<img src='plugins/Morpheus/images/warning.png' />{% endset %} + {% set okIcon %}<img src='plugins/Morpheus/images/ok.png' />{% endset %} + + {% for result in results %} <tr> - <td class="label">{{ 'Installation_DatabaseAbilities'|translate }}</td> + <td class="label">{{ result.label }}</td> <td> - {% if infos.extra.load_data_infile_available %} - {{ ok }} LOAD DATA INFILE - <br/> - {% else %} - {{ warning }} - <span class="warn">LOAD DATA INFILE</span> - <br/> - <br/> - <p>{{ 'Installation_LoadDataInfileUnavailableHelp'|translate("LOAD DATA INFILE","FILE") }}</p> - <p>{{ 'Installation_LoadDataInfileRecommended'|translate }}</p> - {% if infos.extra.load_data_infile_error is defined %} - <em><strong>{{ 'General_Error'|translate }}:</strong></em> - {{ infos.extra.load_data_infile_error|raw }} + {% for item in result.items %} + + {% if item.status == error %} + {{ errorIcon }} <span class="err">{{ item.comment|raw }}</span> + {% elseif item.status == warning %} + {{ warningIcon }} {{ item.comment|raw }} + {% else %} + {{ okIcon }} {{ item.comment|raw }} {% endif %} - <p>Troubleshooting: <a target='_blank' href="?module=Proxy&action=redirect&url=http://piwik.org/faq/troubleshooting/%23faq_194">FAQ on piwik.org</a></p> - {% endif %} - </td> - </tr> - {% endif %} - <tr> - <td class="label">{{ 'Installation_SystemCheckUpdateHttps'|translate }}</td> - <td> - {% if infos.https_update %} - {{ ok }} - {% else %} - {{ warning }} {{ 'Installation_SystemCheckUpdateHttpsNotSupported'|translate }} - {% endif %} - </td> - </tr> + <br/> -</table> + {% endfor %} + </td> + </tr> + {% if result.longErrorMessage %} + <tr> + <td colspan="2" class="error" style="font-size: small;"> + {{ result.longErrorMessage|raw }} + </td> + </tr> + {% endif %} + {% endfor %} -{% include "@Installation/_integrityDetails.twig" %} +{% endmacro %} diff --git a/plugins/Installation/templates/systemCheckPage.twig b/plugins/Installation/templates/systemCheckPage.twig index b66c493eea23f7dc9519d2c44cf608b1f2f43534..88f6b42688da23db3905b0799649caf16c6a7aa8 100755 --- a/plugins/Installation/templates/systemCheckPage.twig +++ b/plugins/Installation/templates/systemCheckPage.twig @@ -1,20 +1,24 @@ {% extends 'admin.twig' %} {% block content %} -{% if isSuperUser %} + <h2 piwik-enriched-headline>{{ 'Installation_SystemCheck'|translate }}</h2> + <p style="margin-left:0.2em;"> - {% if infos.has_errors %} + {% if diagnosticReport.hasErrors() %} <img src="plugins/Morpheus/images/error.png"/> - {{ 'Installation_SystemCheckSummaryThereWereErrors'|translate('<strong>','</strong>','<strong><em>','</em></strong>')|raw }} {{ 'Installation_SeeBelowForMoreInfo'|translate }} - {% elseif infos.has_warnings %} + {{ 'Installation_SystemCheckSummaryThereWereErrors'|translate('<strong>','</strong>','<strong><em>','</em></strong>')|raw }} + {{ 'Installation_SeeBelowForMoreInfo'|translate }} + {% elseif diagnosticReport.hasWarnings() %} <img src="plugins/Morpheus/images/warning.png"/> - {{ 'Installation_SystemCheckSummaryThereWereWarnings'|translate }} {{ 'Installation_SeeBelowForMoreInfo'|translate }} + {{ 'Installation_SystemCheckSummaryThereWereWarnings'|translate }} + {{ 'Installation_SeeBelowForMoreInfo'|translate }} {% else %} <img src="plugins/Morpheus/images/ok.png"/> {{ 'Installation_SystemCheckSummaryNoProblems'|translate }} {% endif %} </p> + {% include "@Installation/_systemCheckSection.twig" %} -{% endif %} + {% endblock %} diff --git a/plugins/UserCountry/Diagnostic/GeolocationDiagnostic.php b/plugins/UserCountry/Diagnostic/GeolocationDiagnostic.php new file mode 100644 index 0000000000000000000000000000000000000000..720d24e2798ae91194e8c20f3e094c9e1e8eba6b --- /dev/null +++ b/plugins/UserCountry/Diagnostic/GeolocationDiagnostic.php @@ -0,0 +1,59 @@ +<?php + +namespace Piwik\Plugins\UserCountry\Diagnostic; + +use Piwik\Config; +use Piwik\Plugins\Diagnostics\Diagnostic\Diagnostic; +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; +use Piwik\Plugins\UserCountry\LocationProvider; +use Piwik\Translation\Translator; + +/** + * Check the geolocation setup. + */ +class GeolocationDiagnostic implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $isPiwikInstalling = !Config::getInstance()->existsLocalConfig(); + if ($isPiwikInstalling) { + // Skip the diagnostic if Piwik is being installed + return array(); + } + + $label = $this->translator->translate('UserCountry_Geolocation'); + + $currentProviderId = LocationProvider::getCurrentProviderId(); + $allProviders = LocationProvider::getAllProviderInfo(); + $isRecommendedProvider = in_array($currentProviderId, array(LocationProvider\GeoIp\Php::ID, $currentProviderId == LocationProvider\GeoIp\Pecl::ID)); + $isProviderInstalled = ($allProviders[$currentProviderId]['status'] == LocationProvider::INSTALLED); + + if ($isRecommendedProvider && $isProviderInstalled) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK)); + } + + if ($isProviderInstalled) { + $comment = $this->translator->translate('UserCountry_GeoIpLocationProviderNotRecomnended') . ' '; + $comment .= $this->translator->translate('UserCountry_GeoIpLocationProviderDesc_ServerBased2', array( + '<a href="http://piwik.org/docs/geo-locate/" rel="noreferrer" target="_blank">', '', '', '</a>' + )); + } else { + $comment = $this->translator->translate('UserCountry_DefaultLocationProviderDesc1') . ' '; + $comment .= $this->translator->translate('UserCountry_DefaultLocationProviderDesc2', array( + '<a href="http://piwik.org/docs/geo-locate/" rel="noreferrer" target="_blank">', '', '', '</a>' + )); + } + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment)); + } +} diff --git a/plugins/UserCountry/config/config.php b/plugins/UserCountry/config/config.php new file mode 100644 index 0000000000000000000000000000000000000000..89c61fc9797a70782acb77cb0de8a19b28370268 --- /dev/null +++ b/plugins/UserCountry/config/config.php @@ -0,0 +1,7 @@ +<?php + +return array( + 'diagnostics.optional' => DI\add(array( + DI\link('Piwik\Plugins\UserCountry\Diagnostic\GeolocationDiagnostic'), + )), +);