From: Philipp Holzer Date: Mon, 29 Oct 2018 17:44:39 +0000 (+0100) Subject: Refactoring Installation X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=f0382ab919b3848dd4dce9717a4f7cc7910c19d7;p=friendica.git Refactoring Installation - centralized installation - renamed Core\Install to Core\Installer - avoid using $a->data[] for states - removed unnecessary code --- diff --git a/src/Core/Console/AutomaticInstallation.php b/src/Core/Console/AutomaticInstallation.php index c4e542e762..491e826039 100644 --- a/src/Core/Console/AutomaticInstallation.php +++ b/src/Core/Console/AutomaticInstallation.php @@ -5,7 +5,7 @@ namespace Friendica\Core\Console; use Asika\SimpleConsole\Console; use Friendica\BaseObject; use Friendica\Core\Config; -use Friendica\Core\Install; +use Friendica\Core\Installer; use Friendica\Core\Theme; use Friendica\Database\DBA; use Friendica\Database\DBStructure; @@ -76,7 +76,7 @@ HELP; $a = BaseObject::getApp(); - $install = new Install(); + $installer = new Installer(); // if a config file is set, $config_file = $this->getOption(['f', 'file']); @@ -111,7 +111,7 @@ HELP; $tz = $this->getOption(['T', 'tz'], (!empty('FRIENDICA_TZ')) ? getenv('FRIENDICA_TZ') : ''); $lang = $this->getOption(['L', 'lang'], (!empty('FRIENDICA_LANG')) ? getenv('FRIENDICA_LANG') : ''); - $install->createConfig( + $installer->createConfig( $php_path, $url_path, ((!empty($db_port)) ? $db_host . ':' . $db_port : $db_host), @@ -130,14 +130,10 @@ HELP; // Check basic setup $this->out("Checking basic setup...\n"); - $checkResults = []; + $installer->resetChecks(); - $this->runBasicChecks($install); - - $checkResults['basic'] = $install->getChecks(); - $errorMessage = $this->extractErrors($checkResults['basic']); - - if ($errorMessage !== '') { + if (!$this->runBasicChecks($installer)) { + $errorMessage = $this->extractErrors($installer->getChecks()); throw new RuntimeException($errorMessage); } @@ -146,11 +142,10 @@ HELP; // Check database connection $this->out("Checking database...\n"); - $checkResults['db'] = array(); - $checkResults['db'][] = $this->runDatabaseCheck($db_host, $db_user, $db_pass, $db_data); - $errorMessage = $this->extractErrors($checkResults['db']); + $installer->resetChecks(); - if ($errorMessage !== '') { + if (!$installer->checkDB($db_host, $db_user, $db_pass, $db_data)) { + $errorMessage = $this->extractErrors($installer->getChecks()); throw new RuntimeException($errorMessage); } @@ -159,10 +154,11 @@ HELP; // Install database $this->out("Inserting data into database...\n"); - $checkResults['data'] = DBStructure::update(false, true, true); + $installer->resetChecks(); - if ($checkResults['data'] !== '') { - throw new RuntimeException("ERROR: DB Database creation error. Is the DB empty?\n"); + if (!$installer->installDatabase()) { + $errorMessage = $this->extractErrors($installer->getChecks()); + throw new RuntimeException($errorMessage); } $this->out(" Complete!\n\n"); @@ -182,16 +178,30 @@ HELP; } /** - * @param Install $install the Installer instance + * @param Installer $install the Installer instance + * + * @return bool true if checks were successfully, otherwise false */ - private function runBasicChecks(Install $install) + private function runBasicChecks(Installer $install) { + $checked = true; + $install->resetChecks(); - $install->checkFunctions(); - $install->checkImagick(); - $install->checkLocalIni(); - $install->checkSmarty3(); - $install->checkKeys(); + if (!$install->checkFunctions()) { + $checked = false; + } + if (!$install->checkImagick()) { + $checked = false; + } + if (!$install->checkLocalIni()) { + $checked = false; + } + if (!$install->checkSmarty3()) { + $checked = false; + } + if ($install->checkKeys()) { + $checked = false; + } if (!empty(Config::get('config', 'php_path'))) { if (!$install->checkPHP(Config::get('config', 'php_path'), true)) { @@ -202,32 +212,8 @@ HELP; } $this->out(" NOTICE: Not checking .htaccess/URL-Rewrite during CLI installation.\n"); - } - - /** - * @param $db_host - * @param $db_user - * @param $db_pass - * @param $db_data - * - * @return array - */ - private function runDatabaseCheck($db_host, $db_user, $db_pass, $db_data) - { - $result = array( - 'title' => 'MySQL Connection', - 'required' => true, - 'status' => true, - 'help' => '', - ); - - - if (!DBA::connect($db_host, $db_user, $db_pass, $db_data)) { - $result['status'] = false; - $result['help'] = 'Failed, please check your MySQL settings and credentials.'; - } - return $result; + return $checked; } /** diff --git a/src/Core/Install.php b/src/Core/Install.php deleted file mode 100644 index 0a97e93d84..0000000000 --- a/src/Core/Install.php +++ /dev/null @@ -1,511 +0,0 @@ -checks; - } - - /** - * Resets all checks - */ - public function resetChecks() - { - $this->checks = []; - } - - /** - * Install constructor. - * - */ - public function __construct() - { - $this->checks = []; - } - - /** - * Checks the current installation environment. There are optional and mandatory checks. - * - * @param string $baseurl The baseurl of Friendica - * @param string $phpath Optional path to the PHP binary - * - * @return bool if the check succeed - */ - public function checkAll($baseurl, $phpath = null) - { - $returnVal = true; - - if (isset($phpath)) { - if (!$this->checkPHP($phpath)) { - $returnVal = false; - } - } - - if (!$this->checkFunctions()) { - $returnVal = false; - } - - if (!$this->checkImagick()) { - $returnVal = false; - } - - if (!$this->checkLocalIni()) { - $returnVal = false; - } - - if (!$this->checkSmarty3()) { - $returnVal = false; - } - - if (!$this->checkKeys()) { - $returnVal = false; - } - - if (!$this->checkHtAccess($baseurl)) { - $returnVal = false; - } - - return $returnVal; - } - - /** - * Executes the installation of Friendica in the given environment. - * - Creates `config/local.ini.php` - * - Installs Database Structure - * - * @param string $phppath Path to the PHP-Binary (optional, if not set e.g. 'php' or '/usr/bin/php') - * @param string $urlpath Path based on the URL of Friendica (e.g. '/friendica') - * @param string $dbhost Hostname/IP of the Friendica Database - * @param string $dbuser Username of the Database connection credentials - * @param string $dbpass Password of the Database connection credentials - * @param string $dbdata Name of the Database - * @param string $timezone Timezone of the Friendica Installaton (e.g. 'Europe/Berlin') - * @param string $language 2-letter ISO 639-1 code (eg. 'en') - * @param string $adminmail Mail-Adress of the administrator - * @param string $basepath The basepath of Friendica - * - * @return bool|string true if the config was created, the text if something went wrong - */ - public function createConfig($phppath, $urlpath, $dbhost, $dbuser, $dbpass, $dbdata, $timezone, $language, $adminmail, $basepath) - { - $tpl = get_markup_template('local.ini.tpl'); - $txt = replace_macros($tpl,[ - '$phpath' => $phppath, - '$dbhost' => $dbhost, - '$dbuser' => $dbuser, - '$dbpass' => $dbpass, - '$dbdata' => $dbdata, - '$timezone' => $timezone, - '$language' => $language, - '$urlpath' => $urlpath, - '$adminmail' => $adminmail, - ]); - - $result = file_put_contents($basepath . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'local.ini.php', $txt); - - if (!$result) { - return $txt; - } else { - return true; - } - } - - /** - * Adds new checks to the array $checks - * - * @param string $title The title of the current check - * @param bool $status 1 = check passed, 0 = check not passed - * @param bool $required 1 = check is mandatory, 0 = check is optional - * @param string $help A help-string for the current check - * @param string $error_msg Optional. A error message, if the current check failed - */ - private function addCheck($title, $status, $required, $help, $error_msg = "") - { - array_push($this->checks, [ - 'title' => $title, - 'status' => $status, - 'required' => $required, - 'help' => $help, - 'error_msg' => $error_msg, - ]); - } - - /** - * PHP Check - * - * Checks the PHP environment. - * - * - Checks if a PHP binary is available - * - Checks if it is the CLI version - * - Checks if "register_argc_argv" is enabled - * - * @param string $phppath Optional. The Path to the PHP-Binary - * @param bool $required Optional. If set to true, the PHP-Binary has to exist (Default false) - * - * @return bool false if something required failed - */ - public function checkPHP($phppath = null, $required = false) - { - $passed = $passed2 = $passed3 = false; - if (isset($phppath)) { - $passed = file_exists($phppath); - } else { - $phppath = trim(shell_exec('which php')); - $passed = strlen($phppath); - } - - $help = ""; - if (!$passed) { - $help .= L10n::t('Could not find a command line version of PHP in the web server PATH.') . EOL; - $help .= L10n::t("If you don't have a command line version of PHP installed on your server, you will not be able to run the background processing. See 'Setup the worker'") . EOL; - $help .= EOL . EOL; - $tpl = get_markup_template('field_input.tpl'); - $help .= replace_macros($tpl, [ - '$field' => ['phpath', L10n::t('PHP executable path'), $phppath, L10n::t('Enter full path to php executable. You can leave this blank to continue the installation.')], - ]); - $phppath = ""; - } - - $this->addCheck(L10n::t('Command line PHP') . ($passed ? " ($phppath)" : ""), $passed, false, $help); - - if ($passed) { - $cmd = "$phppath -v"; - $result = trim(shell_exec($cmd)); - $passed2 = (strpos($result, "(cli)") !== false); - list($result) = explode("\n", $result); - $help = ""; - if (!$passed2) { - $help .= L10n::t("PHP executable is not the php cli binary \x28could be cgi-fgci version\x29") . EOL; - $help .= L10n::t('Found PHP version: ') . "$result"; - } - $this->addCheck(L10n::t('PHP cli binary'), $passed2, true, $help); - } else { - // return if it was required - return $required; - } - - if ($passed2) { - $str = autoname(8); - $cmd = "$phppath testargs.php $str"; - $result = trim(shell_exec($cmd)); - $passed3 = $result == $str; - $help = ""; - if (!$passed3) { - $help .= L10n::t('The command line version of PHP on your system does not have "register_argc_argv" enabled.') . EOL; - $help .= L10n::t('This is required for message delivery to work.'); - } else { - $this->phppath = $phppath; - } - - $this->addCheck(L10n::t('PHP register_argc_argv'), $passed3, true, $help); - } - - // passed2 & passed3 are required if first check passed - return $passed2 && $passed3; - } - - /** - * OpenSSL Check - * - * Checks the OpenSSL Environment - * - * - Checks, if the command "openssl_pkey_new" is available - * - * @return bool false if something required failed - */ - public function checkKeys() - { - $help = ''; - $res = false; - $status = true; - - if (function_exists('openssl_pkey_new')) { - $res = openssl_pkey_new([ - 'digest_alg' => 'sha1', - 'private_key_bits' => 4096, - 'encrypt_key' => false - ]); - } - - // Get private key - if (!$res) { - $help .= L10n::t('Error: the "openssl_pkey_new" function on this system is not able to generate encryption keys') . EOL; - $help .= L10n::t('If running under Windows, please see "http://www.php.net/manual/en/openssl.installation.php".'); - $status = false; - } - $this->addCheck(L10n::t('Generate encryption keys'), $res, true, $help); - - return $status; - } - - /** - * PHP basic function check - * - * @param string $name The name of the function - * @param string $title The (localized) title of the function - * @param string $help The (localized) help of the function - * @param boolean $required If true, this check is required - * - * @return bool false, if the check failed - */ - private function checkFunction($name, $title, $help, $required) - { - $currHelp = ''; - $status = true; - if (!function_exists($name)) { - $currHelp = $help; - $status = false; - } - $this->addCheck($title, $status, $required, $currHelp); - - return $status || (!$status && !$required); - } - - /** - * PHP functions Check - * - * Checks the following PHP functions - * - libCurl - * - GD Graphics - * - OpenSSL - * - PDO or MySQLi - * - mb_string - * - XML - * - iconv - * - POSIX - * - * @return bool false if something required failed - */ - public function checkFunctions() - { - $returnVal = true; - - $help = ''; - $status = true; - if (function_exists('apache_get_modules')) { - if (!in_array('mod_rewrite', apache_get_modules())) { - $help = L10n::t('Error: Apache webserver mod-rewrite module is required but not installed.'); - $status = false; - $returnVal = false; - } - } - $this->addCheck(L10n::t('Apache mod_rewrite module'), $status, true, $help); - - $help = ''; - $status = true; - if (!function_exists('mysqli_connect') && !class_exists('pdo')) { - $status = false; - $help = L10n::t('Error: PDO or MySQLi PHP module required but not installed.'); - $returnVal = false; - } else { - if (!function_exists('mysqli_connect') && class_exists('pdo') && !in_array('mysql', \PDO::getAvailableDrivers())) { - $status = false; - $help = L10n::t('Error: The MySQL driver for PDO is not installed.'); - $returnVal = false; - } - } - $this->addCheck(L10n::t('PDO or MySQLi PHP module'), $status, true, $help); - - // check for XML DOM Documents being able to be generated - $help = ''; - $status = true; - try { - $xml = new DOMDocument(); - } catch (Exception $e) { - $help = L10n::t('Error, XML PHP module required but not installed.'); - $status = false; - $returnVal = false; - } - $this->addCheck(L10n::t('XML PHP module'), $status, true, $help); - - $status = $this->checkFunction('curl_init', - L10n::t('libCurl PHP module'), - L10n::t('Error: libCURL PHP module required but not installed.'), - true - ); - $returnVal = $returnVal ? $status : false; - - $status = $this->checkFunction('imagecreatefromjpeg', - L10n::t('GD graphics PHP module'), - L10n::t('Error: GD graphics PHP module with JPEG support required but not installed.'), - true - ); - $returnVal = $returnVal ? $status : false; - - $status = $this->checkFunction('openssl_public_encrypt', - L10n::t('OpenSSL PHP module'), - L10n::t('Error: openssl PHP module required but not installed.'), - true - ); - $returnVal = $returnVal ? $status : false; - - $status = $this->checkFunction('mb_strlen', - L10n::t('mb_string PHP module'), - L10n::t('Error: mb_string PHP module required but not installed.'), - true - ); - $returnVal = $returnVal ? $status : false; - - $status = $this->checkFunction('iconv_strlen', - L10n::t('iconv PHP module'), - L10n::t('Error: iconv PHP module required but not installed.'), - true - ); - $returnVal = $returnVal ? $status : false; - - $status = $this->checkFunction('posix_kill', - L10n::t('POSIX PHP module'), - L10n::t('Error: POSIX PHP module required but not installed.'), - true - ); - $returnVal = $returnVal ? $status : false; - - return $returnVal; - } - - /** - * "config/local.ini.php" - Check - * - * Checks if it's possible to create the "config/local.ini.php" - * - * @return bool false if something required failed - */ - public function checkLocalIni() - { - $status = true; - $help = ""; - if ((file_exists('config/local.ini.php') && !is_writable('config/local.ini.php')) || - (!file_exists('config/local.ini.php') && !is_writable('.'))) { - - $status = false; - $help = L10n::t('The web installer needs to be able to create a file called "local.ini.php" in the "config" folder of your web server and it is unable to do so.') . EOL; - $help .= L10n::t('This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can.') . EOL; - $help .= L10n::t('At the end of this procedure, we will give you a text to save in a file named local.ini.php in your Friendica "config" folder.') . EOL; - $help .= L10n::t('You can alternatively skip this procedure and perform a manual installation. Please see the file "INSTALL.txt" for instructions.') . EOL; - } - - $this->addCheck(L10n::t('config/local.ini.php is writable'), $status, false, $help); - - // Local INI File is not required - return true; - } - - /** - * Smarty3 Template Check - * - * Checks, if the directory of Smarty3 is writable - * - * @return bool false if something required failed - */ - public function checkSmarty3() - { - $status = true; - $help = ""; - if (!is_writable('view/smarty3')) { - - $status = false; - $help = L10n::t('Friendica uses the Smarty3 template engine to render its web views. Smarty3 compiles templates to PHP to speed up rendering.') . EOL; - $help .= L10n::t('In order to store these compiled templates, the web server needs to have write access to the directory view/smarty3/ under the Friendica top level folder.') . EOL; - $help .= L10n::t("Please ensure that the user that your web server runs as \x28e.g. www-data\x29 has write access to this folder.") . EOL; - $help .= L10n::t("Note: as a security measure, you should give the web server write access to view/smarty3/ only--not the template files \x28.tpl\x29 that it contains.") . EOL; - } - - $this->addCheck(L10n::t('view/smarty3 is writable'), $status, true, $help); - - return $status; - } - - /** - * ".htaccess" - Check - * - * Checks, if "url_rewrite" is enabled in the ".htaccess" file - * - * @param string $baseurl The baseurl of the app - * @return bool false if something required failed - */ - public function checkHtAccess($baseurl) - { - $status = true; - $help = ""; - $error_msg = ""; - if (function_exists('curl_init')) { - $fetchResult = Network::fetchUrlFull($baseurl . "/install/testrewrite"); - - $url = normalise_link($baseurl . "/install/testrewrite"); - if ($fetchResult->getReturnCode() != 204) { - $fetchResult = Network::fetchUrlFull($url); - } - - if ($fetchResult->getReturnCode() != 204) { - $status = false; - $help = L10n::t('Url rewrite in .htaccess is not working. Make sure you copied .htaccess-dist to .htaccess.'); - $error_msg = []; - $error_msg['head'] = L10n::t('Error message from Curl when fetching'); - $error_msg['url'] = $fetchResult->getRedirectUrl(); - $error_msg['msg'] = $fetchResult->getError(); - } - - $this->addCheck(L10n::t('Url rewrite is working'), $status, true, $help, $error_msg); - } else { - // cannot check modrewrite if libcurl is not installed - /// @TODO Maybe issue warning here? - } - - return $status; - } - - /** - * Imagick Check - * - * Checks, if the imagick module is available - * - * @return bool false if something required failed - */ - public function checkImagick() - { - $imagick = false; - $gif = false; - - if (class_exists('Imagick')) { - $imagick = true; - $supported = Image::supportedTypes(); - if (array_key_exists('image/gif', $supported)) { - $gif = true; - } - } - if (!$imagick) { - $this->addCheck(L10n::t('ImageMagick PHP extension is not installed'), $imagick, false, ""); - } else { - $this->addCheck(L10n::t('ImageMagick PHP extension is installed'), $imagick, false, ""); - if ($imagick) { - $this->addCheck(L10n::t('ImageMagick supports GIF'), $gif, false, ""); - } - } - - // Imagick is not required - return true; - } -} diff --git a/src/Core/Installer.php b/src/Core/Installer.php new file mode 100644 index 0000000000..03d888b7ea --- /dev/null +++ b/src/Core/Installer.php @@ -0,0 +1,569 @@ +checks; + } + + /** + * Resets all checks + */ + public function resetChecks() + { + $this->checks = []; + } + + /** + * Install constructor. + * + */ + public function __construct() + { + $this->checks = []; + } + + /** + * Checks the current installation environment. There are optional and mandatory checks. + * + * @param string $baseurl The baseurl of Friendica + * @param string $phpath Optional path to the PHP binary + * + * @return bool if the check succeed + */ + public function checkEnvironment($baseurl, $phpath = null) + { + $returnVal = true; + + if (isset($phpath)) { + if (!$this->checkPHP($phpath)) { + $returnVal = false; + } + } + + if (!$this->checkFunctions()) { + $returnVal = false; + } + + if (!$this->checkImagick()) { + $returnVal = false; + } + + if (!$this->checkLocalIni()) { + $returnVal = false; + } + + if (!$this->checkSmarty3()) { + $returnVal = false; + } + + if (!$this->checkKeys()) { + $returnVal = false; + } + + if (!$this->checkHtAccess($baseurl)) { + $returnVal = false; + } + + return $returnVal; + } + + /** + * Executes the installation of Friendica in the given environment. + * - Creates `config/local.ini.php` + * - Installs Database Structure + * + * @param string $phppath Path to the PHP-Binary (optional, if not set e.g. 'php' or '/usr/bin/php') + * @param string $urlpath Path based on the URL of Friendica (e.g. '/friendica') + * @param string $dbhost Hostname/IP of the Friendica Database + * @param string $dbuser Username of the Database connection credentials + * @param string $dbpass Password of the Database connection credentials + * @param string $dbdata Name of the Database + * @param string $timezone Timezone of the Friendica Installaton (e.g. 'Europe/Berlin') + * @param string $language 2-letter ISO 639-1 code (eg. 'en') + * @param string $adminmail Mail-Adress of the administrator + * @param string $basepath The basepath of Friendica + * + * @return bool true if the config was created, otherwise false + */ + public function createConfig($phppath, $urlpath, $dbhost, $dbuser, $dbpass, $dbdata, $timezone, $language, $adminmail, $basepath) + { + $tpl = get_markup_template('local.ini.tpl'); + $txt = replace_macros($tpl, [ + '$phpath' => $phppath, + '$dbhost' => $dbhost, + '$dbuser' => $dbuser, + '$dbpass' => $dbpass, + '$dbdata' => $dbdata, + '$timezone' => $timezone, + '$language' => $language, + '$urlpath' => $urlpath, + '$adminmail' => $adminmail, + ]); + + $result = file_put_contents($basepath . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'local.ini.php', $txt); + + if (!$result) { + $this->addCheck(L10n::t('The database configuration file "config/local.ini.php" could not be written. Please use the enclosed text to create a configuration file in your web server root.'), false, false, htmlentities($txt, ENT_COMPAT, 'UTF-8')); + } + + return $result; + } + + /*** + * Installs the DB-Scheme for Friendica + * + * @return bool true if the installation was successful, otherwise false + */ + public function installDatabase() + { + $result = DBStructure::update(false, true, true); + + if ($result) { + $txt = L10n::t('You may need to import the file "database.sql" manually using phpmyadmin or mysql.') . EOL; + $txt .= L10n::t('Please see the file "INSTALL.txt".'); + + $this->addCheck($txt, false, true, htmlentities($result, ENT_COMPAT, 'UTF-8')); + + return false; + } + + return true; + } + + /** + * Adds new checks to the array $checks + * + * @param string $title The title of the current check + * @param bool $status 1 = check passed, 0 = check not passed + * @param bool $required 1 = check is mandatory, 0 = check is optional + * @param string $help A help-string for the current check + * @param string $error_msg Optional. A error message, if the current check failed + */ + private function addCheck($title, $status, $required, $help, $error_msg = "") + { + array_push($this->checks, [ + 'title' => $title, + 'status' => $status, + 'required' => $required, + 'help' => $help, + 'error_msg' => $error_msg, + ]); + } + + /** + * PHP Check + * + * Checks the PHP environment. + * + * - Checks if a PHP binary is available + * - Checks if it is the CLI version + * - Checks if "register_argc_argv" is enabled + * + * @param string $phppath Optional. The Path to the PHP-Binary + * @param bool $required Optional. If set to true, the PHP-Binary has to exist (Default false) + * + * @return bool false if something required failed + */ + public function checkPHP($phppath = null, $required = false) + { + $passed = $passed2 = $passed3 = false; + if (isset($phppath)) { + $passed = file_exists($phppath); + } else { + $phppath = trim(shell_exec('which php')); + $passed = strlen($phppath); + } + + $help = ""; + if (!$passed) { + $help .= L10n::t('Could not find a command line version of PHP in the web server PATH.') . EOL; + $help .= L10n::t("If you don't have a command line version of PHP installed on your server, you will not be able to run the background processing. See 'Setup the worker'") . EOL; + $help .= EOL . EOL; + $tpl = get_markup_template('field_input.tpl'); + $help .= replace_macros($tpl, [ + '$field' => ['phpath', L10n::t('PHP executable path'), $phppath, L10n::t('Enter full path to php executable. You can leave this blank to continue the installation.')], + ]); + $phppath = ""; + } + + $this->addCheck(L10n::t('Command line PHP') . ($passed ? " ($phppath)" : ""), $passed, false, $help); + + if ($passed) { + $cmd = "$phppath -v"; + $result = trim(shell_exec($cmd)); + $passed2 = (strpos($result, "(cli)") !== false); + list($result) = explode("\n", $result); + $help = ""; + if (!$passed2) { + $help .= L10n::t("PHP executable is not the php cli binary \x28could be cgi-fgci version\x29") . EOL; + $help .= L10n::t('Found PHP version: ') . "$result"; + } + $this->addCheck(L10n::t('PHP cli binary'), $passed2, true, $help); + } else { + // return if it was required + return $required; + } + + if ($passed2) { + $str = autoname(8); + $cmd = "$phppath testargs.php $str"; + $result = trim(shell_exec($cmd)); + $passed3 = $result == $str; + $help = ""; + if (!$passed3) { + $help .= L10n::t('The command line version of PHP on your system does not have "register_argc_argv" enabled.') . EOL; + $help .= L10n::t('This is required for message delivery to work.'); + } else { + $this->phppath = $phppath; + } + + $this->addCheck(L10n::t('PHP register_argc_argv'), $passed3, true, $help); + } + + // passed2 & passed3 are required if first check passed + return $passed2 && $passed3; + } + + /** + * OpenSSL Check + * + * Checks the OpenSSL Environment + * + * - Checks, if the command "openssl_pkey_new" is available + * + * @return bool false if something required failed + */ + public function checkKeys() + { + $help = ''; + $res = false; + $status = true; + + if (function_exists('openssl_pkey_new')) { + $res = openssl_pkey_new([ + 'digest_alg' => 'sha1', + 'private_key_bits' => 4096, + 'encrypt_key' => false + ]); + } + + // Get private key + if (!$res) { + $help .= L10n::t('Error: the "openssl_pkey_new" function on this system is not able to generate encryption keys') . EOL; + $help .= L10n::t('If running under Windows, please see "http://www.php.net/manual/en/openssl.installation.php".'); + $status = false; + } + $this->addCheck(L10n::t('Generate encryption keys'), $res, true, $help); + + return $status; + } + + /** + * PHP basic function check + * + * @param string $name The name of the function + * @param string $title The (localized) title of the function + * @param string $help The (localized) help of the function + * @param boolean $required If true, this check is required + * + * @return bool false, if the check failed + */ + private function checkFunction($name, $title, $help, $required) + { + $currHelp = ''; + $status = true; + if (!function_exists($name)) { + $currHelp = $help; + $status = false; + } + $this->addCheck($title, $status, $required, $currHelp); + + return $status || (!$status && !$required); + } + + /** + * PHP functions Check + * + * Checks the following PHP functions + * - libCurl + * - GD Graphics + * - OpenSSL + * - PDO or MySQLi + * - mb_string + * - XML + * - iconv + * - POSIX + * + * @return bool false if something required failed + */ + public function checkFunctions() + { + $returnVal = true; + + $help = ''; + $status = true; + if (function_exists('apache_get_modules')) { + if (!in_array('mod_rewrite', apache_get_modules())) { + $help = L10n::t('Error: Apache webserver mod-rewrite module is required but not installed.'); + $status = false; + $returnVal = false; + } + } + $this->addCheck(L10n::t('Apache mod_rewrite module'), $status, true, $help); + + $help = ''; + $status = true; + if (!function_exists('mysqli_connect') && !class_exists('pdo')) { + $status = false; + $help = L10n::t('Error: PDO or MySQLi PHP module required but not installed.'); + $returnVal = false; + } else { + if (!function_exists('mysqli_connect') && class_exists('pdo') && !in_array('mysql', \PDO::getAvailableDrivers())) { + $status = false; + $help = L10n::t('Error: The MySQL driver for PDO is not installed.'); + $returnVal = false; + } + } + $this->addCheck(L10n::t('PDO or MySQLi PHP module'), $status, true, $help); + + // check for XML DOM Documents being able to be generated + $help = ''; + $status = true; + try { + $xml = new DOMDocument(); + } catch (Exception $e) { + $help = L10n::t('Error, XML PHP module required but not installed.'); + $status = false; + $returnVal = false; + } + $this->addCheck(L10n::t('XML PHP module'), $status, true, $help); + + $status = $this->checkFunction('curl_init', + L10n::t('libCurl PHP module'), + L10n::t('Error: libCURL PHP module required but not installed.'), + true + ); + $returnVal = $returnVal ? $status : false; + + $status = $this->checkFunction('imagecreatefromjpeg', + L10n::t('GD graphics PHP module'), + L10n::t('Error: GD graphics PHP module with JPEG support required but not installed.'), + true + ); + $returnVal = $returnVal ? $status : false; + + $status = $this->checkFunction('openssl_public_encrypt', + L10n::t('OpenSSL PHP module'), + L10n::t('Error: openssl PHP module required but not installed.'), + true + ); + $returnVal = $returnVal ? $status : false; + + $status = $this->checkFunction('mb_strlen', + L10n::t('mb_string PHP module'), + L10n::t('Error: mb_string PHP module required but not installed.'), + true + ); + $returnVal = $returnVal ? $status : false; + + $status = $this->checkFunction('iconv_strlen', + L10n::t('iconv PHP module'), + L10n::t('Error: iconv PHP module required but not installed.'), + true + ); + $returnVal = $returnVal ? $status : false; + + $status = $this->checkFunction('posix_kill', + L10n::t('POSIX PHP module'), + L10n::t('Error: POSIX PHP module required but not installed.'), + true + ); + $returnVal = $returnVal ? $status : false; + + return $returnVal; + } + + /** + * "config/local.ini.php" - Check + * + * Checks if it's possible to create the "config/local.ini.php" + * + * @return bool false if something required failed + */ + public function checkLocalIni() + { + $status = true; + $help = ""; + if ((file_exists('config/local.ini.php') && !is_writable('config/local.ini.php')) || + (!file_exists('config/local.ini.php') && !is_writable('.'))) { + + $status = false; + $help = L10n::t('The web installer needs to be able to create a file called "local.ini.php" in the "config" folder of your web server and it is unable to do so.') . EOL; + $help .= L10n::t('This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can.') . EOL; + $help .= L10n::t('At the end of this procedure, we will give you a text to save in a file named local.ini.php in your Friendica "config" folder.') . EOL; + $help .= L10n::t('You can alternatively skip this procedure and perform a manual installation. Please see the file "INSTALL.txt" for instructions.') . EOL; + } + + $this->addCheck(L10n::t('config/local.ini.php is writable'), $status, false, $help); + + // Local INI File is not required + return true; + } + + /** + * Smarty3 Template Check + * + * Checks, if the directory of Smarty3 is writable + * + * @return bool false if something required failed + */ + public function checkSmarty3() + { + $status = true; + $help = ""; + if (!is_writable('view/smarty3')) { + + $status = false; + $help = L10n::t('Friendica uses the Smarty3 template engine to render its web views. Smarty3 compiles templates to PHP to speed up rendering.') . EOL; + $help .= L10n::t('In order to store these compiled templates, the web server needs to have write access to the directory view/smarty3/ under the Friendica top level folder.') . EOL; + $help .= L10n::t("Please ensure that the user that your web server runs as \x28e.g. www-data\x29 has write access to this folder.") . EOL; + $help .= L10n::t("Note: as a security measure, you should give the web server write access to view/smarty3/ only--not the template files \x28.tpl\x29 that it contains.") . EOL; + } + + $this->addCheck(L10n::t('view/smarty3 is writable'), $status, true, $help); + + return $status; + } + + /** + * ".htaccess" - Check + * + * Checks, if "url_rewrite" is enabled in the ".htaccess" file + * + * @param string $baseurl The baseurl of the app + * @return bool false if something required failed + */ + public function checkHtAccess($baseurl) + { + $status = true; + $help = ""; + $error_msg = ""; + if (function_exists('curl_init')) { + $fetchResult = Network::fetchUrlFull($baseurl . "/install/testrewrite"); + + $url = normalise_link($baseurl . "/install/testrewrite"); + if ($fetchResult->getReturnCode() != 204) { + $fetchResult = Network::fetchUrlFull($url); + } + + if ($fetchResult->getReturnCode() != 204) { + $status = false; + $help = L10n::t('Url rewrite in .htaccess is not working. Make sure you copied .htaccess-dist to .htaccess.'); + $error_msg = []; + $error_msg['head'] = L10n::t('Error message from Curl when fetching'); + $error_msg['url'] = $fetchResult->getRedirectUrl(); + $error_msg['msg'] = $fetchResult->getError(); + } + + $this->addCheck(L10n::t('Url rewrite is working'), $status, true, $help, $error_msg); + } else { + // cannot check modrewrite if libcurl is not installed + /// @TODO Maybe issue warning here? + } + + return $status; + } + + /** + * Imagick Check + * + * Checks, if the imagick module is available + * + * @return bool false if something required failed + */ + public function checkImagick() + { + $imagick = false; + $gif = false; + + if (class_exists('Imagick')) { + $imagick = true; + $supported = Image::supportedTypes(); + if (array_key_exists('image/gif', $supported)) { + $gif = true; + } + } + if (!$imagick) { + $this->addCheck(L10n::t('ImageMagick PHP extension is not installed'), $imagick, false, ""); + } else { + $this->addCheck(L10n::t('ImageMagick PHP extension is installed'), $imagick, false, ""); + if ($imagick) { + $this->addCheck(L10n::t('ImageMagick supports GIF'), $gif, false, ""); + } + } + + // Imagick is not required + return true; + } + + /** + * Checking the Database connection and if it is available for the current installation + * + * @param string $dbhost Hostname/IP of the Friendica Database + * @param string $dbuser Username of the Database connection credentials + * @param string $dbpass Password of the Database connection credentials + * @param string $dbdata Name of the Database + * + * @return bool true if the check was successful, otherwise false + */ + public function checkDB($dbhost, $dbuser, $dbpass, $dbdata) + { + require_once 'include/dba.php'; + if (!DBA::connect($dbhost, $dbuser, $dbpass, $dbdata)) { + $this->addCheck(L10n::t('Could not connect to database.'), false, true, ''); + + return false; + } + + if (DBA::connected()) { + if (DBA::count('user') > 0) { + $this->addCheck(L10n::t('Database already in use.'), false, true, ''); + + return false; + } + } + + return true; + } +} diff --git a/src/Module/Install.php b/src/Module/Install.php index 3622559744..e09f23a18d 100644 --- a/src/Module/Install.php +++ b/src/Module/Install.php @@ -29,16 +29,16 @@ class Install extends BaseModule */ const FINISHED = 4; - // Default values for the install page - const DEFAULT_LANG = 'en'; - const DEFAULT_TZ = 'America/Los_Angeles'; - const DEFAULT_HOST = 'localhost'; - /** * @var int The current step of the wizard */ private static $currentWizardStep; + /** + * @var Core\Installer The installer + */ + private static $installer; + public static function init() { $a = self::getApp(); @@ -52,9 +52,9 @@ class Install extends BaseModule // We overwrite current theme css, because during install we may not have a working mod_rewrite // so we may not have a css at all. Here we set a static css file for the install procedure pages - $a->setConfigValue('system', 'value', '../install'); $a->theme['stylesheet'] = $a->getBaseURL() . '/view/install/style.css'; + self::$installer = new Core\Installer(); self::$currentWizardStep = defaults($_POST, 'pass', self::SYSTEM_CHECK); } @@ -66,56 +66,44 @@ class Install extends BaseModule case self::SYSTEM_CHECK: case self::DATABASE_CONFIG: // Nothing to do in these steps - return; + break; case self::SITE_SETTINGS: - $dbhost = notags(trim(defaults($_POST, 'dbhost', self::DEFAULT_HOST))); + $dbhost = notags(trim(defaults($_POST, 'dbhost', Core\Installer::DEFAULT_HOST))); $dbuser = notags(trim(defaults($_POST, 'dbuser', ''))); $dbpass = notags(trim(defaults($_POST, 'dbpass', ''))); $dbdata = notags(trim(defaults($_POST, 'dbdata', ''))); - require_once 'include/dba.php'; - if (!DBA::connect($dbhost, $dbuser, $dbpass, $dbdata)) { - $a->data['db_conn_failed'] = true; + // If we cannot connect to the database, return to the previous step + if (!self::$installer->checkDB($dbhost, $dbuser, $dbpass, $dbdata)) { + self::$currentWizardStep = self::DATABASE_CONFIG; } - return; + break; case self::FINISHED: $urlpath = $a->getURLPath(); - $dbhost = notags(trim(defaults($_POST, 'dbhost', self::DEFAULT_HOST))); + $dbhost = notags(trim(defaults($_POST, 'dbhost', Core\Installer::DEFAULT_HOST))); $dbuser = notags(trim(defaults($_POST, 'dbuser', ''))); $dbpass = notags(trim(defaults($_POST, 'dbpass', ''))); $dbdata = notags(trim(defaults($_POST, 'dbdata', ''))); $phpath = notags(trim(defaults($_POST, 'phpath', ''))); - $timezone = notags(trim(defaults($_POST, 'timezone', self::DEFAULT_TZ))); - $language = notags(trim(defaults($_POST, 'language', self::DEFAULT_LANG))); + $timezone = notags(trim(defaults($_POST, 'timezone', Core\Installer::DEFAULT_TZ))); + $language = notags(trim(defaults($_POST, 'language', Core\Installer::DEFAULT_LANG))); $adminmail = notags(trim(defaults($_POST, 'adminmail', ''))); - // connect to db - DBA::connect($dbhost, $dbuser, $dbpass, $dbdata); - - $install = new Core\Install(); - - $errors = $install->createConfig($phpath, $urlpath, $dbhost, $dbuser, $dbpass, $dbdata, $timezone, $language, $adminmail, $a->getBasePath()); - - if ($errors !== true) { - $a->data['txt'] = $errors; - return; + // If we cannot connect to the database, return to the Database config wizard + if (!self::$installer->checkDB($dbhost, $dbuser, $dbpass, $dbdata)) { + self::$currentWizardStep = self::DATABASE_CONFIG; } - $errors = DBStructure::update(false, true, true); - - if ($errors) { - $a->data['db_failed'] = $errors; - } else { - $a->data['db_installed'] = true; + if (!self::$installer->createConfig($phpath, $urlpath, $dbhost, $dbuser, $dbpass, $dbdata, $timezone, $language, $adminmail, $a->getBasePath())) { + return; } - return; + self::$installer->installDatabase(); - default: - return; + break; } } @@ -126,20 +114,18 @@ class Install extends BaseModule $output = ''; $install_title = L10n::t('Friendica Communctions Server - Setup'); - $wizard_status = self::checkWizardStatus($a); switch (self::$currentWizardStep) { case self::SYSTEM_CHECK: $phppath = defaults($_POST, 'phpath', null); - $install = new Core\Install(); - $status = $install->checkAll($a->getBaseURL(), $phppath); + $status = self::$installer->checkEnvironment($a->getBaseURL(), $phppath); $tpl = get_markup_template('install_checks.tpl'); $output .= replace_macros($tpl, [ '$title' => $install_title, '$pass' => L10n::t('System check'), - '$checks' => $install->getChecks(), + '$checks' => self::$installer->getChecks(), '$passed' => $status, '$see_install' => L10n::t('Please see the file "Install.txt".'), '$next' => L10n::t('Next'), @@ -150,12 +136,12 @@ class Install extends BaseModule break; case self::DATABASE_CONFIG: - $dbhost = notags(trim(defaults($_POST, 'dbhost' , self::DEFAULT_HOST))); - $dbuser = notags(trim(defaults($_POST, 'dbuser' , ''))); - $dbpass = notags(trim(defaults($_POST, 'dbpass' , ''))); - $dbdata = notags(trim(defaults($_POST, 'dbdata' , ''))); - $phpath = notags(trim(defaults($_POST, 'phpath' , ''))); - $adminmail = notags(trim(defaults($_POST, 'adminmail', ''))); + $dbhost = notags(trim(defaults($_POST, 'dbhost' , Core\Installer::DEFAULT_HOST))); + $dbuser = notags(trim(defaults($_POST, 'dbuser' , '' ))); + $dbpass = notags(trim(defaults($_POST, 'dbpass' , '' ))); + $dbdata = notags(trim(defaults($_POST, 'dbdata' , '' ))); + $phpath = notags(trim(defaults($_POST, 'phpath' , '' ))); + $adminmail = notags(trim(defaults($_POST, 'adminmail', '' ))); $tpl = get_markup_template('install_db.tpl'); $output .= replace_macros($tpl, [ @@ -164,7 +150,7 @@ class Install extends BaseModule '$info_01' => L10n::t('In order to install Friendica we need to know how to connect to your database.'), '$info_02' => L10n::t('Please contact your hosting provider or site administrator if you have questions about these settings.'), '$info_03' => L10n::t('The database you specify below should already exist. If it does not, please create it before continuing.'), - '$status' => $wizard_status, + 'checks' => self::$installer->getChecks(), '$dbhost' => ['dbhost', L10n::t('Database Server Name'), $dbhost, @@ -199,24 +185,25 @@ class Install extends BaseModule '$submit' => L10n::t('Submit') ]); break; + case self::SITE_SETTINGS: - $dbhost = notags(trim(defaults($_POST, 'dbhost', self::DEFAULT_HOST))); - $dbuser = notags(trim(defaults($_POST, 'dbuser', '' ))); - $dbpass = notags(trim(defaults($_POST, 'dbpass', '' ))); - $dbdata = notags(trim(defaults($_POST, 'dbdata', '' ))); - $phpath = notags(trim(defaults($_POST, 'phpath', '' ))); + $dbhost = notags(trim(defaults($_POST, 'dbhost', Core\Installer::DEFAULT_HOST))); + $dbuser = notags(trim(defaults($_POST, 'dbuser', '' ))); + $dbpass = notags(trim(defaults($_POST, 'dbpass', '' ))); + $dbdata = notags(trim(defaults($_POST, 'dbdata', '' ))); + $phpath = notags(trim(defaults($_POST, 'phpath', '' ))); $adminmail = notags(trim(defaults($_POST, 'adminmail', ''))); - $timezone = defaults($_POST, 'timezone', self::DEFAULT_TZ); + $timezone = defaults($_POST, 'timezone', Core\Installer::DEFAULT_TZ); /* Installed langs */ $lang_choices = L10n::getAvailableLanguages(); $tpl = get_markup_template('install_settings.tpl'); $output .= replace_macros($tpl, [ '$title' => $install_title, + '$checks' => self::$installer->getChecks(), '$pass' => L10n::t('Site settings'), - '$status' => $wizard_status, '$dbhost' => $dbhost, '$dbuser' => $dbuser, '$dbpass' => $dbpass, @@ -225,8 +212,8 @@ class Install extends BaseModule '$adminmail' => ['adminmail', L10n::t('Site administrator email address'), $adminmail, L10n::t('Your account email address must match this in order to use the web admin panel.'), 'required', 'autofocus', 'email'], '$timezone' => Temporal::getTimezoneField('timezone', L10n::t('Please select a default timezone for your website'), $timezone, ''), '$language' => ['language', - L10n::t('System Language:'), # - self::DEFAULT_LANG, + L10n::t('System Language:'), + Core\Installer::DEFAULT_LANG, L10n::t('Set the default language for your Friendica installation interface and to send emails.'), $lang_choices], '$baseurl' => $a->getBaseURL(), @@ -237,28 +224,18 @@ class Install extends BaseModule case self::FINISHED: $db_return_text = ""; - if (defaults($a->data, 'db_installed', false)) { + if (count(self::$installer->getChecks()) == 0) { $txt = '

