Update copyright
[friendica.git/.git] / src / Console / AutomaticInstallation.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Console;
23
24 use Asika\SimpleConsole\Console;
25 use Friendica\App;
26 use Friendica\App\BaseURL;
27 use Friendica\Core\Config\IConfig;
28 use Friendica\Core\Config\Cache;
29 use Friendica\Core\Installer;
30 use Friendica\Core\Theme;
31 use Friendica\Database\Database;
32 use Friendica\Util\BasePath;
33 use Friendica\Util\ConfigFileLoader;
34 use RuntimeException;
35
36 class AutomaticInstallation extends Console
37 {
38         /** @var App\Mode */
39         private $appMode;
40         /** @var Cache */
41         private $configCache;
42         /** @var IConfig */
43         private $config;
44         /** @var Database */
45         private $dba;
46
47         protected function getHelp()
48         {
49                 return <<<HELP
50 Installation - Install Friendica automatically
51 Synopsis
52         bin/console autoinstall [-h|--help|-?] [-v] [-a] [-f]
53
54 Description
55     Installs Friendica with data based on the local.config.php file or environment variables
56
57 Notes
58     Not checking .htaccess/URL-Rewrite during CLI installation.
59
60 Options
61     -h|--help|-?            Show help information
62     -v                      Show more debug information.
63     -a                      All setup checks are required (except .htaccess)
64     -f|--file <config>      prepared config file (e.g. "config/local.config.php" itself) which will override every other config option - except the environment variables)
65     -s|--savedb               Save the DB credentials to the file (if environment variables is used)
66     -H|--dbhost <host>        The host of the mysql/mariadb database (env MYSQL_HOST)
67     -p|--dbport <port>        The port of the mysql/mariadb database (env MYSQL_PORT)
68     -d|--dbdata <database>    The name of the mysql/mariadb database (env MYSQL_DATABASE)
69     -U|--dbuser <username>    The username of the mysql/mariadb database login (env MYSQL_USER or MYSQL_USERNAME)
70     -P|--dbpass <password>    The password of the mysql/mariadb database login (env MYSQL_PASSWORD)
71     -U|--url <url>            The full base URL of Friendica - f.e. 'https://friendica.local/sub' (env FRIENDICA_URL) 
72     -B|--phppath <php_path>   The path of the PHP binary (env FRIENDICA_PHP_PATH)
73     -b|--basepath <base_path> The basepath of Friendica (env FRIENDICA_BASE_PATH)
74     -t|--tz <timezone>        The timezone of Friendica (env FRIENDICA_TZ)
75     -L|--lang <language>      The language of Friendica (env FRIENDICA_LANG)
76  
77 Environment variables
78    MYSQL_HOST                  The host of the mysql/mariadb database (mandatory if mysql and environment is used)
79    MYSQL_PORT                  The port of the mysql/mariadb database
80    MYSQL_USERNAME|MYSQL_USER   The username of the mysql/mariadb database login (MYSQL_USERNAME is for mysql, MYSQL_USER for mariadb)
81    MYSQL_PASSWORD              The password of the mysql/mariadb database login
82    MYSQL_DATABASE              The name of the mysql/mariadb database
83    FRIENDICA_URL               The full base URL of Friendica - f.e. 'https://friendica.local/sub'
84    FRIENDICA_PHP_PATH          The path of the PHP binary - leave empty for auto detection
85    FRIENDICA_BASE_PATH         The basepath of Friendica - leave empty for auto detection
86    FRIENDICA_ADMIN_MAIL        The admin email address of Friendica (this email will be used for admin access)
87    FRIENDICA_TZ                The timezone of Friendica
88    FRIENDICA_LANG              The langauge of Friendica
89    
90 Examples
91         bin/console autoinstall -f 'input.config.php
92                 Installs Friendica with the prepared 'input.config.php' file
93
94         bin/console autoinstall --savedb
95                 Installs Friendica with environment variables and saves them to the 'config/local.config.php' file
96
97         bin/console autoinstall -h localhost -p 3365 -U user -P passwort1234 -d friendica
98                 Installs Friendica with a local mysql database with credentials
99 HELP;
100         }
101
102         public function __construct(App\Mode $appMode, Cache $configCache, IConfig $config, Database $dba, array $argv = null)
103         {
104                 parent::__construct($argv);
105
106                 $this->appMode     = $appMode;
107                 $this->configCache = $configCache;
108                 $this->config      = $config;
109                 $this->dba         = $dba;
110         }
111
112         protected function doExecute()
113         {
114                 // Initialise the app
115                 $this->out("Initializing setup...\n");
116
117                 $installer = new Installer();
118
119                 $configCache  = $this->configCache;
120                 $basePathConf = $configCache->get('system', 'basepath');
121                 $basepath     = new BasePath($basePathConf);
122                 $installer->setUpCache($configCache, $basepath->getPath());
123
124                 $this->out(" Complete!\n\n");
125
126                 // Check Environment
127                 $this->out("Checking environment...\n");
128
129                 $installer->resetChecks();
130
131                 if (!$this->runBasicChecks($installer, $configCache)) {
132                         $errorMessage = $this->extractErrors($installer->getChecks());
133                         throw new RuntimeException($errorMessage);
134                 }
135
136                 $this->out(" Complete!\n\n");
137
138                 // if a config file is set,
139                 $config_file = $this->getOption(['f', 'file']);
140
141                 if (!empty($config_file)) {
142
143                         if (!file_exists($config_file)) {
144                                 throw new RuntimeException("ERROR: Config file does not exist.\n");
145                         }
146
147                         //reload the config cache
148                         $loader = new ConfigFileLoader($config_file);
149                         $loader->setupCache($configCache);
150
151                 } else {
152                         // Creating config file
153                         $this->out("Creating config file...\n");
154
155                         $save_db = $this->getOption(['s', 'savedb'], false);
156
157                         $db_host = $this->getOption(['H', 'dbhost'], ($save_db) ? (getenv('MYSQL_HOST')) : Installer::DEFAULT_HOST);
158                         $db_port = $this->getOption(['p', 'dbport'], ($save_db) ? getenv('MYSQL_PORT') : null);
159                         $configCache->set('database', 'hostname', $db_host . (!empty($db_port) ? ':' . $db_port : ''));
160                         $configCache->set('database', 'database',
161                                 $this->getOption(['d', 'dbdata'],
162                                         ($save_db) ? getenv('MYSQL_DATABASE') : ''));
163                         $configCache->set('database', 'username',
164                                 $this->getOption(['U', 'dbuser'],
165                                         ($save_db) ? getenv('MYSQL_USER') . getenv('MYSQL_USERNAME') : ''));
166                         $configCache->set('database', 'password',
167                                 $this->getOption(['P', 'dbpass'],
168                                         ($save_db) ? getenv('MYSQL_PASSWORD') : ''));
169
170                         $php_path = $this->getOption(['b', 'phppath'], !empty('FRIENDICA_PHP_PATH') ? getenv('FRIENDICA_PHP_PATH') : null);
171                         if (!empty($php_path)) {
172                                 $configCache->set('config', 'php_path', $php_path);
173                         } else {
174                                 $configCache->set('config', 'php_path', $installer->getPHPPath());
175                         }
176
177                         $configCache->set('config', 'admin_email',
178                                 $this->getOption(['A', 'admin'],
179                                         !empty(getenv('FRIENDICA_ADMIN_MAIL')) ? getenv('FRIENDICA_ADMIN_MAIL') : ''));
180                         $configCache->set('system', 'default_timezone',
181                                 $this->getOption(['T', 'tz'],
182                                         !empty(getenv('FRIENDICA_TZ')) ? getenv('FRIENDICA_TZ') : Installer::DEFAULT_TZ));
183                         $configCache->set('system', 'language',
184                                 $this->getOption(['L', 'lang'],
185                                         !empty(getenv('FRIENDICA_LANG')) ? getenv('FRIENDICA_LANG') : Installer::DEFAULT_LANG));
186
187                         $basepath = $this->getOption(['b', 'basepath'], !empty(getenv('FRIENDICA_BASE_PATH')) ? getenv('FRIENDICA_BASE_PATH') : null);
188                         if (!empty($basepath)) {
189                                 $configCache->set('system', 'basepath', $basepath);
190                         }
191
192                         $url = $this->getOption(['U', 'url'], !empty(getenv('FRIENDICA_URL')) ? getenv('FRIENDICA_URL') : null);
193
194                         if (empty($url)) {
195                                 $this->out('The Friendica URL has to be set during CLI installation.');
196                                 return 1;
197                         } else {
198                                 $baseUrl = new BaseURL($this->config, []);
199                                 $baseUrl->saveByURL($url);
200                         }
201
202                         $installer->createConfig($configCache);
203                 }
204
205                 $this->out("Complete!\n\n");
206
207                 // Check database connection
208                 $this->out("Checking database...\n");
209
210                 $installer->resetChecks();
211
212                 if (!$installer->checkDB($this->dba)) {
213                         $errorMessage = $this->extractErrors($installer->getChecks());
214                         throw new RuntimeException($errorMessage);
215                 }
216
217                 $this->out(" Complete!\n\n");
218
219                 // Install database
220                 $this->out("Inserting data into database...\n");
221
222                 $installer->resetChecks();
223
224                 if (!$installer->installDatabase($basePathConf)) {
225                         $errorMessage = $this->extractErrors($installer->getChecks());
226                         throw new RuntimeException($errorMessage);
227                 }
228
229                 if (!empty($config_file) && $config_file != 'config' . DIRECTORY_SEPARATOR . 'local.config.php') {
230                         // Copy config file
231                         $this->out("Copying config file...\n");
232                         if (!copy($basePathConf . DIRECTORY_SEPARATOR . $config_file, $basePathConf . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'local.config.php')) {
233                                 throw new RuntimeException("ERROR: Saving config file failed. Please copy '$config_file' to '" . $basePathConf . "'" . DIRECTORY_SEPARATOR . "config" . DIRECTORY_SEPARATOR . "local.config.php' manually.\n");
234                         }
235                 }
236
237                 $this->out(" Complete!\n\n");
238
239                 // Install theme
240                 $this->out("Installing theme\n");
241                 if (!empty($this->config->get('system', 'theme'))) {
242                         Theme::install($this->config->get('system', 'theme'));
243                         $this->out(" Complete\n\n");
244                 } else {
245                         $this->out(" Theme setting is empty. Please check the file 'config/local.config.php'\n\n");
246                 }
247
248                 $this->out("\nInstallation is finished\n");
249
250                 return 0;
251         }
252
253         /**
254          * @param Installer $installer   The Installer instance
255          * @param Cache     $configCache The config cache
256          *
257          * @return bool true if checks were successfully, otherwise false
258          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
259          */
260         private function runBasicChecks(Installer $installer, Cache $configCache)
261         {
262                 $checked = true;
263
264                 $installer->resetChecks();
265                 if (!$installer->checkFunctions()) {
266                         $checked = false;
267                 }
268                 if (!$installer->checkImagick()) {
269                         $checked = false;
270                 }
271                 if (!$installer->checkLocalIni()) {
272                         $checked = false;
273                 }
274                 if (!$installer->checkSmarty3()) {
275                         $checked = false;
276                 }
277                 if (!$installer->checkKeys()) {
278                         $checked = false;
279                 }
280
281                 $php_path = $configCache->get('config', 'php_path');
282
283                 if (!$installer->checkPHP($php_path, true)) {
284                         $checked = false;
285                 }
286
287                 $this->out(" NOTICE: Not checking .htaccess/URL-Rewrite during CLI installation.\n");
288
289                 return $checked;
290         }
291
292         /**
293          * @param array $results
294          *
295          * @return string
296          */
297         private function extractErrors($results)
298         {
299                 $errorMessage      = '';
300                 $allChecksRequired = $this->getOption('a') !== null;
301
302                 foreach ($results as $result) {
303                         if (($allChecksRequired || $result['required'] === true) && $result['status'] === false) {
304                                 $errorMessage .= "--------\n";
305                                 $errorMessage .= $result['title'] . ': ' . $result['help'] . "\n";
306                         }
307                 }
308
309                 return $errorMessage;
310         }
311 }