Elgg  Version 3.0
ElggInstaller.php
Go to the documentation of this file.
1 <?php
2 
3 use Elgg\Database;
5 use Elgg\Config;
10 
36 
37  private $steps = [
38  'welcome',
39  'requirements',
40  'database',
41  'settings',
42  'admin',
43  'complete',
44  ];
45 
46  private $has_completed = [
47  'config' => false,
48  'database' => false,
49  'settings' => false,
50  'admin' => false,
51  ];
52 
53  private $is_action = false;
54 
55  private $autoLogin = true;
56 
60  private $app;
61 
68  public function run() {
69  $app = $this->getApp();
70 
71  $this->is_action = $app->_services->request->getMethod() === 'POST';
72 
73  $step = get_input('step', 'welcome');
74 
75  if (!in_array($step, $this->getSteps())) {
76  $step = 'welcome';
77  }
78 
79  $this->determineInstallStatus();
80 
81  $response = $this->checkInstallCompletion($step);
82  if ($response) {
83  return $response;
84  }
85 
86  // check if this is an install being resumed
87  $response = $this->resumeInstall($step);
88  if ($response) {
89  return $response;
90  }
91 
92  $this->finishBootstrapping($step);
93 
94  $params = $app->_services->request->request->all();
95 
96  $method = "run" . ucwords($step);
97 
98  return $this->$method($params);
99  }
100 
107  protected function getApp() {
108  if ($this->app) {
109  return $this->app;
110  }
111 
112  try {
113  $config = new Config();
114  $config->elgg_config_locks = false;
115  $config->installer_running = true;
116  $config->dbencoding = 'utf8mb4';
117  $config->boot_cache_ttl = 0;
118  $config->system_cache_enabled = false;
119  $config->simplecache_enabled = false;
120  $config->debug = \Psr\Log\LogLevel::WARNING;
121  $config->cacheroot = Paths::sanitize(sys_get_temp_dir()) . 'elgginstaller/caches/';
122  $config->assetroot = Paths::sanitize(sys_get_temp_dir()) . 'elgginstaller/assets/';
123 
124  $services = new ServiceProvider($config);
125 
126  $app = Application::factory([
127  'service_provider' => $services,
128  'handle_exceptions' => false,
129  'handle_shutdown' => false,
130  ]);
131 
132  // Don't set global $CONFIG, because loading the settings file may require it to write to
133  // it, and it can have array sets (e.g. cookie config) that fail when using a proxy for
134  // the config service.
135  //$app->setGlobalConfig();
136 
137  Application::setInstance($app);
138  $app->loadCore();
139  $this->app = $app;
140 
141  $app->_services->boot->getCache()->disable();
142  $app->_services->plugins->getCache()->disable();
143  $app->_services->sessionCache->disable();
144  $app->_services->dic_cache->getCache()->disable();
145  $app->_services->dataCache->disable();
146  $app->_services->autoloadManager->getCache()->disable();
147 
148  $app->_services->setValue('session', \ElggSession::getMock());
149  $app->_services->views->setViewtype('installation');
150  $app->_services->views->registerViewtypeFallback('installation');
151  $app->_services->views->registerPluginViews(Paths::elgg());
152  $app->_services->translator->registerTranslations(Paths::elgg() . "install/languages/", true);
153 
154  return $this->app;
155  } catch (ConfigurationException $ex) {
156  throw new InstallationException($ex->getMessage());
157  }
158  }
159 
167  public function setAutoLogin($flag) {
168  $this->autoLogin = (bool) $flag;
169  }
170 
186  public function batchInstall(array $params, $create_htaccess = false) {
187  $app = $this->getApp();
188 
189  $defaults = [
190  'dbhost' => 'localhost',
191  'dbprefix' => 'elgg_',
192  'language' => 'en',
193  'siteaccess' => ACCESS_PUBLIC,
194  ];
195  $params = array_merge($defaults, $params);
196 
197  $required_params = [
198  'dbuser',
199  'dbpassword',
200  'dbname',
201  'sitename',
202  'wwwroot',
203  'dataroot',
204  'displayname',
205  'email',
206  'username',
207  'password',
208  ];
209  foreach ($required_params as $key) {
210  if (empty($params[$key])) {
211  $msg = elgg_echo('install:error:requiredfield', [$key]);
212  throw new InstallationException($msg);
213  }
214  }
215 
216  // password is passed in once
217  $params['password1'] = $params['password2'] = $params['password'];
218 
219  if ($create_htaccess) {
220  $rewrite_tester = new ElggRewriteTester();
221  if (!$rewrite_tester->createHtaccess($params['wwwroot'])) {
222  throw new InstallationException(elgg_echo('install:error:htaccess'));
223  }
224  }
225 
226  if (!_elgg_sane_validate_url($params['wwwroot'])) {
227  throw new InstallationException(elgg_echo('install:error:wwwroot', [$params['wwwroot']]));
228  }
229 
230  // sanitize dataroot path
231  $params['dataroot'] = Paths::sanitize($params['dataroot']);
232 
233  $this->determineInstallStatus();
234 
235  if (!$this->has_completed['config']) {
236  if (!$this->createSettingsFile($params)) {
237  throw new InstallationException(elgg_echo('install:error:settings'));
238  }
239  }
240 
241  $this->loadSettingsFile();
242 
243  // Make sure settings file matches parameters
244  $config = $app->_services->config;
245  $config_keys = [
246  // param key => config key
247  'dbhost' => 'dbhost',
248  'dbuser' => 'dbuser',
249  'dbpassword' => 'dbpass',
250  'dbname' => 'dbname',
251  'dataroot' => 'dataroot',
252  'dbprefix' => 'dbprefix',
253  ];
254  foreach ($config_keys as $params_key => $config_key) {
255  if ($params[$params_key] !== $config->$config_key) {
256  throw new InstallationException(elgg_echo('install:error:settings_mismatch', [$config_key]));
257  }
258  }
259 
260  if (!$this->connectToDatabase()) {
261  throw new InstallationException(elgg_echo('install:error:databasesettings'));
262  }
263 
264  if (!$this->has_completed['database']) {
265  if (!$this->installDatabase()) {
266  throw new InstallationException(elgg_echo('install:error:cannotloadtables'));
267  }
268  }
269 
270  // load remaining core libraries
271  $this->finishBootstrapping('settings');
272 
273  if (!$this->saveSiteSettings($params)) {
274  throw new InstallationException(elgg_echo('install:error:savesitesettings'));
275  }
276 
277  if (!$this->createAdminAccount($params)) {
278  throw new InstallationException(elgg_echo('install:admin:cannot_create'));
279  }
280  }
281 
290  protected function render($step, $vars = []) {
291  $vars['next_step'] = $this->getNextStep($step);
292 
293  $title = elgg_echo("install:$step");
294  $body = elgg_view("install/pages/$step", $vars);
295 
297  $title,
298  $body,
299  'default',
300  [
301  'step' => $step,
302  'steps' => $this->getSteps(),
303  ]
304  );
305 
306  return new \Elgg\Http\OkResponse($output);
307  }
308 
320  protected function runWelcome($vars) {
321  return $this->render('welcome');
322  }
323 
334  protected function runRequirements($vars) {
335 
336  $report = [];
337 
338  // check PHP parameters and libraries
339  $this->checkPHP($report);
340 
341  // check URL rewriting
342  $this->checkRewriteRules($report);
343 
344  // check for existence of settings file
345  if ($this->checkSettingsFile($report) !== true) {
346  // no file, so check permissions on engine directory
348  }
349 
350  // check the database later
351  $report['database'] = [
352  [
353  'severity' => 'notice',
354  'message' => elgg_echo('install:check:database')
355  ]
356  ];
357 
358  // any failures?
359  $numFailures = $this->countNumConditions($report, 'error');
360 
361  // any warnings
362  $numWarnings = $this->countNumConditions($report, 'warning');
363 
364 
365  $params = [
366  'report' => $report,
367  'num_failures' => $numFailures,
368  'num_warnings' => $numWarnings,
369  ];
370 
371  return $this->render('requirements', $params);
372  }
373 
384  protected function runDatabase($submissionVars) {
385 
386  $app = $this->getApp();
387 
388  $formVars = [
389  'dbuser' => [
390  'type' => 'text',
391  'value' => '',
392  'required' => true,
393  ],
394  'dbpassword' => [
395  'type' => 'password',
396  'value' => '',
397  'required' => false,
398  ],
399  'dbname' => [
400  'type' => 'text',
401  'value' => '',
402  'required' => true,
403  ],
404  'dbhost' => [
405  'type' => 'text',
406  'value' => 'localhost',
407  'required' => true,
408  ],
409  'dbprefix' => [
410  'type' => 'text',
411  'value' => 'elgg_',
412  'required' => true,
413  ],
414  'dataroot' => [
415  'type' => 'text',
416  'value' => '',
417  'required' => true,
418  ],
419  'wwwroot' => [
420  'type' => 'url',
421  'value' => $app->_services->config->wwwroot,
422  'required' => true,
423  ],
424  'timezone' => [
425  'type' => 'dropdown',
426  'value' => 'UTC',
427  'options' => \DateTimeZone::listIdentifiers(),
428  'required' => true
429  ]
430  ];
431 
432  if ($this->checkSettingsFile()) {
433  // user manually created settings file so we fake out action test
434  $this->is_action = true;
435  }
436 
437  if ($this->is_action) {
438  $getResponse = function () use ($app, $submissionVars, $formVars) {
439  // only create settings file if it doesn't exist
440  if (!$this->checkSettingsFile()) {
441  if (!$this->validateDatabaseVars($submissionVars, $formVars)) {
442  // error so we break out of action and serve same page
443  return;
444  }
445 
446  if (!$this->createSettingsFile($submissionVars)) {
447  return;
448  }
449  }
450 
451  // check db version and connect
452  if (!$this->connectToDatabase()) {
453  return;
454  }
455 
456  if (!$this->installDatabase()) {
457  return;
458  }
459 
460  $app->_services->systemMessages->addSuccessMessage(elgg_echo('install:success:database'));
461 
462  return $this->continueToNextStep('database');
463  };
464 
465  $response = $getResponse();
466  if ($response) {
467  return $response;
468  }
469  }
470 
471  $formVars = $this->makeFormSticky($formVars, $submissionVars);
472 
473  $params = ['variables' => $formVars,];
474 
475  if ($this->checkSettingsFile()) {
476  // settings file exists and we're here so failed to create database
477  $params['failure'] = true;
478  }
479 
480  return $this->render('database', $params);
481  }
482 
492  protected function runSettings($submissionVars) {
493 
494  $app = $this->getApp();
495 
496  $formVars = [
497  'sitename' => [
498  'type' => 'text',
499  'value' => 'My New Community',
500  'required' => true,
501  ],
502  'siteemail' => [
503  'type' => 'email',
504  'value' => '',
505  'required' => false,
506  ],
507  'siteaccess' => [
508  'type' => 'access',
509  'value' => ACCESS_PUBLIC,
510  'required' => true,
511  ],
512  ];
513 
514  if ($this->is_action) {
515  $getResponse = function () use ($app, $submissionVars, $formVars) {
516 
517  if (!$this->validateSettingsVars($submissionVars, $formVars)) {
518  return;
519  }
520 
521  if (!$this->saveSiteSettings($submissionVars)) {
522  return;
523  }
524 
525  $app->_services->systemMessages->addSuccessMessage(elgg_echo('install:success:settings'));
526 
527  return $this->continueToNextStep('settings');
528  };
529 
530  $response = $getResponse();
531  if ($response) {
532  return $response;
533  }
534  }
535 
536  $formVars = $this->makeFormSticky($formVars, $submissionVars);
537 
538  return $this->render('settings', ['variables' => $formVars]);
539  }
540 
551  protected function runAdmin($submissionVars) {
552  $app = $this->getApp();
553 
554  $formVars = [
555  'displayname' => [
556  'type' => 'text',
557  'value' => '',
558  'required' => true,
559  ],
560  'email' => [
561  'type' => 'email',
562  'value' => '',
563  'required' => true,
564  ],
565  'username' => [
566  'type' => 'text',
567  'value' => '',
568  'required' => true,
569  ],
570  'password1' => [
571  'type' => 'password',
572  'value' => '',
573  'required' => true,
574  'pattern' => '.{6,}',
575  ],
576  'password2' => [
577  'type' => 'password',
578  'value' => '',
579  'required' => true,
580  ],
581  ];
582 
583  if ($this->is_action) {
584  $getResponse = function () use ($app, $submissionVars, $formVars) {
585  if (!$this->validateAdminVars($submissionVars, $formVars)) {
586  return;
587  }
588 
589  if (!$this->createAdminAccount($submissionVars, $this->autoLogin)) {
590  return;
591  }
592 
593  $app->_services->systemMessages->addSuccessMessage(elgg_echo('install:success:admin'));
594 
595  return $this->continueToNextStep('admin');
596  };
597 
598  $response = $getResponse();
599  if ($response) {
600  return $response;
601  }
602  }
603 
604  // Bit of a hack to get the password help to show right number of characters
605  // We burn the value into the stored translation.
606  $app = $this->getApp();
607  $lang = $app->_services->translator->getCurrentLanguage();
608  $translations = $app->_services->translator->getLoadedTranslations();
609  $app->_services->translator->addTranslation($lang, [
610  'install:admin:help:password1' => sprintf(
611  $translations[$lang]['install:admin:help:password1'],
612  $app->_services->config->min_password_length
613  ),
614  ]);
615 
616  $formVars = $this->makeFormSticky($formVars, $submissionVars);
617 
618  return $this->render('admin', ['variables' => $formVars]);
619  }
620 
626  protected function runComplete() {
627 
628  // nudge to check out settings
630  '#tag_name' => 'a',
631  '#text' => elgg_echo('install:complete:admin_notice:link_text'),
632  'href' => elgg_normalize_url('admin/settings/basic'),
633  ]);
634  $notice = elgg_echo('install:complete:admin_notice', [$link]);
635  elgg_add_admin_notice('fresh_install', $notice);
636 
637  $result = $this->render('complete');
638 
639  _elgg_rmdir(Paths::sanitize(sys_get_temp_dir()) . 'elgginstaller/');
640 
641  return $result;
642  }
643 
653  protected function getSteps() {
654  return $this->steps;
655  }
656 
665  protected function continueToNextStep($currentStep) {
666  $this->is_action = false;
667 
668  return new \Elgg\Http\RedirectResponse($this->getNextStepUrl($currentStep));
669  }
670 
678  protected function getNextStep($currentStep) {
679  $index = 1 + array_search($currentStep, $this->steps);
680  if (isset($this->steps[$index])) {
681  return $this->steps[$index];
682  } else {
683  return null;
684  }
685  }
686 
695  protected function getNextStepUrl($currentStep) {
696  $app = $this->getApp();
697  $nextStep = $this->getNextStep($currentStep);
698 
699  return $app->_services->config->wwwroot . "install.php?step=$nextStep";
700  }
701 
708  protected function determineInstallStatus() {
709  $app = $this->getApp();
710 
711  $path = Config::resolvePath();
712  if (!is_file($path) || !is_readable($path)) {
713  return;
714  }
715 
716  $this->loadSettingsFile();
717 
718  $this->has_completed['config'] = true;
719 
720  // must be able to connect to database to jump install steps
721  $dbSettingsPass = $this->checkDatabaseSettings(
722  $app->_services->config->dbuser,
723  $app->_services->config->dbpass,
724  $app->_services->config->dbname,
725  $app->_services->config->dbhost
726  );
727 
728  if (!$dbSettingsPass) {
729  return;
730  }
731 
732  $db = $app->_services->db;
733 
734  try {
735  // check that the config table has been created
736  $result = $db->getData("SHOW TABLES");
737  if (empty($result)) {
738  return;
739  }
740  foreach ($result as $table) {
741  $table = (array) $table;
742  if (in_array("{$db->prefix}config", $table)) {
743  $this->has_completed['database'] = true;
744  }
745  }
746  if ($this->has_completed['database'] == false) {
747  return;
748  }
749 
750  // check that the config table has entries
751  $qb = \Elgg\Database\Select::fromTable('config');
752  $qb->select('COUNT(*) AS total');
753 
754  $result = $db->getDataRow($qb);
755  if (!empty($result) && $result->total > 0) {
756  $this->has_completed['settings'] = true;
757  } else {
758  return;
759  }
760 
761  // check that the users entity table has an entry
762  $qb = \Elgg\Database\Select::fromTable('entities', 'e');
763  $qb->select('COUNT(*) AS total')
764  ->where($qb->compare('type', '=', 'user', ELGG_VALUE_STRING));
765 
766  $result = $db->getDataRow($qb);
767  if (!empty($result) && $result->total > 0) {
768  $this->has_completed['admin'] = true;
769  } else {
770  return;
771  }
772  } catch (DatabaseException $ex) {
773  throw new InstallationException('Elgg can not connect to the database: ' . $ex->getMessage());
774  }
775 
776  return;
777  }
778 
787  protected function checkInstallCompletion($step) {
788  if ($step != 'complete') {
789  if (!in_array(false, $this->has_completed)) {
790  // install complete but someone is trying to view an install page
791  return new \Elgg\Http\RedirectResponse('/');
792  }
793  }
794  }
795 
804  protected function resumeInstall($step) {
805  // only do a resume from the first step
806  if ($step !== 'welcome') {
807  return null;
808  }
809 
810  if ($this->has_completed['database'] == false) {
811  return null;
812  }
813 
814  if ($this->has_completed['settings'] == false) {
815  return new \Elgg\Http\RedirectResponse("install.php?step=settings");
816  }
817 
818  if ($this->has_completed['admin'] == false) {
819  return new \Elgg\Http\RedirectResponse("install.php?step=admin");
820  }
821 
822  // everything appears to be set up
823  return new \Elgg\Http\RedirectResponse("install.php?step=complete");
824  }
825 
839  protected function finishBootstrapping($step) {
840 
841  $app = $this->getApp();
842 
843  $index_db = array_search('database', $this->getSteps());
844  $index_settings = array_search('settings', $this->getSteps());
845  $index_admin = array_search('admin', $this->getSteps());
846  $index_complete = array_search('complete', $this->getSteps());
847  $index_step = array_search($step, $this->getSteps());
848 
849  // To log in the user, we need to use the Elgg core session handling.
850  // Otherwise, use default php session handling
851  $use_elgg_session = ($index_step == $index_admin && $this->is_action) || ($index_step == $index_complete);
852  if (!$use_elgg_session) {
853  $this->createSessionFromFile();
854  }
855 
856  if ($index_step > $index_db) {
857  // once the database has been created, load rest of engine
858 
859  // dummy site needed to boot
860  $app->_services->config->site = new ElggSite();
861 
862  $app->bootCore();
863  }
864  }
865 
872  protected function loadSettingsFile() {
873  try {
874  $app = $this->getApp();
875 
876  $config = Config::fromFile(Config::resolvePath());
877  $app->_services->setValue('config', $config);
878 
879  // in case the DB instance is already captured in services, we re-inject its settings.
880  $app->_services->db->resetConnections(DbConfig::fromElggConfig($config));
881  } catch (\Exception $e) {
882  $msg = elgg_echo('InstallationException:CannotLoadSettings');
883  throw new InstallationException($msg, 0, $e);
884  }
885  }
886 
899  protected function makeFormSticky($formVars, $submissionVars) {
900  foreach ($submissionVars as $field => $value) {
901  $formVars[$field]['value'] = $value;
902  }
903 
904  return $formVars;
905  }
906 
907  /* Requirement checks support methods */
908 
916  protected function isInstallDirWritable(&$report) {
917  if (!is_writable(Paths::projectConfig())) {
918  $msg = elgg_echo('install:check:installdir', [Paths::PATH_TO_CONFIG]);
919  $report['settings'] = [
920  [
921  'severity' => 'error',
922  'message' => $msg,
923  ]
924  ];
925 
926  return false;
927  }
928 
929  return true;
930  }
931 
939  protected function checkSettingsFile(&$report = []) {
940  if (!is_file(Config::resolvePath())) {
941  return false;
942  }
943 
944  if (!is_readable(Config::resolvePath())) {
945  $report['settings'] = [
946  [
947  'severity' => 'error',
948  'message' => elgg_echo('install:check:readsettings'),
949  ]
950  ];
951  }
952 
953  return true;
954  }
955 
963  protected function checkPHP(&$report) {
964  $phpReport = [];
965 
966  $min_php_version = '7.0.0';
967  if (version_compare(PHP_VERSION, $min_php_version, '<')) {
968  $phpReport[] = [
969  'severity' => 'error',
970  'message' => elgg_echo('install:check:php:version', [$min_php_version, PHP_VERSION])
971  ];
972  }
973 
974  $this->checkPhpExtensions($phpReport);
975 
976  $this->checkPhpDirectives($phpReport);
977 
978  if (count($phpReport) == 0) {
979  $phpReport[] = [
980  'severity' => 'success',
981  'message' => elgg_echo('install:check:php:success')
982  ];
983  }
984 
985  $report['php'] = $phpReport;
986  }
987 
995  protected function checkPhpExtensions(&$phpReport) {
996  $extensions = get_loaded_extensions();
997  $requiredExtensions = [
998  'pdo_mysql',
999  'json',
1000  'xml',
1001  'gd',
1002  ];
1003  foreach ($requiredExtensions as $extension) {
1004  if (!in_array($extension, $extensions)) {
1005  $phpReport[] = [
1006  'severity' => 'error',
1007  'message' => elgg_echo('install:check:php:extension', [$extension])
1008  ];
1009  }
1010  }
1011 
1012  $recommendedExtensions = [
1013  'mbstring',
1014  ];
1015  foreach ($recommendedExtensions as $extension) {
1016  if (!in_array($extension, $extensions)) {
1017  $phpReport[] = [
1018  'severity' => 'warning',
1019  'message' => elgg_echo('install:check:php:extension:recommend', [$extension])
1020  ];
1021  }
1022  }
1023  }
1024 
1032  protected function checkPhpDirectives(&$phpReport) {
1033  if (ini_get('open_basedir')) {
1034  $phpReport[] = [
1035  'severity' => 'warning',
1036  'message' => elgg_echo("install:check:php:open_basedir")
1037  ];
1038  }
1039 
1040  if (ini_get('safe_mode')) {
1041  $phpReport[] = [
1042  'severity' => 'warning',
1043  'message' => elgg_echo("install:check:php:safe_mode")
1044  ];
1045  }
1046 
1047  if (ini_get('arg_separator.output') !== '&') {
1048  $separator = htmlspecialchars(ini_get('arg_separator.output'));
1049  $msg = elgg_echo("install:check:php:arg_separator", [$separator]);
1050  $phpReport[] = [
1051  'severity' => 'error',
1052  'message' => $msg,
1053  ];
1054  }
1055 
1056  if (ini_get('register_globals')) {
1057  $phpReport[] = [
1058  'severity' => 'error',
1059  'message' => elgg_echo("install:check:php:register_globals")
1060  ];
1061  }
1062 
1063  if (ini_get('session.auto_start')) {
1064  $phpReport[] = [
1065  'severity' => 'error',
1066  'message' => elgg_echo("install:check:php:session.auto_start")
1067  ];
1068  }
1069  }
1070 
1079  protected function checkRewriteRules(&$report) {
1080  $app = $this->getApp();
1081 
1082  $tester = new ElggRewriteTester();
1083  $url = $app->_services->config->wwwroot;
1084  $url .= Request::REWRITE_TEST_TOKEN . '?' . http_build_query([
1085  Request::REWRITE_TEST_TOKEN => '1',
1086  ]);
1087  $report['rewrite'] = [$tester->run($url)];
1088  }
1089 
1098  protected function countNumConditions($report, $condition) {
1099  $count = 0;
1100  foreach ($report as $category => $checks) {
1101  foreach ($checks as $check) {
1102  if ($check['severity'] === $condition) {
1103  $count++;
1104  }
1105  }
1106  }
1107 
1108  return $count;
1109  }
1110 
1111 
1125  protected function validateDatabaseVars($submissionVars, $formVars) {
1126 
1127  $app = $this->getApp();
1128 
1129  foreach ($formVars as $field => $info) {
1130  if ($info['required'] == true && !$submissionVars[$field]) {
1131  $name = elgg_echo("install:database:label:$field");
1132  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:requiredfield', [$name]));
1133 
1134  return false;
1135  }
1136  }
1137 
1138  if (!empty($submissionVars['wwwroot']) && !_elgg_sane_validate_url($submissionVars['wwwroot'])) {
1139  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:wwwroot', [$submissionVars['wwwroot']]));
1140 
1141  return false;
1142  }
1143 
1144  // check that data root is absolute path
1145  if (stripos(PHP_OS, 'win') === 0) {
1146  if (strpos($submissionVars['dataroot'], ':') !== 1) {
1147  $msg = elgg_echo('install:error:relative_path', [$submissionVars['dataroot']]);
1148  $app->_services->systemMessages->addErrorMessage($msg);
1149 
1150  return false;
1151  }
1152  } else {
1153  if (strpos($submissionVars['dataroot'], '/') !== 0) {
1154  $msg = elgg_echo('install:error:relative_path', [$submissionVars['dataroot']]);
1155  $app->_services->systemMessages->addErrorMessage($msg);
1156 
1157  return false;
1158  }
1159  }
1160 
1161  // check that data root exists
1162  if (!is_dir($submissionVars['dataroot'])) {
1163  $msg = elgg_echo('install:error:datadirectoryexists', [$submissionVars['dataroot']]);
1164  $app->_services->systemMessages->addErrorMessage($msg);
1165 
1166  return false;
1167  }
1168 
1169  // check that data root is writable
1170  if (!is_writable($submissionVars['dataroot'])) {
1171  $msg = elgg_echo('install:error:writedatadirectory', [$submissionVars['dataroot']]);
1172  $app->_services->systemMessages->addErrorMessage($msg);
1173 
1174  return false;
1175  }
1176 
1177  if (!$app->_services->config->data_dir_override) {
1178  // check that data root is not subdirectory of Elgg root
1179  if (stripos($submissionVars['dataroot'], $app->_services->config->path) === 0) {
1180  $msg = elgg_echo('install:error:locationdatadirectory', [$submissionVars['dataroot']]);
1181  $app->_services->systemMessages->addErrorMessage($msg);
1182 
1183  return false;
1184  }
1185  }
1186 
1187  // according to postgres documentation: SQL identifiers and key words must
1188  // begin with a letter (a-z, but also letters with diacritical marks and
1189  // non-Latin letters) or an underscore (_). Subsequent characters in an
1190  // identifier or key word can be letters, underscores, digits (0-9), or dollar signs ($).
1191  // Refs #4994
1192  if (!preg_match("/^[a-zA-Z_][\w]*$/", $submissionVars['dbprefix'])) {
1193  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:database_prefix'));
1194 
1195  return false;
1196  }
1197 
1198  return $this->checkDatabaseSettings(
1199  $submissionVars['dbuser'],
1200  $submissionVars['dbpassword'],
1201  $submissionVars['dbname'],
1202  $submissionVars['dbhost']
1203  );
1204  }
1205 
1216  protected function checkDatabaseSettings($user, $password, $dbname, $host) {
1217  $app = $this->getApp();
1218 
1219  $config = new DbConfig((object) [
1220  'dbhost' => $host,
1221  'dbuser' => $user,
1222  'dbpass' => $password,
1223  'dbname' => $dbname,
1224  'dbencoding' => 'utf8mb4',
1225  ]);
1226  $db = new Database($config, $app->_services->queryCache);
1227 
1228  try {
1229  $db->getDataRow("SELECT 1");
1230  } catch (DatabaseException $e) {
1231  if (0 === strpos($e->getMessage(), "Elgg couldn't connect")) {
1232  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:databasesettings'));
1233  } else {
1234  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:nodatabase', [$dbname]));
1235  }
1236 
1237  return false;
1238  }
1239 
1240  // check MySQL version
1241  $version = $db->getServerVersion(DbConfig::READ_WRITE);
1242  if (version_compare($version, '5.5.3', '<')) {
1243  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:oldmysql2', [$version]));
1244 
1245  return false;
1246  }
1247 
1248  return true;
1249  }
1250 
1259  protected function createSettingsFile($params) {
1260  $app = $this->getApp();
1261 
1262  $template = Application::elggDir()->getContents("elgg-config/settings.example.php");
1263  if (!$template) {
1264  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:readsettingsphp'));
1265 
1266  return false;
1267  }
1268 
1269  foreach ($params as $k => $v) {
1270  // do some sanitization
1271  switch ($k) {
1272  case 'dataroot':
1273  $v = Paths::sanitize($v);
1274  break;
1275  }
1276 
1277  $template = str_replace("{{" . $k . "}}", $v, $template);
1278  }
1279 
1280  $result = file_put_contents(Config::resolvePath(), $template);
1281  if ($result === false) {
1282  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:writesettingphp'));
1283 
1284  return false;
1285  }
1286 
1287  $config = (object) [
1288  'dbhost' => elgg_extract('dbhost', $params, 'localhost'),
1289  'dbuser' => elgg_extract('dbuser', $params),
1290  'dbpass' => elgg_extract('dbpassword', $params),
1291  'dbname' => elgg_extract('dbname', $params),
1292  'dbencoding' => elgg_extract('dbencoding', $params, 'utf8mb4'),
1293  'dbprefix' => elgg_extract('dbprefix', $params, 'elgg_'),
1294  ];
1295 
1296  $dbConfig = new DbConfig($config);
1297  $this->getApp()->_services->setValue('dbConfig', $dbConfig);
1298  $this->getApp()->_services->db->resetConnections($dbConfig);
1299 
1300  return true;
1301  }
1302 
1309  protected function connectToDatabase() {
1310  try {
1311  $app = $this->getApp();
1312  $app->_services->db->setupConnections();
1313  } catch (DatabaseException $e) {
1314  $app->_services->systemMessages->addErrorMessage($e->getMessage());
1315 
1316  return false;
1317  }
1318 
1319  return true;
1320  }
1321 
1327  protected function installDatabase() {
1328  try {
1329  return $this->getApp()->migrate();
1330  } catch (\Exception $e) {
1331  return false;
1332  }
1333  }
1334 
1347  protected function createDataDirectory(&$submissionVars, $formVars) {
1348  // did the user have option of Elgg creating the data directory
1349  if ($formVars['dataroot']['type'] != 'combo') {
1350  return true;
1351  }
1352 
1353  // did the user select the option
1354  if ($submissionVars['dataroot'] != 'dataroot-checkbox') {
1355  return true;
1356  }
1357 
1358  $dir = \Elgg\Project\Paths::sanitize($submissionVars['path']) . 'data';
1359  if (file_exists($dir) || mkdir($dir, 0755)) {
1360  $submissionVars['dataroot'] = $dir;
1361  if (!file_exists("$dir/.htaccess")) {
1362  $htaccess = "Order Deny,Allow\nDeny from All\n";
1363  if (!file_put_contents("$dir/.htaccess", $htaccess)) {
1364  return false;
1365  }
1366  }
1367 
1368  return true;
1369  }
1370 
1371  return false;
1372  }
1373 
1382  protected function validateSettingsVars($submissionVars, $formVars) {
1383  $app = $this->getApp();
1384 
1385  foreach ($formVars as $field => $info) {
1386  $submissionVars[$field] = trim($submissionVars[$field]);
1387  if ($info['required'] == true && $submissionVars[$field] === '') {
1388  $name = elgg_echo("install:settings:label:$field");
1389  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:requiredfield', [$name]));
1390 
1391  return false;
1392  }
1393  }
1394 
1395  // check that email address is email address
1396  if ($submissionVars['siteemail'] && !is_email_address($submissionVars['siteemail'])) {
1397  $msg = elgg_echo('install:error:emailaddress', [$submissionVars['siteemail']]);
1398  $app->_services->systemMessages->addErrorMessage($msg);
1399 
1400  return false;
1401  }
1402 
1403  return true;
1404  }
1405 
1414  protected function saveSiteSettings($submissionVars) {
1415  $app = $this->getApp();
1416 
1418 
1419  if (!$site->guid) {
1420  $site = new ElggSite();
1421  $site->name = strip_tags($submissionVars['sitename']);
1422  $site->access_id = ACCESS_PUBLIC;
1423  $site->email = $submissionVars['siteemail'];
1424  $site->save();
1425  }
1426 
1427  if ($site->guid !== 1) {
1428  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:createsite'));
1429 
1430  return false;
1431  }
1432 
1433  $app->_services->config->site = $site;
1434 
1435  $sets = [
1436  'installed' => time(),
1437  'version' => elgg_get_version(),
1438  'simplecache_enabled' => 1,
1439  'system_cache_enabled' => 1,
1440  'simplecache_minify_js' => true,
1441  'simplecache_minify_css' => true,
1442  'simplecache_lastupdate' => time(),
1443  'processed_upgrades' => [],
1444  'language' => 'en',
1445  'default_access' => $submissionVars['siteaccess'],
1446  'allow_registration' => false,
1447  'walled_garden' => false,
1448  'allow_user_default_access' => '',
1449  'default_limit' => 10,
1450  'security_protect_upgrade' => true,
1451  'security_notify_admins' => true,
1452  'security_notify_user_password' => true,
1453  'security_email_require_password' => true,
1454  ];
1455 
1456  foreach ($sets as $key => $value) {
1458  }
1459 
1460  try {
1461  // Plugins hold reference to non-existing DB
1462  $app->_services->reset('plugins');
1463 
1465 
1466  $plugins = $app->_services->plugins->find('any');
1467 
1468  foreach ($plugins as $plugin) {
1469  $manifest = $plugin->getManifest();
1470  if (!$manifest instanceof ElggPluginManifest) {
1471  continue;
1472  }
1473 
1474  if (!$manifest->getActivateOnInstall()) {
1475  continue;
1476  }
1477 
1478  $plugin->activate();
1479  }
1480 
1481  // Wo don't need to run upgrades on new installations
1482  $app->_services->events->unregisterHandler('create', 'object', '_elgg_create_notice_of_pending_upgrade');
1483  $upgrades = $app->_services->upgradeLocator->locate();
1484  foreach ($upgrades as $upgrade) {
1485  $upgrade->setCompleted();
1486  }
1487  } catch (Exception $e) {
1488  $app->_services->logger->log(\Psr\Log\LogLevel::ERROR, $e);
1489  }
1490 
1491  return true;
1492  }
1493 
1503  protected function validateAdminVars($submissionVars, $formVars) {
1504 
1505  $app = $this->getApp();
1506 
1507  foreach ($formVars as $field => $info) {
1508  if ($info['required'] == true && !$submissionVars[$field]) {
1509  $name = elgg_echo("install:admin:label:$field");
1510  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:requiredfield', [$name]));
1511 
1512  return false;
1513  }
1514  }
1515 
1516  if ($submissionVars['password1'] !== $submissionVars['password2']) {
1517  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:admin:password:mismatch'));
1518 
1519  return false;
1520  }
1521 
1522  if (trim($submissionVars['password1']) == "") {
1523  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:admin:password:empty'));
1524 
1525  return false;
1526  }
1527 
1528  $minLength = $app->_services->configTable->get('min_password_length');
1529  if (strlen($submissionVars['password1']) < $minLength) {
1530  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:admin:password:tooshort'));
1531 
1532  return false;
1533  }
1534 
1535  // check that email address is email address
1536  if ($submissionVars['email'] && !is_email_address($submissionVars['email'])) {
1537  $msg = elgg_echo('install:error:emailaddress', [$submissionVars['email']]);
1538  $app->_services->systemMessages->addErrorMessage($msg);
1539 
1540  return false;
1541  }
1542 
1543  return true;
1544  }
1545 
1555  protected function createAdminAccount($submissionVars, $login = false) {
1556  $app = $this->getApp();
1557 
1558  try {
1559  $guid = register_user(
1560  $submissionVars['username'],
1561  $submissionVars['password1'],
1562  $submissionVars['displayname'],
1563  $submissionVars['email']
1564  );
1565  } catch (RegistrationException $e) {
1566  $app->_services->systemMessages->addErrorMessage($e->getMessage());
1567 
1568  return false;
1569  }
1570 
1571  if ($guid === false) {
1572  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:admin:cannot_create'));
1573 
1574  return false;
1575  }
1576 
1577  $user = get_entity($guid);
1578 
1579  if (!$user instanceof ElggUser) {
1580  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:loadadmin'));
1581 
1582  return false;
1583  }
1584 
1585  $app = $this->getApp();
1586 
1587  $ia = $app->_services->session->setIgnoreAccess(true);
1588  if (!$user->makeAdmin()) {
1589  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:adminaccess'));
1590  } else {
1591  $app->_services->configTable->set('admin_registered', 1);
1592  }
1593  $app->_services->session->setIgnoreAccess($ia);
1594 
1595  // add validation data to satisfy user validation plugins
1596  $user->validated = true;
1597  $user->validated_method = 'admin_user';
1598 
1599  if (!$login) {
1600  return true;
1601  }
1602 
1603  $this->createSessionFromDatabase();
1604  try {
1605  login($user);
1606  } catch (LoginException $ex) {
1607  $app->_services->systemMessages->addErrorMessage(elgg_echo('install:error:adminlogin'));
1608 
1609  return false;
1610  }
1611 
1612  return true;
1613  }
1614 
1621  protected function createSessionFromFile() {
1622  $app = $this->getApp();
1623  $session = ElggSession::fromFiles($app->_services->config);
1624  $session->setName('Elgg_install');
1625  $app->_services->setValue('session', $session);
1626  }
1627 
1634  protected function createSessionFromDatabase() {
1635  $app = $this->getApp();
1636  $session = ElggSession::fromDatabase($app->_services->config, $app->_services->db);
1637  $session->start();
1638  $app->_services->setValue('session', $session);
1639  }
1640 }
$plugin
foreach(array_keys($combine_languages) as $language) $translations
if(!$user||!$user->canDelete()) $name
Definition: delete.php:22
createDataDirectory(&$submissionVars, $formVars)
Site settings support methods.
createSessionFromDatabase()
Setup session.
Bundled plugins(the contents of the"/mod"directory) are available only under the GPLv2 license.The remainder of the project is available under either MIT or GPLv2.Both licenses can be found below.More info and that you know you can do these things To protect your we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights These restrictions translate to certain responsibilities for you if you distribute copies of the or if you modify it For if you distribute copies of such a whether gratis or for a you must give the recipients all the rights that you have You must make sure that receive or can get the source code And you must show them these terms so they know their rights We protect your rights with two steps
Definition: LICENSE.txt:1
$params
Saves global plugin settings.
Definition: save.php:13
WARNING: API IN FLUX.
Definition: DbConfig.php:13
countNumConditions($report, $condition)
Count the number of failures in the requirements report.
elgg_normalize_url($url)
Definition: output.php:186
Elgg RewriteTester.
runAdmin($submissionVars)
Admin account controller.
_elgg_sane_validate_url($url)
Use a "fixed" filter_var() with FILTER_VALIDATE_URL that handles multi-byte chars.
Definition: output.php:449
checkPhpDirectives(&$phpReport)
Check PHP parameters.
$lang
Definition: html.php:13
$defaults
runWelcome($vars)
Step controllers.
$extensions
elgg_add_admin_notice($id, $message)
Write a persistent message to the admin view.
Definition: admin.php:59
$path
Definition: details.php:89
createSessionFromFile()
Setup session.
installDatabase()
Create the database tables.
run()
Dispatches a request to one of the step controllers.
checkInstallCompletion($step)
Security check to ensure the installer cannot be run after installation has finished.
getNextStep($currentStep)
Get the next step as a string.
$guid
Removes an admin notice.
static fromFiles(Config $config)
Create a session stored in files.
$plugins
Definition: categories.php:3
getSteps()
Step management.
getApp()
Build the application needed by the installer.
catch(LoginException $e) if($request->isXhr()) $output
Definition: login.php:56
$report
elgg_echo($message_key, array $args=[], $language="")
Given a message key, returns an appropriately translated full-text string.
Definition: languages.php:21
checkPHP(&$report)
Check version of PHP, extensions, and variables.
if(!$item instanceof ElggEntity) $link
Definition: container.php:16
runDatabase($submissionVars)
Database set up controller.
elgg_view_page($title, $body, $page_shell= 'default', $vars=[])
Assembles and outputs a full page.
Definition: views.php:360
validateSettingsVars($submissionVars, $formVars)
Validate the site settings form variables.
elgg_save_config($name, $value)
Save a configuration setting.
resumeInstall($step)
Check if this is a case of a install being resumed and figure out where to continue from...
$config
Advanced site settings, debugging section.
Definition: debugging.php:6
$upgrades
Lists pending upgrades.
Definition: upgrades.php:11
is_email_address($address)
Validates an email address.
Definition: input.php:105
continueToNextStep($currentStep)
Forwards the browser to the next step.
$upgrade
Definition: upgrade.php:8
getNextStepUrl($currentStep)
Get the URL of the next step.
if(!$user||!$user->canEdit()) $password
$title
Definition: user.php:20
finishBootstrapping($step)
Bootstrapping.
get_input($variable, $default=null, $filter_result=true)
Get some input from variables passed submitted through GET or POST.
Definition: input.php:27
render($step, $vars=[])
Renders the data passed by a controller.
runComplete()
Controller for last step.
elgg_format_element($tag_name, array $attributes=[], $text= '', array $options=[])
Format an HTML element.
Definition: output.php:168
makeFormSticky($formVars, $submissionVars)
Action handling methods.
$user
Definition: ban.php:7
$url
Definition: default.php:33
checkPhpExtensions(&$phpReport)
Check the server&#39;s PHP extensions.
if(!empty($screenshots)) $info
Definition: details.php:59
register_user($username, $password, $name, $email, $allow_multiple_emails=false, $subtype=null)
Registers a user, returning false if the username already exists.
Definition: users.php:205
$body
Definition: useradd.php:60
_elgg_rmdir($dir, $empty=false)
Recursively deletes a directory, including all hidden files.
Definition: cache.php:190
runSettings($submissionVars)
Site settings controller.
elgg_get_site_entity()
Get the current site entity.
Definition: entities.php:130
checkRewriteRules(&$report)
Confirm that the rewrite rules are firing.
elgg_extract($key, $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:1131
$separator
Definition: tags.php:56
runRequirements($vars)
Requirements controller.
validateDatabaseVars($submissionVars, $formVars)
Database support methods.
determineInstallStatus()
Updates $this->has_completed according to the current installation.
if($container instanceof ElggGroup &&$container->guid!=elgg_get_page_owner_guid()) $key
Definition: summary.php:55
createSettingsFile($params)
Writes the settings file to the engine directory.
if(elgg_extract('required', $vars)) $field
Definition: field.php:34
$step
Definition: time.php:39
checkSettingsFile(&$report=[])
Check that the settings file exists.
$value
Definition: debugging.php:7
elgg_get_version($human_readable=false)
Get the current Elgg version information.
Definition: elgglib.php:814
isInstallDirWritable(&$report)
Indicates whether the webserver can add settings.php on its own or not.
const ELGG_VALUE_STRING
Definition: constants.php:139
loadSettingsFile()
Load settings.
$vars['type']
Definition: save.php:11
login(\ElggUser $user, $persistent=false)
Logs in a specified .
Definition: sessions.php:273
checkDatabaseSettings($user, $password, $dbname, $host)
Confirm the settings for the database.
if(elgg_in_context('widget')) $count
Definition: pagination.php:21
static sanitize($path, $append_slash=true)
Sanitise file paths ensuring that they begin and end with slashes etc.
Definition: Paths.php:76
elgg ajax ERROR
Definition: ajax.js:33
const ACCESS_PUBLIC
Definition: constants.php:14
static getMock()
Get an isolated ElggSession that does not persist between requests.
$table
Definition: cron.php:57
batchInstall(array $params, $create_htaccess=false)
A batch install of Elgg.
connectToDatabase()
Bootstrap database connection before entire engine is available.
setAutoLogin($flag)
Set the auto login flag.
$index
Definition: gallery.php:47
createAdminAccount($submissionVars, $login=false)
Create a user account for the admin.
saveSiteSettings($submissionVars)
Initialize the site including site entity, plugins, and configuration.
elgg_view($view, $vars=[], $viewtype= '')
Return a parsed view.
Definition: views.php:246
$version
Definition: version.php:14
var elgg
Definition: elgglib.js:4
_elgg_generate_plugin_entities()
Elgg plugins library Contains functions for managing plugins.
Definition: plugins.php:17
$extension
Definition: default.php:28
get_entity($guid)
Loads and returns an entity object from a guid.
Definition: entities.php:87
static fromDatabase(Config $config, Database $db)
Create a session stored in the DB.
validateAdminVars($submissionVars, $formVars)
Validate account form variables.