'; $txt .= L10n::t('Your Friendica site database has been installed.') . EOL; $db_return_text .= $txt; } - if (defaults($a->data, 'db_failed', false)) { - $txt = L10n::t('You may need to import the file "database.sql" manually using phpmyadmin or mysql.') . EOL; - $txt .= L10n::t('Please see the file "INSTALL.txt".') . EOL ."


"; - $txt .= "
".$a->data['db_failed'] . "
". EOL; - $db_return_text .= $txt; - } - - if (isset($a->data['txt']) && strlen($a->data['txt'])) { - $db_return_text .= self::manualConfig($a); - } - - $tpl = get_markup_template('install.tpl'); + $tpl = get_markup_template('install_finished.tpl'); $output .= replace_macros($tpl, [ - '$title' => $install_title, - '$pass' => "", - '$text' => $db_return_text . self::whatNext($a), + '$title' => $install_title, + '$checks' => self::$installer->getChecks(), + '$pass' => L10n::t('Installation finished'), + '$text' => $db_return_text . self::whatNext($a), ]); break; @@ -267,50 +244,6 @@ class Install extends BaseModule return $output; } - /** - * @param App $a The global Friendica App - * - * @return string The status of Wizard steps - */ - private static function checkWizardStatus($a) - { - $wizardStatus = ""; - - if (defaults($a->data, 'db_conn_failed', false)) { - self::$currentWizardStep = 2; - $wizardStatus = L10n::t('Could not connect to database.'); - } - - if (defaults($a->data, 'db_create_failed', false)) { - self::$currentWizardStep = 2; - $wizardStatus = L10n::t('Could not create table.'); - } - - if (DBA::connected()) { - if (DBA::count('user')) { - self::$currentWizardStep = 2; - $wizardStatus = L10n::t('Database already in use.'); - } - } - - return $wizardStatus; - } - - /** - * Creates the text for manual config - * - * @param App $a The global App - * - * @return string The manual config text - */ - private static function manualConfig($a) - { - $data = htmlentities($a->data['txt'], ENT_COMPAT, 'UTF-8'); - $output = L10n::t('The database configuration file "config/local.ini.php" could not be written. Please use the enclosed text to create a configuration file in your web server root.'); - $output .= ""; - return $output; - } - /** * Creates the text for the next steps * diff --git a/tests/src/Core/InstallTest.php b/tests/src/Core/InstallTest.php deleted file mode 100644 index b44104d5f8..0000000000 --- a/tests/src/Core/InstallTest.php +++ /dev/null @@ -1,354 +0,0 @@ -setUpVfsDir(); - } - - private function assertCheckExist($position, $title, $help, $status, $required, $assertionArray) - { - $this->assertArraySubset([$position => [ - 'title' => $title, - 'status' => $status, - 'required' => $required, - 'error_msg' => null, - 'help' => $help] - ], $assertionArray); - } - - /** - * Replaces function_exists results with given mocks - * - * @param array $functions a list from function names and their result - */ - private function setFunctions($functions) - { - global $phpMock; - $phpMock['function_exists'] = function($function) use ($functions) { - foreach ($functions as $name => $value) { - if ($function == $name) { - return $value; - } - } - return '__phpunit_continue__'; - }; - } - - /** - * Replaces class_exist results with given mocks - * - * @param array $classes a list from class names and their results - */ - private function setClasses($classes) - { - global $phpMock; - $phpMock['class_exists'] = function($class) use ($classes) { - foreach ($classes as $name => $value) { - if ($class == $name) { - return $value; - } - } - return '__phpunit_continue__'; - }; - } - - /** - * @small - */ - public function testCheckKeys() - { - $this->setFunctions(['openssl_pkey_new' => false]); - $install = new Install(); - $this->assertFalse($install->checkKeys()); - - $this->setFunctions(['openssl_pkey_new' => true]); - $install = new Install(); - $this->assertTrue($install->checkKeys()); - } - - /** - * @small - */ - public function testCheckFunctions() - { - $this->setFunctions(['curl_init' => false]); - $install = new Install(); - $this->assertFalse($install->checkFunctions()); - $this->assertCheckExist(3, - L10n::t('libCurl PHP module'), - L10n::t('Error: libCURL PHP module required but not installed.'), - false, - true, - $install->getChecks()); - - $this->setFunctions(['imagecreatefromjpeg' => false]); - $install = new Install(); - $this->assertFalse($install->checkFunctions()); - $this->assertCheckExist(4, - L10n::t('GD graphics PHP module'), - L10n::t('Error: GD graphics PHP module with JPEG support required but not installed.'), - false, - true, - $install->getChecks()); - - $this->setFunctions(['openssl_public_encrypt' => false]); - $install = new Install(); - $this->assertFalse($install->checkFunctions()); - $this->assertCheckExist(5, - L10n::t('OpenSSL PHP module'), - L10n::t('Error: openssl PHP module required but not installed.'), - false, - true, - $install->getChecks()); - - $this->setFunctions(['mb_strlen' => false]); - $install = new Install(); - $this->assertFalse($install->checkFunctions()); - $this->assertCheckExist(6, - L10n::t('mb_string PHP module'), - L10n::t('Error: mb_string PHP module required but not installed.'), - false, - true, - $install->getChecks()); - - $this->setFunctions(['iconv_strlen' => false]); - $install = new Install(); - $this->assertFalse($install->checkFunctions()); - $this->assertCheckExist(7, - L10n::t('iconv PHP module'), - L10n::t('Error: iconv PHP module required but not installed.'), - false, - true, - $install->getChecks()); - - $this->setFunctions(['posix_kill' => false]); - $install = new Install(); - $this->assertFalse($install->checkFunctions()); - $this->assertCheckExist(8, - L10n::t('POSIX PHP module'), - L10n::t('Error: POSIX PHP module required but not installed.'), - false, - true, - $install->getChecks()); - - $this->setFunctions([ - 'curl_init' => true, - 'imagecreatefromjpeg' => true, - 'openssl_public_encrypt' => true, - 'mb_strlen' => true, - 'iconv_strlen' => true, - 'posix_kill' => true - ]); - $install = new Install(); - $this->assertTrue($install->checkFunctions()); - } - - /** - * @small - */ - public function testCheckLocalIni() - { - $this->assertTrue($this->root->hasChild('config/local.ini.php')); - - $install = new Install(); - $this->assertTrue($install->checkLocalIni()); - - $this->delConfigFile('local.ini.php'); - - $this->assertFalse($this->root->hasChild('config/local.ini.php')); - - $install = new Install(); - $this->assertTrue($install->checkLocalIni()); - } - - /** - * @small - */ - public function testCheckHtAccessFail() - { - // Mocking the CURL Response - $curlResult = \Mockery::mock('Friendica\Network\CurlResult'); - $curlResult - ->shouldReceive('getReturnCode') - ->andReturn('404'); - $curlResult - ->shouldReceive('getRedirectUrl') - ->andReturn(''); - $curlResult - ->shouldReceive('getError') - ->andReturn('test Error'); - - // Mocking the CURL Request - $networkMock = \Mockery::mock('alias:Friendica\Util\Network'); - $networkMock - ->shouldReceive('fetchUrlFull') - ->with('https://test/install/testrewrite') - ->andReturn($curlResult); - $networkMock - ->shouldReceive('fetchUrlFull') - ->with('http://test/install/testrewrite') - ->andReturn($curlResult); - - // Mocking that we can use CURL - $this->setFunctions(['curl_init' => true]); - - // needed because of "normalise_link" - require_once __DIR__ . '/../../../include/text.php'; - - $install = new Install(); - - $this->assertFalse($install->checkHtAccess('https://test')); - $this->assertSame('test Error', $install->getChecks()[0]['error_msg']['msg']); - } - - /** - * @small - */ - public function testCheckHtAccessWork() - { - // Mocking the failed CURL Response - $curlResultF = \Mockery::mock('Friendica\Network\CurlResult'); - $curlResultF - ->shouldReceive('getReturnCode') - ->andReturn('404'); - - // Mocking the working CURL Response - $curlResultW = \Mockery::mock('Friendica\Network\CurlResult'); - $curlResultW - ->shouldReceive('getReturnCode') - ->andReturn('204'); - - // Mocking the CURL Request - $networkMock = \Mockery::mock('alias:Friendica\Util\Network'); - $networkMock - ->shouldReceive('fetchUrlFull') - ->with('https://test/install/testrewrite') - ->andReturn($curlResultF); - $networkMock - ->shouldReceive('fetchUrlFull') - ->with('http://test/install/testrewrite') - ->andReturn($curlResultW); - - // Mocking that we can use CURL - $this->setFunctions(['curl_init' => true]); - - // needed because of "normalise_link" - require_once __DIR__ . '/../../../include/text.php'; - - $install = new Install(); - - $this->assertTrue($install->checkHtAccess('https://test')); - } - - /** - * @small - */ - public function testImagick() - { - $imageMock = \Mockery::mock('alias:Friendica\Object\Image'); - $imageMock - ->shouldReceive('supportedTypes') - ->andReturn(['image/gif' => 'gif']); - - $this->setClasses(['Imagick' => true]); - - $install = new Install(); - - // even there is no supported type, Imagick should return true (because it is not required) - $this->assertTrue($install->checkImagick()); - - $this->assertCheckExist(1, - L10n::t('ImageMagick supports GIF'), - '', - true, - false, - $install->getChecks()); - } - - /** - * @small - */ - public function testImagickNotFound() - { - $imageMock = \Mockery::mock('alias:Friendica\Object\Image'); - $imageMock - ->shouldReceive('supportedTypes') - ->andReturn([]); - - $this->setClasses(['Imagick' => true]); - - $install = new Install(); - - // even there is no supported type, Imagick should return true (because it is not required) - $this->assertTrue($install->checkImagick()); - $this->assertCheckExist(1, - L10n::t('ImageMagick supports GIF'), - '', - false, - false, - $install->getChecks()); - } - - public function testImagickNotInstalled() - { - $this->setClasses(['Imagick' => false]); - - $install = new Install(); - - // even there is no supported type, Imagick should return true (because it is not required) - $this->assertTrue($install->checkImagick()); - $this->assertCheckExist(0, - L10n::t('ImageMagick PHP extension is not installed'), - '', - false, - false, - $install->getChecks()); - } -} - -/** - * A workaround to replace the PHP native function_exists with a mocked function - * - * @param string $function_name the Name of the function - * - * @return bool true or false - */ -function function_exists($function_name) -{ - global $phpMock; - if (isset($phpMock['function_exists'])) { - $result = call_user_func_array($phpMock['function_exists'], func_get_args()); - if ($result !== '__phpunit_continue__') { - return $result; - } - } - return call_user_func_array('\function_exists', func_get_args()); -} - -function class_exists($class_name) -{ - global $phpMock; - if (isset($phpMock['class_exists'])) { - $result = call_user_func_array($phpMock['class_exists'], func_get_args()); - if ($result !== '__phpunit_continue__') { - return $result; - } - } - return call_user_func_array('\class_exists', func_get_args()); -} diff --git a/tests/src/Core/InstallerTest.php b/tests/src/Core/InstallerTest.php new file mode 100644 index 0000000000..a4ee20b8ce --- /dev/null +++ b/tests/src/Core/InstallerTest.php @@ -0,0 +1,354 @@ +setUpVfsDir(); + } + + private function assertCheckExist($position, $title, $help, $status, $required, $assertionArray) + { + $this->assertArraySubset([$position => [ + 'title' => $title, + 'status' => $status, + 'required' => $required, + 'error_msg' => null, + 'help' => $help] + ], $assertionArray); + } + + /** + * Replaces function_exists results with given mocks + * + * @param array $functions a list from function names and their result + */ + private function setFunctions($functions) + { + global $phpMock; + $phpMock['function_exists'] = function($function) use ($functions) { + foreach ($functions as $name => $value) { + if ($function == $name) { + return $value; + } + } + return '__phpunit_continue__'; + }; + } + + /** + * Replaces class_exist results with given mocks + * + * @param array $classes a list from class names and their results + */ + private function setClasses($classes) + { + global $phpMock; + $phpMock['class_exists'] = function($class) use ($classes) { + foreach ($classes as $name => $value) { + if ($class == $name) { + return $value; + } + } + return '__phpunit_continue__'; + }; + } + + /** + * @small + */ + public function testCheckKeys() + { + $this->setFunctions(['openssl_pkey_new' => false]); + $install = new Installer(); + $this->assertFalse($install->checkKeys()); + + $this->setFunctions(['openssl_pkey_new' => true]); + $install = new Installer(); + $this->assertTrue($install->checkKeys()); + } + + /** + * @small + */ + public function testCheckFunctions() + { + $this->setFunctions(['curl_init' => false]); + $install = new Installer(); + $this->assertFalse($install->checkFunctions()); + $this->assertCheckExist(3, + L10n::t('libCurl PHP module'), + L10n::t('Error: libCURL PHP module required but not installed.'), + false, + true, + $install->getChecks()); + + $this->setFunctions(['imagecreatefromjpeg' => false]); + $install = new Installer(); + $this->assertFalse($install->checkFunctions()); + $this->assertCheckExist(4, + L10n::t('GD graphics PHP module'), + L10n::t('Error: GD graphics PHP module with JPEG support required but not installed.'), + false, + true, + $install->getChecks()); + + $this->setFunctions(['openssl_public_encrypt' => false]); + $install = new Installer(); + $this->assertFalse($install->checkFunctions()); + $this->assertCheckExist(5, + L10n::t('OpenSSL PHP module'), + L10n::t('Error: openssl PHP module required but not installed.'), + false, + true, + $install->getChecks()); + + $this->setFunctions(['mb_strlen' => false]); + $install = new Installer(); + $this->assertFalse($install->checkFunctions()); + $this->assertCheckExist(6, + L10n::t('mb_string PHP module'), + L10n::t('Error: mb_string PHP module required but not installed.'), + false, + true, + $install->getChecks()); + + $this->setFunctions(['iconv_strlen' => false]); + $install = new Installer(); + $this->assertFalse($install->checkFunctions()); + $this->assertCheckExist(7, + L10n::t('iconv PHP module'), + L10n::t('Error: iconv PHP module required but not installed.'), + false, + true, + $install->getChecks()); + + $this->setFunctions(['posix_kill' => false]); + $install = new Installer(); + $this->assertFalse($install->checkFunctions()); + $this->assertCheckExist(8, + L10n::t('POSIX PHP module'), + L10n::t('Error: POSIX PHP module required but not installed.'), + false, + true, + $install->getChecks()); + + $this->setFunctions([ + 'curl_init' => true, + 'imagecreatefromjpeg' => true, + 'openssl_public_encrypt' => true, + 'mb_strlen' => true, + 'iconv_strlen' => true, + 'posix_kill' => true + ]); + $install = new Installer(); + $this->assertTrue($install->checkFunctions()); + } + + /** + * @small + */ + public function testCheckLocalIni() + { + $this->assertTrue($this->root->hasChild('config/local.ini.php')); + + $install = new Installer(); + $this->assertTrue($install->checkLocalIni()); + + $this->delConfigFile('local.ini.php'); + + $this->assertFalse($this->root->hasChild('config/local.ini.php')); + + $install = new Installer(); + $this->assertTrue($install->checkLocalIni()); + } + + /** + * @small + */ + public function testCheckHtAccessFail() + { + // Mocking the CURL Response + $curlResult = \Mockery::mock('Friendica\Network\CurlResult'); + $curlResult + ->shouldReceive('getReturnCode') + ->andReturn('404'); + $curlResult + ->shouldReceive('getRedirectUrl') + ->andReturn(''); + $curlResult + ->shouldReceive('getError') + ->andReturn('test Error'); + + // Mocking the CURL Request + $networkMock = \Mockery::mock('alias:Friendica\Util\Network'); + $networkMock + ->shouldReceive('fetchUrlFull') + ->with('https://test/install/testrewrite') + ->andReturn($curlResult); + $networkMock + ->shouldReceive('fetchUrlFull') + ->with('http://test/install/testrewrite') + ->andReturn($curlResult); + + // Mocking that we can use CURL + $this->setFunctions(['curl_init' => true]); + + // needed because of "normalise_link" + require_once __DIR__ . '/../../../include/text.php'; + + $install = new Installer(); + + $this->assertFalse($install->checkHtAccess('https://test')); + $this->assertSame('test Error', $install->getChecks()[0]['error_msg']['msg']); + } + + /** + * @small + */ + public function testCheckHtAccessWork() + { + // Mocking the failed CURL Response + $curlResultF = \Mockery::mock('Friendica\Network\CurlResult'); + $curlResultF + ->shouldReceive('getReturnCode') + ->andReturn('404'); + + // Mocking the working CURL Response + $curlResultW = \Mockery::mock('Friendica\Network\CurlResult'); + $curlResultW + ->shouldReceive('getReturnCode') + ->andReturn('204'); + + // Mocking the CURL Request + $networkMock = \Mockery::mock('alias:Friendica\Util\Network'); + $networkMock + ->shouldReceive('fetchUrlFull') + ->with('https://test/install/testrewrite') + ->andReturn($curlResultF); + $networkMock + ->shouldReceive('fetchUrlFull') + ->with('http://test/install/testrewrite') + ->andReturn($curlResultW); + + // Mocking that we can use CURL + $this->setFunctions(['curl_init' => true]); + + // needed because of "normalise_link" + require_once __DIR__ . '/../../../include/text.php'; + + $install = new Installer(); + + $this->assertTrue($install->checkHtAccess('https://test')); + } + + /** + * @small + */ + public function testImagick() + { + $imageMock = \Mockery::mock('alias:Friendica\Object\Image'); + $imageMock + ->shouldReceive('supportedTypes') + ->andReturn(['image/gif' => 'gif']); + + $this->setClasses(['Imagick' => true]); + + $install = new Installer(); + + // even there is no supported type, Imagick should return true (because it is not required) + $this->assertTrue($install->checkImagick()); + + $this->assertCheckExist(1, + L10n::t('ImageMagick supports GIF'), + '', + true, + false, + $install->getChecks()); + } + + /** + * @small + */ + public function testImagickNotFound() + { + $imageMock = \Mockery::mock('alias:Friendica\Object\Image'); + $imageMock + ->shouldReceive('supportedTypes') + ->andReturn([]); + + $this->setClasses(['Imagick' => true]); + + $install = new Installer(); + + // even there is no supported type, Imagick should return true (because it is not required) + $this->assertTrue($install->checkImagick()); + $this->assertCheckExist(1, + L10n::t('ImageMagick supports GIF'), + '', + false, + false, + $install->getChecks()); + } + + public function testImagickNotInstalled() + { + $this->setClasses(['Imagick' => false]); + + $install = new Installer(); + + // even there is no supported type, Imagick should return true (because it is not required) + $this->assertTrue($install->checkImagick()); + $this->assertCheckExist(0, + L10n::t('ImageMagick PHP extension is not installed'), + '', + false, + false, + $install->getChecks()); + } +} + +/** + * A workaround to replace the PHP native function_exists with a mocked function + * + * @param string $function_name the Name of the function + * + * @return bool true or false + */ +function function_exists($function_name) +{ + global $phpMock; + if (isset($phpMock['function_exists'])) { + $result = call_user_func_array($phpMock['function_exists'], func_get_args()); + if ($result !== '__phpunit_continue__') { + return $result; + } + } + return call_user_func_array('\function_exists', func_get_args()); +} + +function class_exists($class_name) +{ + global $phpMock; + if (isset($phpMock['class_exists'])) { + $result = call_user_func_array($phpMock['class_exists'], func_get_args()); + if ($result !== '__phpunit_continue__') { + return $result; + } + } + return call_user_func_array('\class_exists', func_get_args()); +} diff --git a/view/templates/install.tpl b/view/templates/install.tpl deleted file mode 100644 index 24ae022424..0000000000 --- a/view/templates/install.tpl +++ /dev/null @@ -1,11 +0,0 @@ - - -

