diff --git a/lang/en.json b/lang/en.json index d63346e104c41836c0ff1947a3173992ea706ddf..42d54237313c39089d0bc6ddb719a9daa9e0e09d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1050,8 +1050,6 @@ "Congratulations": "Congratulations", "CongratulationsHelp": "<p>Congratulations! Your Piwik installation is complete.<\/p><p>Make sure your JavaScript code is entered on your pages, and wait for your first visitors!<\/p>", "DatabaseAbilities": "Database abilities", - "DatabaseCheck": "Database Check", - "DatabaseClientVersion": "Database client version", "DatabaseCreation": "Database creation", "DatabaseErrorConnect": "Error while trying to connect to the database server", "DatabaseServerVersion": "Database server version", @@ -1062,7 +1060,7 @@ "DatabaseSetupServer": "Database Server", "DatabaseSetupTablePrefix": "Table Prefix", "Email": "email", - "ErrorInvalidState": "Error: it seems you tried to skip a step of the Installation process, or your cookies are disabled, or the Piwik configuration file was already created. %1$sMake sure your cookies are enabled%2$s and go back %3$s to the first page of the installation %4$s.", + "InvalidStateError": "Error: Piwik is already installed. %1$s Go back %3$s to Piwik%4$s.", "Extension": "extension", "Filesystem": "Filesystem", "GetInvolved": "If you like what you see, you can %1$sget involved%2$s.", diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php index dc015c52592f51d8da3d23dd1cd37e689877d66e..972d851747c8ddbbb59be15dc26703a775111af2 100644 --- a/plugins/Installation/Controller.php +++ b/plugins/Installation/Controller.php @@ -10,7 +10,6 @@ namespace Piwik\Plugins\Installation; use Exception; use Piwik\Access; -use Piwik\API\Request; use Piwik\AssetManager; use Piwik\Common; use Piwik\Config; @@ -18,7 +17,6 @@ use Piwik\DataAccess\ArchiveTableCreator; use Piwik\Db\Adapter; use Piwik\Db; use Piwik\DbHelper; -use Piwik\Filechecks; use Piwik\Filesystem; use Piwik\Http; use Piwik\Option; @@ -27,14 +25,12 @@ use Piwik\Plugin\Manager; use Piwik\Plugins\CoreUpdater\CoreUpdater; use Piwik\Plugins\LanguagesManager\LanguagesManager; use Piwik\Plugins\SitesManager\API as APISitesManager; +use Piwik\Plugins\SitesManager\API; use Piwik\Plugins\UserCountry\LocationProvider; use Piwik\Plugins\UsersManager\API as APIUsersManager; use Piwik\ProxyHeaders; -use Piwik\Session\SessionNamespace; use Piwik\SettingsPiwik; -use Piwik\SettingsServer; use Piwik\Updater; -use Piwik\UpdaterErrorException; use Piwik\Url; use Piwik\Version; use Zend_Db_Adapter_Exception; @@ -45,12 +41,10 @@ use Zend_Db_Adapter_Exception; */ class Controller extends \Piwik\Plugin\ControllerAdmin { - // public so plugins can add/delete installation steps public $steps = array( 'welcome' => 'Installation_Welcome', 'systemCheck' => 'Installation_SystemCheck', 'databaseSetup' => 'Installation_DatabaseSetup', - 'databaseCheck' => 'Installation_DatabaseCheck', 'tablesCreation' => 'Installation_Tables', 'generalSetup' => 'Installation_SuperUser', 'firstWebsiteSetup' => 'Installation_SetupWebsite', @@ -58,27 +52,6 @@ class Controller extends \Piwik\Plugin\ControllerAdmin 'finished' => 'Installation_Congratulations', ); - protected $session; - - public function __construct() - { - $this->session = new SessionNamespace('Installation'); - if (!isset($this->session->currentStepDone)) { - $this->session->currentStepDone = ''; - $this->session->skipThisStep = array(); - } - } - - protected static function initServerFilesForSecurity() - { - if (SettingsServer::isIIS()) { - ServerFilesGenerator::createWebConfigFiles(); - } else { - ServerFilesGenerator::createHtAccessFiles(); - } - ServerFilesGenerator::createWebRootFiles(); - } - /** * Get installation steps * @@ -116,9 +89,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $view->newInstall = !$this->isFinishedInstallation(); $view->errorMessage = $message; - $this->skipThisStep(__FUNCTION__); $view->showNextStep = $view->newInstall; - $this->session->currentStepDone = __FUNCTION__; return $view->render(); } @@ -127,36 +98,27 @@ class Controller extends \Piwik\Plugin\ControllerAdmin */ function systemCheck() { - $this->checkPreviousStepIsValid(__FUNCTION__); + $this->checkPiwikIsNotInstalled(); $view = new View( '@Installation/systemCheck', $this->getInstallationSteps(), __FUNCTION__ ); - $this->skipThisStep(__FUNCTION__); $view->duringInstall = true; $this->setupSystemCheckView($view); - $this->session->general_infos = $view->infos['general_infos']; - $this->session->general_infos['salt'] = Common::generateUniqId(); - - // make sure DB sessions are used if the filesystem is NFS - if ($view->infos['is_nfs']) { - $this->session->general_infos['session_save_handler'] = 'dbtable'; - } $view->showNextStep = !$view->problemWithSomeDirectories && $view->infos['phpVersion_ok'] && count($view->infos['adapters']) && !count($view->infos['missing_extensions']) && !count($view->infos['missing_functions']); + // On the system check page, if all is green, display Next link at the top $view->showNextStepAtTop = $view->showNextStep; - $this->session->currentStepDone = __FUNCTION__; - return $view->render(); } @@ -166,15 +128,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin */ function databaseSetup() { - $this->checkPreviousStepIsValid(__FUNCTION__); - - // case the user hits the back button - $this->session->skipThisStep = array( - 'firstWebsiteSetup' => false, - 'trackingCode' => false, - ); - - $this->skipThisStep(__FUNCTION__); + $this->checkPiwikIsNotInstalled(); $view = new View( '@Installation/databaseSetup', @@ -189,10 +143,10 @@ class Controller extends \Piwik\Plugin\ControllerAdmin if ($form->validate()) { try { $dbInfos = $form->createDatabaseObject(); - $this->session->databaseCreated = true; DbHelper::checkDatabaseVersion(); - $this->session->databaseVersionOk = true; + + Db::get()->checkClientVersion(); $this->deleteConfigFileIfNeeded(); $this->createConfigFile($dbInfos); @@ -208,83 +162,21 @@ class Controller extends \Piwik\Plugin\ControllerAdmin } /** - * Installation Step 4: Database Check - */ - function databaseCheck() - { - $this->checkPreviousStepIsValid(__FUNCTION__); - $view = new View( - '@Installation/databaseCheck', - $this->getInstallationSteps(), - __FUNCTION__ - ); - - $error = false; - $this->skipThisStep(__FUNCTION__); - - if (isset($this->session->databaseVersionOk) - && $this->session->databaseVersionOk === true - ) { - $view->databaseVersionOk = true; - } else { - $error = true; - } - - if (isset($this->session->databaseCreated) - && $this->session->databaseCreated === true - ) { - $view->databaseName = Config::getInstance()->database['dbname']; - $view->databaseCreated = true; - } else { - $error = true; - } - - $db = Db::get(); - - try { - $db->checkClientVersion(); - } catch (Exception $e) { - $view->clientVersionWarning = $e->getMessage(); - $error = true; - } - - if (!DbHelper::isDatabaseConnectionUTF8()) { - Config::getInstance()->database['charset'] = 'utf8'; - Config::getInstance()->forceSave(); - } - - $view->showNextStep = true; - $this->session->currentStepDone = __FUNCTION__; - - if ($error === false) { - $this->redirectToNextStep(__FUNCTION__); - } - return $view->render(); - } - - /** - * Installation Step 5: Table Creation + * Installation Step 4: Table Creation */ function tablesCreation() { - $this->checkPreviousStepIsValid(__FUNCTION__); + $this->checkPiwikIsNotInstalled(); $view = new View( '@Installation/tablesCreation', $this->getInstallationSteps(), __FUNCTION__ ); - $this->skipThisStep(__FUNCTION__); - if (Common::getRequestVar('deleteTables', 0, 'int') == 1) { + if ($this->getParam('deleteTables')) { Db::dropAllTables(); $view->existingTablesDeleted = true; - - // when the user decides to drop the tables then we dont skip the next steps anymore - $tmp = $this->session->skipThisStep; - $tmp['firstWebsiteSetup'] = false; - $tmp['trackingCode'] = false; - $this->session->skipThisStep = $tmp; } $tablesInstalled = DbHelper::getTablesInstalled(); @@ -303,11 +195,6 @@ class Controller extends \Piwik\Plugin\ControllerAdmin count(APIUsersManager::getInstance()->getUsers()) > 0 ) { $view->showReuseExistingTables = true; - // when the user reuses the same tables we skip the website creation step - $tmp = $this->session->skipThisStep; - $tmp['firstWebsiteSetup'] = true; - $tmp['trackingCode'] = true; - $this->session->skipThisStep = $tmp; } } else { @@ -320,13 +207,12 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $view->showNextStep = true; } - $this->session->currentStepDone = __FUNCTION__; return $view->render(); } function reuseTables() { - $this->checkPreviousStepIsValid(__FUNCTION__); + $this->checkPiwikIsNotInstalled(); $steps = $this->getInstallationSteps(); $steps['tablesCreation'] = 'Installation_ReusingTables'; @@ -344,9 +230,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $componentsWithUpdateFile = CoreUpdater::getComponentUpdates($updater); if (empty($componentsWithUpdateFile)) { - $this->session->currentStepDone = 'tablesCreation'; - $this->redirectToNextStep('tablesCreation'); - return ''; + return $this->redirectToNextStep('tablesCreation'); } $oldVersion = Option::get('version_core'); @@ -361,24 +245,21 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $view->oldVersion = $oldVersion; $view->showNextStep = true; - $this->session->currentStepDone = 'tablesCreation'; - return $view->render(); } /** - * Installation Step 6: General Set-up (superuser login/password/email and subscriptions) + * Installation Step 5: General Set-up (superuser login/password/email and subscriptions) */ function generalSetup() { - $this->checkPreviousStepIsValid(__FUNCTION__); + $this->checkPiwikIsNotInstalled(); $view = new View( '@Installation/generalSetup', $this->getInstallationSteps(), __FUNCTION__ ); - $this->skipThisStep(__FUNCTION__); $form = new FormGeneralSetup(); @@ -389,30 +270,10 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $form->getSubmitValue('password'), $form->getSubmitValue('email')); - $url = Config::getInstance()->General['api_service_url']; - $url .= '/1.0/subscribeNewsletter/'; - $params = array( - 'email' => $form->getSubmitValue('email'), - 'security' => $form->getSubmitValue('subscribe_newsletter_security'), - 'community' => $form->getSubmitValue('subscribe_newsletter_community'), - 'url' => Url::getCurrentUrlWithoutQueryString(), - ); - if ($params['security'] == '1' - || $params['community'] == '1' - ) { - if (!isset($params['security'])) { - $params['security'] = '0'; - } - if (!isset($params['community'])) { - $params['community'] = '0'; - } - $url .= '?' . http_build_query($params, '', '&'); - try { - Http::sendHttpRequest($url, $timeout = 2); - } catch (Exception $e) { - // e.g., disable_functions = fsockopen; allow_url_open = Off - } - } + $email = $form->getSubmitValue('email'); + $newsletterSecurity = $form->getSubmitValue('subscribe_newsletter_security'); + $newsletterCommunity = $form->getSubmitValue('subscribe_newsletter_community'); + $this->registerNewsletter($email, $newsletterSecurity, $newsletterCommunity); $this->redirectToNextStep(__FUNCTION__); } catch (Exception $e) { @@ -426,26 +287,27 @@ class Controller extends \Piwik\Plugin\ControllerAdmin } /** - * Installation Step 7: Configure first web-site + * Installation Step 6: Configure first web-site */ public function firstWebsiteSetup() { - $this->checkPreviousStepIsValid(__FUNCTION__); + $this->checkPiwikIsNotInstalled(); + + $this->initObjectsToCallAPI(); + + if(count(API::getInstance()->getAllSitesId()) > 0) { + // if there is a already a website, skip this step and trackingCode step + $this->redirectToNextStep('trackingCode'); + } $view = new View( '@Installation/firstWebsiteSetup', $this->getInstallationSteps(), __FUNCTION__ ); - $this->skipThisStep(__FUNCTION__); $form = new FormFirstWebsiteSetup(); - if (!isset($this->session->generalSetupSuccessMessage)) { - $view->displayGeneralSetupSuccess = true; - $this->session->generalSetupSuccessMessage = true; - } - $this->initObjectsToCallAPI(); if ($form->validate()) { $name = Common::unsanitizeInputValue($form->getSubmitValue('siteName')); $url = Common::unsanitizeInputValue($form->getSubmitValue('url')); @@ -453,39 +315,38 @@ class Controller extends \Piwik\Plugin\ControllerAdmin try { $result = APISitesManager::getInstance()->addSite($name, $url, $ecommerce); - $this->session->site_idSite = $result; - $this->session->site_name = $name; - $this->session->site_url = $url; + $params = array( + 'site_idSite' => $result, + 'site_name' => urlencode($name) + ); + $this->addTrustedHosts($url); - $this->redirectToNextStep(__FUNCTION__); + $this->redirectToNextStep(__FUNCTION__, $params); } catch (Exception $e) { $view->errorMessage = $e->getMessage(); } + } else { + $view->displayGeneralSetupSuccess = true; } $view->addForm($form); return $view->render(); } /** - * Installation Step 8: Display JavaScript tracking code + * Installation Step 7: Display JavaScript tracking code */ public function trackingCode() { - $this->checkPreviousStepIsValid(__FUNCTION__); + $this->checkPiwikIsNotInstalled(); $view = new View( '@Installation/trackingCode', $this->getInstallationSteps(), __FUNCTION__ ); - $this->skipThisStep(__FUNCTION__); - if (!isset($this->session->firstWebsiteSetupSuccessMessage)) { - $view->displayfirstWebsiteSetupSuccess = true; - $this->session->firstWebsiteSetupSuccessMessage = true; - } - $siteName = $this->session->site_name; - $idSite = $this->session->site_idSite; + $siteName = Common::unsanitizeInputValue($this->getParam('site_name')); + $idSite = $this->getParam('site_idSite'); // Load the Tracking code and help text from the SitesManager $viewTrackingHelp = new \Piwik\View('@SitesManager/_displayJavascriptCode'); @@ -497,41 +358,42 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $view->trackingHelp = $viewTrackingHelp->render(); $view->displaySiteName = $siteName; + $view->displayfirstWebsiteSetupSuccess = true; $view->showNextStep = true; - $this->session->currentStepDone = __FUNCTION__; return $view->render(); } /** - * Installation Step 9: Finished! + * Installation Step 8: Finished! */ public function finished() { - $this->checkPreviousStepIsValid(__FUNCTION__); - $view = new View( '@Installation/finished', $this->getInstallationSteps(), __FUNCTION__ ); - $this->skipThisStep(__FUNCTION__); if (!$this->isFinishedInstallation()) { - $this->addTrustedHosts(); $this->markInstallationAsCompleted(); } $view->showNextStep = false; - - $this->session->currentStepDone = __FUNCTION__; $output = $view->render(); - $this->session->unsetAll(); - return $output; } + /** + * 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 @@ -555,51 +417,18 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $this->setupSystemCheckView($view); $infos = $view->infos; - $infos['extra'] = self::performAdminPageOnlySystemCheck(); + $infos['extra'] = SystemCheck::performAdminPageOnlySystemCheck(); $view->infos = $infos; return $view->render(); } - /** - * Instantiate access and log objects - */ - protected function initObjectsToCallAPI() - { - Piwik::setUserHasSuperUserAccess(); - } - - /** - * Write configuration file from session-store - */ - protected function createConfigFile($dbInfos) - { - $config = Config::getInstance(); - - $config->General = $this->session->general_infos; - $config->General['installation_in_progress'] = 1; - $config->database = $dbInfos; - $config->forceSave(); - - unset($this->session->general_infos); - } - - /** - * Write configuration file from session-store - */ - protected function markInstallationAsCompleted() - { - $config = Config::getInstance(); - unset($config->General['installation_in_progress']); - $config->forceSave(); - } - /** * Save language selection in session-store */ public function saveLanguage() { - $language = Common::getRequestVar('language'); + $language = $this->getParam('language'); LanguagesManager::setLanguageForSession($language); Url::redirectToReferrer(); } @@ -617,52 +446,70 @@ class Controller extends \Piwik\Plugin\ControllerAdmin return AssetManager::getInstance()->getCompiledBaseCss()->getContent(); } + private function getParam($name) + { + return Common::getRequestVar($name, false, 'string'); + } + /** - * The previous step is valid if it is either - * - any step before (OK to go back) - * - the current step (case when validating a form) - * If step is invalid, then exit. - * - * @param string $currentStep Current step + * Instantiate access and log objects */ - protected function checkPreviousStepIsValid($currentStep) + private function initObjectsToCallAPI() { - $error = false; - - if (empty($this->session->currentStepDone)) { - $error = true; - } else if ($currentStep == 'finished' && $this->session->currentStepDone == 'finished') { - // ok to refresh this page or use language selector - } else if ($currentStep == 'reuseTables' && in_array($this->session->currentStepDone, array('tablesCreation', 'reuseTables'))) { - // this is ok, we cannot add 'reuseTables' to steps as it would appear in the menu otherwise - } else { - if ($this->isFinishedInstallation()) { - $error = true; - } - - $steps = array_keys($this->steps); + Piwik::setUserHasSuperUserAccess(); + } - // the currentStep - $currentStepId = array_search($currentStep, $steps); + /** + * Write configuration file from session-store + */ + private function createConfigFile($dbInfos) + { + $config = Config::getInstance(); - // the step before - $previousStepId = array_search($this->session->currentStepDone, $steps); + // make sure DB sessions are used if the filesystem is NFS + if (Filesystem::checkIfFileSystemIsNFS()) { + $config->General['session_save_handler'] = 'dbtable'; + } + if (count($headers = ProxyHeaders::getProxyClientHeaders()) > 0) { + $config->General['proxy_client_headers'] = $headers; + } + if (count($headers = ProxyHeaders::getProxyHostHeaders()) > 0) { + $config->General['proxy_host_headers'] = $headers; + } + $config->General['salt'] = Common::generateUniqId(); + $config->General['installation_in_progress'] = 1; - // not OK if currentStepId > previous+1 - if ($currentStepId > $previousStepId + 1) { - $error = true; - } + $config->database = $dbInfos; + if (!DbHelper::isDatabaseConnectionUTF8()) { + $config->database['charset'] = 'utf8'; } - if ($error) { - \Piwik\Plugins\Login\Controller::clearSession(); - $message = Piwik::translate('Installation_ErrorInvalidState', - array('<br /><strong>', - '</strong>', - '<a href=\'' . Common::sanitizeInputValue(Url::getCurrentUrlWithoutFileName()) . '\'>', - '</a>') - ); - Piwik::exitWithErrorMessage($message); + $config->forceSave(); + + } + + private function checkPiwikIsNotInstalled() + { + if(!$this->isFinishedInstallation()) { + return; } + \Piwik\Plugins\Login\Controller::clearSession(); + $message = Piwik::translate('Installation_InvalidStateError', + array('<br /><strong>', + '</strong>', + '<a href=\'' . Common::sanitizeInputValue(Url::getCurrentUrlWithoutFileName()) . '\'>', + '</a>') + ); + Piwik::exitWithErrorMessage($message); + } + + /** + * Write configuration file from session-store + */ + private function markInstallationAsCompleted() + { + $config = Config::getInstance(); + unset($config->General['installation_in_progress']); + $config->forceSave(); } /** @@ -671,26 +518,13 @@ class Controller extends \Piwik\Plugin\ControllerAdmin * @param string $currentStep Current step * @return void */ - protected function redirectToNextStep($currentStep) + private function redirectToNextStep($currentStep, $parameters = array()) { $steps = array_keys($this->steps); - $this->session->currentStepDone = $currentStep; $nextStep = $steps[1 + array_search($currentStep, $steps)]; - Piwik::redirectToModule('Installation', $nextStep); + Piwik::redirectToModule('Installation', $nextStep, $parameters); } - /** - * Skip this step (typically to mark the current function as completed) - * - * @param string $step function name - */ - protected function skipThisStep($step) - { - $skipThisStep = $this->session->skipThisStep; - if (isset($skipThisStep[$step]) && $skipThisStep[$step]) { - $this->redirectToNextStep($step); - } - } /** * Extract host from URL @@ -699,7 +533,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin * * @return string|false */ - protected function extractHost($url) + private function extractHost($url) { $urlParts = parse_url($url); if (isset($urlParts['host']) && strlen($host = $urlParts['host'])) { @@ -712,7 +546,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin /** * Add trusted hosts */ - protected function addTrustedHosts() + private function addTrustedHosts($siteUrl) { $trustedHosts = array(); @@ -722,7 +556,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin } // extract host from first web site - if (($host = $this->extractHost(urldecode($this->session->site_url))) !== false) { + if (($host = $this->extractHost(urldecode($siteUrl))) !== false) { $trustedHosts[] = $host; } @@ -737,241 +571,6 @@ class Controller extends \Piwik\Plugin\ControllerAdmin } } - /** - * Get system information - */ - public static function getSystemInformation() - { - global $piwik_minimumPHPVersion; - $minimumMemoryLimit = Config::getInstance()->General['minimum_memory_limit']; - - $infos = array(); - - $infos['general_infos'] = array(); - - - - $directoriesToCheck = array( - '/tmp/', - '/tmp/assets/', - '/tmp/cache/', - '/tmp/climulti/', - '/tmp/latest/', - '/tmp/logs/', - '/tmp/sessions/', - '/tmp/tcpdf/', - '/tmp/templates_c/', - ); - - if (!DbHelper::isInstalled()) { - // at install, need /config to be writable (so we can create config.ini.php) - $directoriesToCheck[] = '/config/'; - } - - $infos['directories'] = Filechecks::checkDirectoriesWritable($directoriesToCheck); - - $infos['can_auto_update'] = Filechecks::canAutoUpdate(); - - self::initServerFilesForSecurity(); - - $infos['phpVersion_minimum'] = $piwik_minimumPHPVersion; - $infos['phpVersion'] = PHP_VERSION; - $infos['phpVersion_ok'] = version_compare($piwik_minimumPHPVersion, $infos['phpVersion']) === -1; - - // critical errors - $extensions = @get_loaded_extensions(); - $needed_extensions = array( - 'zlib', - 'SPL', - 'iconv', - 'json', - 'mbstring', - ); - // HHVM provides the required subset of Reflection but lists Reflections as missing - if (!defined('HHVM_VERSION')) { - $needed_extensions[] = 'Reflection'; - } - $infos['needed_extensions'] = $needed_extensions; - $infos['missing_extensions'] = array(); - foreach ($needed_extensions as $needed_extension) { - if (!in_array($needed_extension, $extensions)) { - $infos['missing_extensions'][] = $needed_extension; - } - } - - // Special case for mbstring - if (!function_exists('mb_get_info') - || ((int)ini_get('mbstring.func_overload')) != 0) { - $infos['missing_extensions'][] = 'mbstring'; - } - - $infos['pdo_ok'] = false; - if (in_array('PDO', $extensions)) { - $infos['pdo_ok'] = true; - } - - $infos['adapters'] = Adapter::getAdapters(); - - $needed_functions = array( - 'debug_backtrace', - 'create_function', - 'eval', - 'gzcompress', - 'gzuncompress', - 'pack', - ); - $infos['needed_functions'] = $needed_functions; - $infos['missing_functions'] = array(); - foreach ($needed_functions as $needed_function) { - if (!self::functionExists($needed_function)) { - $infos['missing_functions'][] = $needed_function; - } - } - - - // warnings - $desired_extensions = array( - 'json', - 'libxml', - 'dom', - 'SimpleXML', - ); - $infos['desired_extensions'] = $desired_extensions; - $infos['missing_desired_extensions'] = array(); - foreach ($desired_extensions as $desired_extension) { - if (!in_array($desired_extension, $extensions)) { - $infos['missing_desired_extensions'][] = $desired_extension; - } - } - $desired_functions = array( - 'set_time_limit', - 'mail', - 'parse_ini_file', - 'glob', - ); - $infos['desired_functions'] = $desired_functions; - $infos['missing_desired_functions'] = array(); - foreach ($desired_functions as $desired_function) { - if (!self::functionExists($desired_function)) { - $infos['missing_desired_functions'][] = $desired_function; - } - } - - $infos['openurl'] = Http::getTransportMethod(); - - $infos['gd_ok'] = SettingsServer::isGdExtensionEnabled(); - - - $serverSoftware = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : ''; - $infos['serverVersion'] = addslashes($serverSoftware); - $infos['serverOs'] = @php_uname(); - $infos['serverTime'] = date('H:i:s'); - - $infos['memoryMinimum'] = $minimumMemoryLimit; - - $infos['memory_ok'] = true; - $infos['memoryCurrent'] = ''; - - $raised = SettingsServer::raiseMemoryLimitIfNecessary(); - if (($memoryValue = SettingsServer::getMemoryLimitValue()) > 0) { - $infos['memoryCurrent'] = $memoryValue . 'M'; - $infos['memory_ok'] = $memoryValue >= $minimumMemoryLimit; - } - - $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(); - - $infos['tracker_status'] = Common::getRequestVar('trackerStatus', 0, 'int'); - - $infos['protocol'] = ProxyHeaders::getProtocolInformation(); - if (!\Piwik\ProxyHttp::isHttps() && $infos['protocol'] !== null) { - $infos['general_infos']['assume_secure_protocol'] = '1'; - } - if (count($headers = ProxyHeaders::getProxyClientHeaders()) > 0) { - $infos['general_infos']['proxy_client_headers'] = $headers; - } - if (count($headers = ProxyHeaders::getProxyHostHeaders()) > 0) { - $infos['general_infos']['proxy_host_headers'] = $headers; - } - - // check if filesystem is NFS, if it is file based sessions won't work properly - $infos['is_nfs'] = Filesystem::checkIfFileSystemIsNFS(); - $infos = self::enrichSystemChecks($infos); - - return $infos; - } - - /** - * @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']) - || !$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; - } - - /** - * 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. - */ - public 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; - } - /** * Utility function, sets up a view that will display system check info. * @@ -1007,67 +606,6 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $view->problemWithSomeDirectories = (false !== array_search(false, $view->infos['directories'])); } - /** - * 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; - } - - - 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'] = 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) . "')"); - } private function createSuperUser($login, $password, $email) { @@ -1109,7 +647,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin return $baseTablesInstalled >= $minimumCountPiwikTables; } - protected function deleteConfigFileIfNeeded() + private function deleteConfigFileIfNeeded() { $config = Config::getInstance(); if($config->existsLocalConfig()) { @@ -1117,4 +655,37 @@ class Controller extends \Piwik\Plugin\ControllerAdmin } } + /** + * @param $email + * @param $newsletterSecurity + * @param $newsletterCommunity + */ + protected function registerNewsletter($email, $newsletterSecurity, $newsletterCommunity) + { + $url = Config::getInstance()->General['api_service_url']; + $url .= '/1.0/subscribeNewsletter/'; + $params = array( + 'email' => $email, + 'security' => $newsletterSecurity, + 'community' => $newsletterCommunity, + 'url' => Url::getCurrentUrlWithoutQueryString(), + ); + if ($params['security'] == '1' + || $params['community'] == '1' + ) { + if (!isset($params['security'])) { + $params['security'] = '0'; + } + if (!isset($params['community'])) { + $params['community'] = '0'; + } + $url .= '?' . http_build_query($params, '', '&'); + try { + Http::sendHttpRequest($url, $timeout = 2); + } catch (Exception $e) { + // e.g., disable_functions = fsockopen; allow_url_open = Off + } + } + } + } diff --git a/plugins/Installation/SystemCheck.php b/plugins/Installation/SystemCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..e650c8004ee3b72b8e2d732bcb2e87b97eb66b03 --- /dev/null +++ b/plugins/Installation/SystemCheck.php @@ -0,0 +1,324 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Installation; + +use Piwik\Common; +use Piwik\Config; +use Piwik\Db\Adapter; +use Piwik\Db; +use Piwik\DbHelper; +use Piwik\Filechecks; +use Piwik\Filesystem; +use Piwik\Http; +use Piwik\Piwik; +use Piwik\Plugins\UserCountry\LocationProvider; +use Piwik\ProxyHeaders; +use Piwik\SettingsServer; + +class SystemCheck +{ + + /** + * Get system information + */ + public static function getSystemInformation() + { + global $piwik_minimumPHPVersion; + $minimumMemoryLimit = Config::getInstance()->General['minimum_memory_limit']; + + $infos = array(); + + $directoriesToCheck = array( + '/tmp/', + '/tmp/assets/', + '/tmp/cache/', + '/tmp/climulti/', + '/tmp/latest/', + '/tmp/logs/', + '/tmp/sessions/', + '/tmp/tcpdf/', + '/tmp/templates_c/', + ); + + if (!DbHelper::isInstalled()) { + // at install, need /config to be writable (so we can create config.ini.php) + $directoriesToCheck[] = '/config/'; + } + + $infos['directories'] = Filechecks::checkDirectoriesWritable($directoriesToCheck); + + $infos['can_auto_update'] = Filechecks::canAutoUpdate(); + + self::initServerFilesForSecurity(); + + $infos['phpVersion_minimum'] = $piwik_minimumPHPVersion; + $infos['phpVersion'] = PHP_VERSION; + $infos['phpVersion_ok'] = version_compare($piwik_minimumPHPVersion, $infos['phpVersion']) === -1; + + // critical errors + $extensions = @get_loaded_extensions(); + $needed_extensions = array( + 'zlib', + 'SPL', + 'iconv', + 'json', + 'mbstring', + ); + // HHVM provides the required subset of Reflection but lists Reflections as missing + if (!defined('HHVM_VERSION')) { + $needed_extensions[] = 'Reflection'; + } + $infos['needed_extensions'] = $needed_extensions; + $infos['missing_extensions'] = array(); + foreach ($needed_extensions as $needed_extension) { + if (!in_array($needed_extension, $extensions)) { + $infos['missing_extensions'][] = $needed_extension; + } + } + + // Special case for mbstring + if (!function_exists('mb_get_info') + || ((int)ini_get('mbstring.func_overload')) != 0) { + $infos['missing_extensions'][] = 'mbstring'; + } + + $infos['pdo_ok'] = false; + if (in_array('PDO', $extensions)) { + $infos['pdo_ok'] = true; + } + + $infos['adapters'] = Adapter::getAdapters(); + + $needed_functions = array( + 'debug_backtrace', + 'create_function', + 'eval', + 'gzcompress', + 'gzuncompress', + 'pack', + ); + $infos['needed_functions'] = $needed_functions; + $infos['missing_functions'] = array(); + foreach ($needed_functions as $needed_function) { + if (!self::functionExists($needed_function)) { + $infos['missing_functions'][] = $needed_function; + } + } + + // warnings + $desired_extensions = array( + 'json', + 'libxml', + 'dom', + 'SimpleXML', + ); + $infos['desired_extensions'] = $desired_extensions; + $infos['missing_desired_extensions'] = array(); + foreach ($desired_extensions as $desired_extension) { + if (!in_array($desired_extension, $extensions)) { + $infos['missing_desired_extensions'][] = $desired_extension; + } + } + $desired_functions = array( + 'set_time_limit', + 'mail', + 'parse_ini_file', + 'glob', + ); + $infos['desired_functions'] = $desired_functions; + $infos['missing_desired_functions'] = array(); + foreach ($desired_functions as $desired_function) { + if (!self::functionExists($desired_function)) { + $infos['missing_desired_functions'][] = $desired_function; + } + } + + $infos['openurl'] = Http::getTransportMethod(); + + $infos['gd_ok'] = SettingsServer::isGdExtensionEnabled(); + + + $serverSoftware = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : ''; + $infos['serverVersion'] = addslashes($serverSoftware); + $infos['serverOs'] = @php_uname(); + $infos['serverTime'] = date('H:i:s'); + + $infos['memoryMinimum'] = $minimumMemoryLimit; + + $infos['memory_ok'] = true; + $infos['memoryCurrent'] = ''; + + $raised = SettingsServer::raiseMemoryLimitIfNecessary(); + if (($memoryValue = SettingsServer::getMemoryLimitValue()) > 0) { + $infos['memoryCurrent'] = $memoryValue . 'M'; + $infos['memory_ok'] = $memoryValue >= $minimumMemoryLimit; + } + + $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(); + + $infos['tracker_status'] = Common::getRequestVar('trackerStatus', 0, 'int'); + + $infos['protocol'] = ProxyHeaders::getProtocolInformation(); + if (!\Piwik\ProxyHttp::isHttps() && $infos['protocol'] !== null) { + $infos['assume_secure_protocol'] = '1'; + } + + // check if filesystem is NFS, if it is file based sessions won't work properly + $infos['is_nfs'] = Filesystem::checkIfFileSystemIsNFS(); + $infos = self::enrichSystemChecks($infos); + + return $infos; + } + + /** + * @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']) + || !$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; + } + + /** + * 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. + */ + public 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; + } + + /** + * 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; + } + + + 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() + { + if (SettingsServer::isIIS()) { + ServerFilesGenerator::createWebConfigFiles(); + } else { + ServerFilesGenerator::createHtAccessFiles(); + } + ServerFilesGenerator::createWebRootFiles(); + } + +} \ No newline at end of file diff --git a/plugins/Installation/templates/_systemCheckSection.twig b/plugins/Installation/templates/_systemCheckSection.twig index 8f614df73e70da99a5281bd8b20d48370f69e8c3..c7e1d23e96094bafb393c814c8dbba8c23b46164 100755 --- a/plugins/Installation/templates/_systemCheckSection.twig +++ b/plugins/Installation/templates/_systemCheckSection.twig @@ -288,7 +288,7 @@ </td> </tr> {% endif %} - {% if infos.general_infos.assume_secure_protocol is defined %} + {% if infos.assume_secure_protocol is defined %} <tr> <td class="label">{{ 'Installation_SystemCheckSecureProtocol'|translate }}</td> <td> diff --git a/plugins/Installation/templates/databaseCheck.twig b/plugins/Installation/templates/databaseCheck.twig deleted file mode 100644 index 36ce2614e23270d22d9d96bc125a1892018868f5..0000000000000000000000000000000000000000 --- a/plugins/Installation/templates/databaseCheck.twig +++ /dev/null @@ -1,36 +0,0 @@ -{% extends '@Installation/layout.twig' %} - -{% block content %} -{% set ok %}<img src='plugins/Zeitgeist/images/ok.png' />{% endset %} -{% set error %}<img src='plugins/Zeitgeist/images/error.png' />{% endset %} -{% set warning %}<img src='plugins/Zeitgeist/images/warning.png' />{% endset %} -{% set link %}<img src='plugins/Zeitgeist/images/link.gif' />{% endset %} - -<h2>{{ 'Installation_DatabaseCheck'|translate }}</h2> - -<table class="infosServer"> - <tr> - <td class="label">{{ 'Installation_DatabaseServerVersion'|translate }}</td> - <td>{% if databaseVersionOk is defined %}{{ ok }}{% else %}{{ error }}{% endif %}</td> - </tr> - <tr> - <td class="label">{{ 'Installation_DatabaseClientVersion'|translate }}</td> - <td>{% if clientVersionWarning is defined %}{{ warning }}{% else %}{{ ok }}{% endif %}</td> - </tr> - {% if clientVersionWarning is defined %} - <tr> - <td colspan="2"> - <span style="font-size: small;color:#FF7F00;">{{ clientVersionWarning }}</span> - </td> - </tr> - {% endif %} - <tr> - <td class="label">{{ 'Installation_DatabaseCreation'|translate }}</td> - <td>{% if databaseCreated is defined %}{{ ok }}{% else %}{{ error }}{% endif %}</td> - </tr> -</table> - -<p> - {{ link }} <a href="?module=Proxy&action=redirect&url=http://piwik.org/docs/requirements/" target="_blank">{{ 'Installation_Requirements'|translate }}</a> -</p> -{% endblock %} \ No newline at end of file diff --git a/tests/PHPUnit/UI b/tests/PHPUnit/UI index 3666b7053577b468530eea5fb820fe90d6092c02..66597ff1e4b6ce6e9ebf556b6db608bbf744ee04 160000 --- a/tests/PHPUnit/UI +++ b/tests/PHPUnit/UI @@ -1 +1 @@ -Subproject commit 3666b7053577b468530eea5fb820fe90d6092c02 +Subproject commit 66597ff1e4b6ce6e9ebf556b6db608bbf744ee04