{{$title}}

-

{{$pass}}

- - -{{if $status}} -

{{$status}}

-{{/if}} - -{{$text}} diff --git a/view/templates/install_db.tpl b/view/templates/install_db.tpl index 6b6c1c1e64..6c018db722 100644 --- a/view/templates/install_db.tpl +++ b/view/templates/install_db.tpl @@ -10,9 +10,14 @@ {{$info_03}}

-{{if $status}} -

{{$status}}

-{{/if}} + + {{foreach $checks as $check}} +
{{$check.title}} + {{if ! $check.status}} + Requirement not satisfied + {{/if}} + {{/foreach}} +
diff --git a/view/templates/install_finished.tpl b/view/templates/install_finished.tpl new file mode 100644 index 0000000000..5c8d765e31 --- /dev/null +++ b/view/templates/install_finished.tpl @@ -0,0 +1,13 @@ + + +

{{$title}}

+

{{$pass}}

+ + +{{foreach $checks as $check}} +Requirement not satisfied +{{$check.title}} + +{{/foreach}} + +{{$text}} diff --git a/view/templates/install_settings.tpl b/view/templates/install_settings.tpl index 5584e14365..55e5ea34b1 100644 --- a/view/templates/install_settings.tpl +++ b/view/templates/install_settings.tpl @@ -4,10 +4,6 @@

{{$pass}}

-{{if $status}} -

{{$status}}

-{{/if}} -