Elgg  Version master
ElggInstaller.php
Go to the documentation of this file.
1 <?php
2 
4 use Elgg\Config;
5 use Elgg\Database;
16 
42 
43  public const MARIADB_MINIMAL_VERSION = '10.6';
44  public const MYSQL_MINIMAL_VERSION = '8.0';
45  public const PHP_MINIMAL_VERSION = '8.1.0';
46 
47  protected array $steps = [
48  'welcome',
49  'requirements',
50  'database',
51  'settings',
52  'admin',
53  'complete',
54  ];
55 
56  protected array $has_completed = [
57  'config' => false,
58  'database' => false,
59  'settings' => false,
60  'admin' => false,
61  ];
62 
63  protected bool $is_action = false;
64 
68  protected $app;
69 
75  public function run(): \Elgg\Http\ResponseBuilder {
76  $app = $this->getApp();
77 
78  $this->is_action = $app->internal_services->request->getMethod() === 'POST';
79 
80  $step = $this->getCurrentStep();
81 
82  $this->determineInstallStatus();
83 
85  if ($response) {
86  return $response;
87  }
88 
89  // check if this is an install being resumed
90  $response = $this->resumeInstall($step);
91  if ($response) {
92  return $response;
93  }
94 
95  $this->finishBootstrapping($step);
96 
97  $params = $app->internal_services->request->request->all();
98 
99  $method = 'run' . ucwords($step);
100 
101  return $this->$method($params);
102  }
103 
110  protected function getApp(): Application {
111  if ($this->app) {
112  return $this->app;
113  }
114 
115  try {
116  $config = new Config();
117  $config->installer_running = true;
118  $config->dbencoding = 'utf8mb4';
119  $config->boot_cache_ttl = 0;
120  $config->system_cache_enabled = false;
121  $config->simplecache_enabled = false;
122  $config->debug = \Psr\Log\LogLevel::WARNING;
123  $config->cacheroot = sys_get_temp_dir() . 'elgginstaller/caches';
124  $config->assetroot = sys_get_temp_dir() . 'elgginstaller/assets';
125 
126  $app = Application::factory([
127  'config' => $config,
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->internal_services->bootCache->disable();
142  $app->internal_services->pluginsCache->disable();
143  $app->internal_services->accessCache->disable();
144  $app->internal_services->metadataCache->disable();
145  $app->internal_services->serverCache->disable();
146 
147  $current_step = $this->getCurrentStep();
148  $index_admin = array_search('admin', $this->getSteps());
149  $index_complete = array_search('complete', $this->getSteps());
150  $index_step = array_search($current_step, $this->getSteps());
151 
152  // For the admin creation action and the complete step we use the Elgg core session handling.
153  // Otherwise, use default php session handling
154  $use_elgg_session = ($index_step == $index_admin) || ($index_step == $index_complete);
155  if (!$use_elgg_session) {
156  $session = \ElggSession::fromFiles($app->internal_services->config);
157  $session->setName('Elgg_install');
158  $app->internal_services->set('session', $session);
159  }
160 
161  $app->internal_services->views->setViewtype('installation');
162  $app->internal_services->views->registerViewtypeFallback('installation');
163  $app->internal_services->views->registerViewsFromPath(Paths::elgg());
164  $app->internal_services->translator->registerTranslations(Paths::elgg() . 'install/languages/', true);
165 
166  return $this->app;
167  } catch (ConfigurationException $ex) {
168  throw new InstallationException($ex->getMessage());
169  }
170  }
171 
187  public function batchInstall(array $params, bool $create_htaccess = false): void {
188  $app = $this->getApp();
189 
190  $defaults = [
191  'dbhost' => 'localhost',
192  'dbport' => '3306',
193  'dbprefix' => 'elgg_',
194  'language' => 'en',
195  'siteaccess' => ACCESS_PUBLIC,
196  ];
197  $params = array_merge($defaults, $params);
198 
199  $required_params = [
200  'dbuser',
201  'dbpassword',
202  'dbname',
203  'sitename',
204  'wwwroot',
205  'dataroot',
206  'displayname',
207  'email',
208  'username',
209  'password',
210  ];
211  foreach ($required_params as $key) {
212  if (empty($params[$key])) {
213  throw new InstallationException(elgg_echo('install:error:requiredfield', [$key]));
214  }
215  }
216 
217  // password is passed in once
218  $params['password1'] = $params['password'];
219  $params['password2'] = $params['password'];
220 
221  if ($create_htaccess) {
222  $rewrite_tester = new RewriteTester();
223  if (!$rewrite_tester->createHtaccess($params['wwwroot'])) {
224  throw new InstallationException(elgg_echo('install:error:htaccess'));
225  }
226  }
227 
228  if (!\Elgg\Http\Urls::isValidMultiByteUrl($params['wwwroot'])) {
229  throw new InstallationException(elgg_echo('install:error:wwwroot', [$params['wwwroot']]));
230  }
231 
232  // sanitize dataroot path
233  $params['dataroot'] = Paths::sanitize($params['dataroot']);
234 
235  $this->determineInstallStatus();
236 
237  if (!$this->has_completed['config']) {
238  if (!$this->createSettingsFile($params)) {
239  throw new InstallationException(elgg_echo('install:error:settings'));
240  }
241  }
242 
243  $this->loadSettingsFile();
244 
245  // Make sure settings file matches parameters
246  $config = $app->internal_services->config;
247  if ($params['dataroot'] !== $config->dataroot) {
248  throw new InstallationException(elgg_echo('install:error:settings_mismatch', ['dataroot', $params['dataroot'], $config->dataroot]));
249  }
250 
251  $db_config = $app->internal_services->dbConfig->getConnectionConfig();
252  $db_config_keys = [
253  // param key => db config key
254  'dbhost' => 'host',
255  'dbport' => 'port',
256  'dbuser' => 'user',
257  'dbpassword' => 'password',
258  'dbname' => 'database',
259  'dbprefix' => 'prefix',
260  ];
261  foreach ($db_config_keys as $params_key => $db_config_key) {
262  if ($params[$params_key] !== (string) $db_config[$db_config_key]) {
263  throw new InstallationException(elgg_echo('install:error:settings_mismatch', [$db_config_key, $params[$params_key], $db_config[$db_config_key]]));
264  }
265  }
266 
267  if (!$this->connectToDatabase()) {
268  throw new InstallationException(elgg_echo('install:error:databasesettings'));
269  }
270 
271  if (!$this->has_completed['database']) {
272  if (!$this->installDatabase()) {
273  throw new InstallationException(elgg_echo('install:error:cannotloadtables'));
274  }
275  }
276 
277  // load remaining core libraries
278  $this->finishBootstrapping('settings');
279 
280  if (!$this->saveSiteSettings($params)) {
281  throw new InstallationException(elgg_echo('install:error:savesitesettings'));
282  }
283 
284  if (!$this->createAdminAccount($params)) {
285  throw new InstallationException(elgg_echo('install:admin:cannot_create'));
286  }
287  }
288 
297  protected function render(string $step, array $vars = []): \Elgg\Http\OkResponse {
298  $vars['next_step'] = $this->getNextStep($step);
299 
300  $title = elgg_echo("install:{$step}");
301  $body = elgg_view("install/pages/{$step}", $vars);
302 
304  $title,
305  $body,
306  'default',
307  [
308  'step' => $step,
309  'steps' => $this->getSteps(),
310  ]
311  );
312 
313  return new \Elgg\Http\OkResponse($output);
314  }
315 
325  protected function runWelcome(): \Elgg\Http\OkResponse {
326  return $this->render('welcome');
327  }
328 
338  protected function runRequirements(array $vars = []): \Elgg\Http\OkResponse {
339 
340  $report = [];
341 
342  // check PHP parameters and libraries
343  $this->checkPHP($report);
344 
345  // check URL rewriting
346  $this->checkRewriteRules($report);
347 
348  // check for existence of settings file
349  if ($this->checkSettingsFile($report) !== true) {
350  // no file, so check permissions on engine directory
351  $this->isInstallDirWritable($report);
352  }
353 
354  // check the database later
355  $report['database'] = [
356  [
357  'severity' => 'notice',
358  'message' => elgg_echo('install:check:database'),
359  ],
360  ];
361 
362  return $this->render('requirements', [
363  'report' => $report,
364  'num_failures' => $this->countNumConditions($report, 'error'),
365  'num_warnings' => $this->countNumConditions($report, 'warning'),
366  ]);
367  }
368 
378  protected function runDatabase(array $submissionVars = []): \Elgg\Http\ResponseBuilder {
379 
380  $app = $this->getApp();
381 
382  $formVars = [
383  'dbuser' => [
384  'type' => 'text',
385  'value' => '',
386  'required' => true,
387  ],
388  'dbpassword' => [
389  'type' => 'password',
390  'value' => '',
391  'required' => false,
392  ],
393  'dbname' => [
394  'type' => 'text',
395  'value' => '',
396  'required' => true,
397  ],
398  'dbhost' => [
399  'type' => 'text',
400  'value' => 'localhost',
401  'required' => true,
402  ],
403  'dbport' => [
404  'type' => 'number',
405  'value' => 3306,
406  'required' => true,
407  'min' => 0,
408  'max' => 65535,
409  ],
410  'dbprefix' => [
411  'type' => 'text',
412  'value' => 'elgg_',
413  'required' => false,
414  ],
415  'dataroot' => [
416  'type' => 'text',
417  'value' => '',
418  'required' => true,
419  ],
420  'wwwroot' => [
421  'type' => 'url',
422  'value' => $app->internal_services->config->wwwroot,
423  'required' => true,
424  ],
425  'timezone' => [
426  'type' => 'dropdown',
427  'value' => 'UTC',
428  'options' => \DateTimeZone::listIdentifiers(),
429  'required' => true
430  ]
431  ];
432 
433  if ($this->checkSettingsFile()) {
434  // user manually created settings file so we fake out action test
435  $this->is_action = true;
436  }
437 
438  if ($this->is_action) {
439  $getResponse = function () use ($app, $submissionVars, $formVars) {
440  // only create settings file if it doesn't exist
441  if (!$this->checkSettingsFile()) {
442  if (!$this->validateDatabaseVars($submissionVars, $formVars)) {
443  // error so we break out of action and serve same page
444  return;
445  }
446 
447  if (!$this->createSettingsFile($submissionVars)) {
448  return;
449  }
450  }
451 
452  // check db version and connect
453  if (!$this->connectToDatabase()) {
454  return;
455  }
456 
457  if (!$this->installDatabase()) {
458  return;
459  }
460 
461  $app->internal_services->system_messages->addSuccessMessage(elgg_echo('install:success:database'));
462 
463  return $this->continueToNextStep('database');
464  };
465 
466  $response = $getResponse();
467  if ($response) {
468  return $response;
469  }
470  }
471 
472  $formVars = $this->makeFormSticky($formVars, $submissionVars);
473 
474  $params = ['variables' => $formVars,];
475 
476  if ($this->checkSettingsFile()) {
477  // settings file exists and we're here so failed to create database
478  $params['failure'] = true;
479  }
480 
481  return $this->render('database', $params);
482  }
483 
493  protected function runSettings(array $submissionVars = []): \Elgg\Http\ResponseBuilder {
494 
495  $app = $this->getApp();
496 
497  $formVars = [
498  'sitename' => [
499  'type' => 'text',
500  'value' => 'My New Community',
501  'required' => true,
502  ],
503  'siteemail' => [
504  'type' => 'email',
505  'value' => '',
506  'required' => false,
507  ],
508  'siteaccess' => [
509  'type' => 'access',
510  'value' => ACCESS_PUBLIC,
511  'required' => true,
512  ],
513  ];
514 
515  if ($this->is_action) {
516  $getResponse = function () use ($app, $submissionVars, $formVars) {
517 
518  if (!$this->validateSettingsVars($submissionVars, $formVars)) {
519  return;
520  }
521 
522  if (!$this->saveSiteSettings($submissionVars)) {
523  return;
524  }
525 
526  $app->internal_services->system_messages->addSuccessMessage(elgg_echo('install:success:settings'));
527 
528  return $this->continueToNextStep('settings');
529  };
530 
531  $response = $getResponse();
532  if ($response) {
533  return $response;
534  }
535  }
536 
537  $formVars = $this->makeFormSticky($formVars, $submissionVars);
538 
539  return $this->render('settings', ['variables' => $formVars]);
540  }
541 
551  protected function runAdmin(array $submissionVars = []): \Elgg\Http\ResponseBuilder {
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, true)) {
590  return;
591  }
592 
593  $app->internal_services->system_messages->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 
607  $lang = $app->internal_services->translator->getCurrentLanguage();
608  $translations = $app->internal_services->translator->getLoadedTranslations();
609 
610  $app->internal_services->translator->addTranslation($lang, [
611  'install:admin:help:password1' => sprintf(
612  $translations[$lang]['install:admin:help:password1'],
613  $app->internal_services->config->min_password_length
614  ),
615  ]);
616 
617  $formVars = $this->makeFormSticky($formVars, $submissionVars);
618 
619  return $this->render('admin', ['variables' => $formVars]);
620  }
621 
627  protected function runComplete(): \Elgg\Http\ResponseBuilder {
628 
629  // nudge to check out settings
630  $link = elgg_view_url(elgg_normalize_url('admin/site_settings'), elgg_echo('install:complete:admin_notice:link_text'));
631  $notice = elgg_format_element('p', [], elgg_echo('install:complete:admin_notice', [$link]));
632 
633  $custom_index_link = elgg_view_url(elgg_normalize_url('admin/plugin_settings/custom_index'), elgg_echo('admin:plugin_settings'));
634  $notice .= elgg_format_element('p', [], elgg_echo('install:complete:admin_notice:custom_index', [$custom_index_link]));
635 
636  elgg_add_admin_notice('fresh_install', $notice);
637 
638  $result = $this->render('complete');
639 
640  elgg_delete_directory(Paths::sanitize(sys_get_temp_dir()) . 'elgginstaller/');
641 
642  return $result;
643  }
644 
654  protected function getSteps(): array {
655  return $this->steps;
656  }
657 
663  protected function getCurrentStep(): string {
664  $step = get_input('step', 'welcome');
665 
666  return in_array($step, $this->getSteps()) ? $step : 'welcome';
667  }
668 
676  protected function continueToNextStep(string $currentStep): \Elgg\Http\RedirectResponse {
677  $this->is_action = false;
678 
679  return new \Elgg\Http\RedirectResponse($this->getNextStepUrl($currentStep));
680  }
681 
689  protected function getNextStep(string $currentStep): string {
690  $index = 1 + array_search($currentStep, $this->steps);
691 
692  return $this->steps[$index] ?? '';
693  }
694 
702  protected function getNextStepUrl(string $currentStep): string {
703  $app = $this->getApp();
704  $nextStep = $this->getNextStep($currentStep);
705 
706  return $app->internal_services->config->wwwroot . "install.php?step={$nextStep}";
707  }
708 
715  protected function determineInstallStatus(): void {
716  $app = $this->getApp();
717 
718  $path = Config::resolvePath();
719  if (!is_file($path) || !is_readable($path)) {
720  return;
721  }
722 
723  $this->loadSettingsFile();
724 
725  $this->has_completed['config'] = true;
726 
727  // must be able to connect to database to jump install steps
728  $dbSettingsPass = $this->checkDatabaseSettings($app->internal_services->dbConfig);
729 
730  if (!$dbSettingsPass) {
731  return;
732  }
733 
734  $db = $app->internal_services->db;
735 
736  try {
737  // check that the config table has been created
738  $result = $db->getConnection('read')->executeQuery('SHOW TABLES');
739  if (empty($result)) {
740  return;
741  }
742 
743  foreach ($result->fetchAllAssociative() as $table) {
744  if (in_array("{$db->prefix}config", $table)) {
745  $this->has_completed['database'] = true;
746  }
747  }
748 
749  if ($this->has_completed['database'] === false) {
750  return;
751  }
752 
753  // check that the config table has entries
754  $qb = \Elgg\Database\Select::fromTable(\Elgg\Database\ConfigTable::TABLE_NAME);
755  $qb->select('COUNT(*) AS total');
756 
757  $result = $db->getDataRow($qb);
758  if (!empty($result) && $result->total > 0) {
759  $this->has_completed['settings'] = true;
760  } else {
761  return;
762  }
763 
764  // check that the users entity table has an entry
765  $qb = \Elgg\Database\Select::fromTable(\Elgg\Database\EntityTable::TABLE_NAME, \Elgg\Database\EntityTable::DEFAULT_JOIN_ALIAS);
766  $qb->select('COUNT(*) AS total')
767  ->where($qb->compare('type', '=', 'user', ELGG_VALUE_STRING));
768 
769  $result = $db->getDataRow($qb);
770  if (!empty($result) && $result->total > 0) {
771  $this->has_completed['admin'] = true;
772  } else {
773  return;
774  }
775  } catch (DatabaseException $ex) {
776  throw new InstallationException('Elgg can not connect to the database: ' . $ex->getMessage(), $ex->getCode(), $ex);
777  }
778  }
779 
788  protected function checkInstallCompletion(string $step): ?\Elgg\Http\RedirectResponse {
789  if ($step === 'complete') {
790  return null;
791  }
792 
793  if (!in_array(false, $this->has_completed)) {
794  // install complete but someone is trying to view an install page
795  return new \Elgg\Http\RedirectResponse('/');
796  }
797 
798  return null;
799  }
800 
809  protected function resumeInstall(string $step): ?\Elgg\Http\RedirectResponse {
810  // only do a resume from the first step
811  if ($step !== 'welcome') {
812  return null;
813  }
814 
815  if ($this->has_completed['database'] === false) {
816  return null;
817  }
818 
819  if ($this->has_completed['settings'] === false) {
820  return new \Elgg\Http\RedirectResponse('install.php?step=settings');
821  }
822 
823  if ($this->has_completed['admin'] === false) {
824  return new \Elgg\Http\RedirectResponse('install.php?step=admin');
825  }
826 
827  // everything appears to be set up
828  return new \Elgg\Http\RedirectResponse('install.php?step=complete');
829  }
830 
843  protected function finishBootstrapping(string $step): void {
844 
845  $app = $this->getApp();
846 
847  $index_db = array_search('database', $this->getSteps());
848  $index_step = array_search($step, $this->getSteps());
849 
850  if ($index_step > $index_db) {
851  // once the database has been created, load rest of engine
852 
853  // dummy site needed to boot
854  $app->internal_services->config->site = new \ElggSite();
855 
856  $app->bootCore();
857  }
858  }
859 
866  protected function loadSettingsFile(): void {
867  try {
868  $app = $this->getApp();
869 
870  $config = Config::factory();
871  $app->internal_services->set('config', $app->internal_services->initConfig($config));
872 
873  // in case the DB instance is already captured in services, we re-inject its settings.
874  $app->internal_services->db->resetConnections($app->internal_services->dbConfig);
875  } catch (\Exception $e) {
876  throw new InstallationException(elgg_echo('InstallationException:CannotLoadSettings'), 0, $e);
877  }
878  }
879 
892  protected function makeFormSticky(array $formVars = [], array $submissionVars = []): array {
893  foreach ($submissionVars as $field => $value) {
894  $formVars[$field]['value'] = $value;
895  }
896 
897  return $formVars;
898  }
899 
900  /* Requirement checks support methods */
901 
909  protected function isInstallDirWritable(array &$report): bool {
910  if (!is_writable(Paths::projectConfig())) {
911  $report['settings'] = [
912  [
913  'severity' => 'error',
914  'message' => elgg_echo('install:check:installdir', [Paths::PATH_TO_CONFIG]),
915  ],
916  ];
917 
918  return false;
919  }
920 
921  return true;
922  }
923 
931  protected function checkSettingsFile(array &$report = []): bool {
932  if (!is_file(Config::resolvePath())) {
933  return false;
934  }
935 
936  if (!is_readable(Config::resolvePath())) {
937  $report['settings'] = [
938  [
939  'severity' => 'error',
940  'message' => elgg_echo('install:check:readsettings'),
941  ],
942  ];
943  }
944 
945  return true;
946  }
947 
955  protected function checkPHP(array &$report): void {
956  $phpReport = [];
957 
958  if (version_compare(PHP_VERSION, self::PHP_MINIMAL_VERSION, '<')) {
959  $phpReport[] = [
960  'severity' => 'error',
961  'message' => elgg_echo('install:check:php:version', [self::PHP_MINIMAL_VERSION, PHP_VERSION]),
962  ];
963  }
964 
965  $this->checkPhpExtensions($phpReport);
966 
967  $this->checkPhpDirectives($phpReport);
968 
969  if (count($phpReport) == 0) {
970  $phpReport[] = [
971  'severity' => 'success',
972  'message' => elgg_echo('install:check:php:success'),
973  ];
974  }
975 
976  $report['php'] = $phpReport;
977  }
978 
986  protected function checkPhpExtensions(array &$phpReport): void {
987  $extensions = get_loaded_extensions();
989  'pdo_mysql',
990  'json',
991  'xml',
992  'gd',
993  'intl',
994  ];
995  foreach ($requiredExtensions as $extension) {
996  if (!in_array($extension, $extensions)) {
997  $phpReport[] = [
998  'severity' => 'error',
999  'message' => elgg_echo('install:check:php:extension', [$extension]),
1000  ];
1001  }
1002  }
1003 
1005  'mbstring',
1006  ];
1007  foreach ($recommendedExtensions as $extension) {
1008  if (!in_array($extension, $extensions)) {
1009  $phpReport[] = [
1010  'severity' => 'warning',
1011  'message' => elgg_echo('install:check:php:extension:recommend', [$extension]),
1012  ];
1013  }
1014  }
1015  }
1016 
1024  protected function checkPhpDirectives(array &$phpReport): void {
1025  if (ini_get('open_basedir')) {
1026  $phpReport[] = [
1027  'severity' => 'warning',
1028  'message' => elgg_echo('install:check:php:open_basedir'),
1029  ];
1030  }
1031 
1032  if (ini_get('safe_mode')) {
1033  $phpReport[] = [
1034  'severity' => 'warning',
1035  'message' => elgg_echo('install:check:php:safe_mode'),
1036  ];
1037  }
1038 
1039  if (ini_get('arg_separator.output') !== '&') {
1040  $separator = htmlspecialchars(ini_get('arg_separator.output'));
1041  $phpReport[] = [
1042  'severity' => 'error',
1043  'message' => elgg_echo('install:check:php:arg_separator', [$separator]),
1044  ];
1045  }
1046 
1047  if (ini_get('register_globals')) {
1048  $phpReport[] = [
1049  'severity' => 'error',
1050  'message' => elgg_echo('install:check:php:register_globals'),
1051  ];
1052  }
1053 
1054  if (ini_get('session.auto_start')) {
1055  $phpReport[] = [
1056  'severity' => 'error',
1057  'message' => elgg_echo('install:check:php:session.auto_start'),
1058  ];
1059  }
1060  }
1061 
1069  protected function checkRewriteRules(array &$report): void {
1070  $tester = new RewriteTester();
1071 
1072  $url = $this->getApp()->internal_services->config->wwwroot;
1073  $url .= Request::REWRITE_TEST_TOKEN . '?' . http_build_query([Request::REWRITE_TEST_TOKEN => '1']);
1074 
1075  $report['rewrite'] = [$tester->run($url)];
1076  }
1077 
1086  protected function countNumConditions(array $report, string $condition): int {
1087  $count = 0;
1088  foreach ($report as $checks) {
1089  foreach ($checks as $check) {
1090  if ($check['severity'] === $condition) {
1091  $count++;
1092  }
1093  }
1094  }
1095 
1096  return $count;
1097  }
1098 
1111  protected function validateDatabaseVars(array $submissionVars, array $formVars): bool {
1112 
1113  $app = $this->getApp();
1114 
1115  foreach ($formVars as $field => $info) {
1116  if ($info['required'] === true && !$submissionVars[$field]) {
1117  $name = elgg_echo("install:database:label:{$field}");
1118  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:requiredfield', [$name]));
1119 
1120  return false;
1121  }
1122  }
1123 
1124  if (!empty($submissionVars['wwwroot']) && !\Elgg\Http\Urls::isValidMultiByteUrl($submissionVars['wwwroot'])) {
1125  $save_value = $this->sanitizeInputValue($submissionVars['wwwroot']);
1126  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:wwwroot', [$save_value]));
1127 
1128  return false;
1129  }
1130 
1131  // check that data root is absolute path
1132  if (stripos(PHP_OS, 'win') === 0) {
1133  if (strpos($submissionVars['dataroot'], ':') !== 1) {
1134  $save_value = $this->sanitizeInputValue($submissionVars['dataroot']);
1135  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:relative_path', [$save_value]));
1136 
1137  return false;
1138  }
1139  } else {
1140  if (!str_starts_with($submissionVars['dataroot'], '/')) {
1141  $save_value = $this->sanitizeInputValue($submissionVars['dataroot']);
1142  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:relative_path', [$save_value]));
1143 
1144  return false;
1145  }
1146  }
1147 
1148  // check that data root exists
1149  if (!is_dir($submissionVars['dataroot'])) {
1150  $save_value = $this->sanitizeInputValue($submissionVars['dataroot']);
1151  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:datadirectoryexists', [$save_value]));
1152 
1153  return false;
1154  }
1155 
1156  // check that data root is writable
1157  if (!is_writable($submissionVars['dataroot'])) {
1158  $save_value = $this->sanitizeInputValue($submissionVars['dataroot']);
1159  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:writedatadirectory', [$save_value]));
1160 
1161  return false;
1162  }
1163 
1164  // check that data root is not subdirectory of Elgg root
1165  if (stripos($submissionVars['dataroot'], Paths::project()) === 0) {
1166  $save_value = $this->sanitizeInputValue($submissionVars['dataroot']);
1167  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:locationdatadirectory', [$save_value]));
1168 
1169  return false;
1170  }
1171 
1172  // according to postgres documentation: SQL identifiers and key words must
1173  // begin with a letter (a-z, but also letters with diacritical marks and
1174  // non-Latin letters) or an underscore (_). Subsequent characters in an
1175  // identifier or key word can be letters, underscores, digits (0-9), or dollar signs ($).
1176  // Refs #4994
1177  if (!empty($submissionVars['dbprefix']) && !preg_match('/^[a-zA-Z_][\w]*$/', $submissionVars['dbprefix'])) {
1178  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:database_prefix'));
1179 
1180  return false;
1181  }
1182 
1183  $config = new DbConfig((object) [
1184  'dbhost' => $submissionVars['dbhost'],
1185  'dbport' => $submissionVars['dbport'],
1186  'dbuser' => $submissionVars['dbuser'],
1187  'dbpass' => $submissionVars['dbpassword'],
1188  'dbname' => $submissionVars['dbname'],
1189  'dbencoding' => 'utf8mb4',
1190  ]);
1191 
1192  return $this->checkDatabaseSettings($config);
1193  }
1194 
1202  protected function checkDatabaseSettings(DbConfig $config): bool {
1203  $app = $this->getApp();
1204 
1205  $db = new Database($config, $app->internal_services->queryCache, $app->internal_services->config);
1206 
1207  try {
1208  $db->getConnection('read')->executeQuery('SELECT 1');
1209  } catch (DatabaseException $e) {
1210  if (str_starts_with($e->getMessage(), "Elgg couldn't connect")) {
1211  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:databasesettings'));
1212  } else {
1213  $database = (string) elgg_extract('database', $config->getConnectionConfig());
1214  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:nodatabase', [$database]));
1215  }
1216 
1217  return false;
1218  }
1219 
1220  // check MySQL version
1221  $version = $db->getServerVersion();
1222  $min_version = $db->isMariaDB() ? self::MARIADB_MINIMAL_VERSION : self::MYSQL_MINIMAL_VERSION;
1223 
1224  if (version_compare($version, $min_version, '<')) {
1225  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:database_version', [$min_version, $version]));
1226 
1227  return false;
1228  }
1229 
1230  return true;
1231  }
1232 
1240  protected function createSettingsFile(array $params): bool {
1241  $app = $this->getApp();
1242 
1243  $template = file_get_contents(Paths::elgg() . 'elgg-config/settings.example.php');
1244  if (!$template) {
1245  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:readsettingsphp'));
1246 
1247  return false;
1248  }
1249 
1250  foreach ($params as $k => $v) {
1251  // do some sanitization
1252  switch ($k) {
1253  case 'dataroot':
1254  $v = Paths::sanitize($v);
1255  break;
1256  case 'dbpassword':
1257  $v = addslashes($v);
1258  break;
1259  }
1260 
1261  $template = str_replace('{{' . $k . '}}', $v, $template);
1262  }
1263 
1264  $result = file_put_contents(Config::resolvePath(), $template);
1265  if ($result === false) {
1266  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:writesettingphp'));
1267 
1268  return false;
1269  }
1270 
1271  $config = (object) [
1272  'dbhost' => elgg_extract('dbhost', $params, 'localhost'),
1273  'dbport' => elgg_extract('dbport', $params, 3306),
1274  'dbuser' => elgg_extract('dbuser', $params),
1275  'dbpass' => elgg_extract('dbpassword', $params),
1276  'dbname' => elgg_extract('dbname', $params),
1277  'dbencoding' => elgg_extract('dbencoding', $params, 'utf8mb4'),
1278  'dbprefix' => elgg_extract('dbprefix', $params, 'elgg_'),
1279  ];
1280 
1281  $dbConfig = new DbConfig($config);
1282  $this->getApp()->internal_services->set('dbConfig', $dbConfig);
1283  $this->getApp()->internal_services->db->resetConnections($dbConfig);
1284 
1285  return true;
1286  }
1287 
1293  protected function connectToDatabase(): bool {
1294  try {
1295  $app = $this->getApp();
1296  $app->internal_services->db->setupConnections();
1297  } catch (DatabaseException $e) {
1298  $app->internal_services->system_messages->addErrorMessage($e->getMessage());
1299 
1300  return false;
1301  }
1302 
1303  return true;
1304  }
1305 
1311  protected function installDatabase(): bool {
1312  try {
1313  return $this->getApp()->migrate();
1314  } catch (\Exception $e) {
1315  return false;
1316  }
1317  }
1318 
1331  protected function createDataDirectory(array &$submissionVars, array $formVars): bool {
1332  // did the user have option of Elgg creating the data directory
1333  if ($formVars['dataroot']['type'] !== 'combo') {
1334  return true;
1335  }
1336 
1337  // did the user select the option
1338  if ($submissionVars['dataroot'] !== 'dataroot-checkbox') {
1339  return true;
1340  }
1341 
1342  $dir = \Elgg\Project\Paths::sanitize($submissionVars['path']) . 'data';
1343  if (file_exists($dir) || mkdir($dir, 0755)) {
1344  $submissionVars['dataroot'] = $dir;
1345  if (!file_exists("{$dir}/.htaccess")) {
1346  $htaccess = "Order Deny,Allow\nDeny from All\n";
1347  if (!file_put_contents("$dir/.htaccess", $htaccess)) {
1348  return false;
1349  }
1350  }
1351 
1352  return true;
1353  }
1354 
1355  return false;
1356  }
1357 
1366  protected function validateSettingsVars(array $submissionVars, array $formVars): bool {
1367  $app = $this->getApp();
1368 
1369  foreach ($formVars as $field => $info) {
1370  $submissionVars[$field] = trim($submissionVars[$field]);
1371  if ($info['required'] === true && $submissionVars[$field] === '') {
1372  $name = elgg_echo("install:settings:label:{$field}");
1373  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:requiredfield', [$name]));
1374 
1375  return false;
1376  }
1377  }
1378 
1379  // check that email address is email address
1380  if ($submissionVars['siteemail'] && !elgg_is_valid_email((string) $submissionVars['siteemail'])) {
1381  $save_value = $this->sanitizeInputValue($submissionVars['siteemail']);
1382  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:emailaddress', [$save_value]));
1383 
1384  return false;
1385  }
1386 
1387  return true;
1388  }
1389 
1397  protected function saveSiteSettings(array $submissionVars): bool {
1398  $app = $this->getApp();
1399 
1401 
1402  if (!$site->guid) {
1403  $site = new \ElggSite();
1404  $site->name = strip_tags($submissionVars['sitename']);
1405  $site->access_id = ACCESS_PUBLIC;
1406  $site->email = $submissionVars['siteemail'];
1407  $site->save();
1408  }
1409 
1410  if ($site->guid !== 1) {
1411  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:createsite'));
1412 
1413  return false;
1414  }
1415 
1416  $app->internal_services->config->site = $site;
1417 
1418  $sets = [
1419  'installed' => time(),
1420  'simplecache_enabled' => 1,
1421  'system_cache_enabled' => 1,
1422  'simplecache_minify_js' => true,
1423  'simplecache_minify_css' => true,
1424  'lastcache' => time(),
1425  'language' => 'en',
1426  'default_access' => $submissionVars['siteaccess'],
1427  'allow_registration' => false,
1428  'require_admin_validation' => false,
1429  'walled_garden' => false,
1430  'allow_user_default_access' => '',
1431  'default_limit' => 10,
1432  ];
1433 
1434  foreach ($sets as $key => $value) {
1436  }
1437 
1438  try {
1439  _elgg_services()->plugins->generateEntities();
1440 
1441  $app->internal_services->reset('plugins');
1442 
1443  if (elgg_extract('activate_plugins', $submissionVars, true)) {
1444  $plugins = $app->internal_services->plugins->find('all');
1445 
1446  foreach ($plugins as $plugin) {
1447  $plugin_config = $plugin->getStaticConfig('plugin', []);
1448  if (!elgg_extract('activate_on_install', $plugin_config, false)) {
1449  continue;
1450  }
1451 
1452  try {
1453  $plugin->activate();
1454  } catch (PluginException $e) {
1455  // do nothing
1456  }
1457  }
1458  }
1459 
1460  // Wo don't need to run upgrades on new installations
1461  $app->internal_services->events->unregisterHandler('create:after', 'object', \Elgg\Upgrade\CreateAdminNoticeHandler::class);
1462  $upgrades = $app->internal_services->upgradeLocator->locate();
1463  foreach ($upgrades as $upgrade) {
1464  $upgrade->setCompleted();
1465  }
1466  } catch (\Exception $e) {
1467  $app->internal_services->logger->log(\Psr\Log\LogLevel::ERROR, $e);
1468  }
1469 
1470  return true;
1471  }
1472 
1481  protected function validateAdminVars(array $submissionVars, array $formVars): bool {
1482 
1483  $app = $this->getApp();
1484 
1485  foreach ($formVars as $field => $info) {
1486  if ($info['required'] === true && !$submissionVars[$field]) {
1487  $name = elgg_echo("install:admin:label:{$field}");
1488  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:requiredfield', [$name]));
1489 
1490  return false;
1491  }
1492  }
1493 
1494  if ($submissionVars['password1'] !== $submissionVars['password2']) {
1495  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:admin:password:mismatch'));
1496 
1497  return false;
1498  }
1499 
1500  if (trim($submissionVars['password1']) === '') {
1501  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:admin:password:empty'));
1502 
1503  return false;
1504  }
1505 
1506  $minLength = $app->internal_services->configTable->get('min_password_length');
1507  if (strlen($submissionVars['password1']) < $minLength) {
1508  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:admin:password:tooshort'));
1509 
1510  return false;
1511  }
1512 
1513  // check that email address is email address
1514  if ($submissionVars['email'] && !elgg_is_valid_email((string) $submissionVars['email'])) {
1515  $save_value = $this->sanitizeInputValue($submissionVars['email']);
1516  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:emailaddress', [$save_value]));
1517 
1518  return false;
1519  }
1520 
1521  return true;
1522  }
1523 
1532  protected function createAdminAccount(array $submissionVars, bool $login = false): bool {
1533  $app = $this->getApp();
1534 
1535  try {
1537  'username' => $submissionVars['username'],
1538  'password' => $submissionVars['password1'],
1539  'name' => $submissionVars['displayname'],
1540  'email' => $submissionVars['email'],
1541  ]);
1542  } catch (RegistrationException $e) {
1543  $app->internal_services->system_messages->addErrorMessage($e->getMessage());
1544 
1545  return false;
1546  }
1547 
1548  elgg_call(ELGG_IGNORE_ACCESS, function() use ($app, $user) {
1549  if (!$user->makeAdmin()) {
1550  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:adminaccess'));
1551  }
1552  });
1553 
1554  // add validation data to satisfy user validation plugins
1555  $user->validated = true;
1556  $user->validated_method = 'admin_user';
1557 
1558  if (!$login) {
1559  return true;
1560  }
1561 
1562  try {
1563  elgg_login($user);
1564  } catch (LoginException $ex) {
1565  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:adminlogin'));
1566 
1567  return false;
1568  }
1569 
1570  return true;
1571  }
1572 
1580  protected function sanitizeInputValue($input_value) {
1581  if (is_array($input_value)) {
1582  return array_map([$this, __FUNCTION__], $input_value);
1583  }
1584 
1585  if (!is_string($input_value)) {
1586  return $input_value;
1587  }
1588 
1589  return htmlspecialchars($input_value);
1590  }
1591 }
$site
Definition: icons.php:5
$vars
Definition: theme.php:5
if(empty($guid)) $upgrade
Definition: upgrade.php:11
if(! $user||! $user->canDelete()) $name
Definition: delete.php:22
$params
Saves global plugin settings.
Definition: save.php:13
$body
Definition: useradd.php:55
return[ 'admin/delete_admin_notices'=>['access'=> 'admin'], 'admin/menu/save'=>['access'=> 'admin'], 'admin/plugins/activate'=>['access'=> 'admin'], 'admin/plugins/activate_all'=>['access'=> 'admin'], 'admin/plugins/deactivate'=>['access'=> 'admin'], 'admin/plugins/deactivate_all'=>['access'=> 'admin'], 'admin/plugins/set_priority'=>['access'=> 'admin'], 'admin/security/security_txt'=>['access'=> 'admin'], 'admin/security/settings'=>['access'=> 'admin'], 'admin/security/regenerate_site_secret'=>['access'=> 'admin'], 'admin/site/cache/invalidate'=>['access'=> 'admin'], 'admin/site/flush_cache'=>['access'=> 'admin'], 'admin/site/icons'=>['access'=> 'admin'], 'admin/site/set_maintenance_mode'=>['access'=> 'admin'], 'admin/site/set_robots'=>['access'=> 'admin'], 'admin/site/theme'=>['access'=> 'admin'], 'admin/site/unlock_upgrade'=>['access'=> 'admin'], 'admin/site/settings'=>['access'=> 'admin'], 'admin/upgrade'=>['access'=> 'admin'], 'admin/upgrade/reset'=>['access'=> 'admin'], 'admin/user/ban'=>['access'=> 'admin'], 'admin/user/bulk/ban'=>['access'=> 'admin'], 'admin/user/bulk/delete'=>['access'=> 'admin'], 'admin/user/bulk/unban'=>['access'=> 'admin'], 'admin/user/bulk/validate'=>['access'=> 'admin'], 'admin/user/change_email'=>['access'=> 'admin'], 'admin/user/delete'=>['access'=> 'admin'], 'admin/user/login_as'=>['access'=> 'admin'], 'admin/user/logout_as'=>[], 'admin/user/makeadmin'=>['access'=> 'admin'], 'admin/user/resetpassword'=>['access'=> 'admin'], 'admin/user/removeadmin'=>['access'=> 'admin'], 'admin/user/unban'=>['access'=> 'admin'], 'admin/user/validate'=>['access'=> 'admin'], 'annotation/delete'=>[], 'avatar/upload'=>[], 'comment/save'=>[], 'diagnostics/download'=>['access'=> 'admin'], 'entity/chooserestoredestination'=>[], 'entity/delete'=>[], 'entity/mute'=>[], 'entity/restore'=>[], 'entity/subscribe'=>[], 'entity/trash'=>[], 'entity/unmute'=>[], 'entity/unsubscribe'=>[], 'login'=>['access'=> 'logged_out'], 'logout'=>[], 'notifications/mute'=>['access'=> 'public'], 'plugins/settings/remove'=>['access'=> 'admin'], 'plugins/settings/save'=>['access'=> 'admin'], 'plugins/usersettings/save'=>[], 'register'=>['access'=> 'logged_out', 'middleware'=>[\Elgg\Router\Middleware\RegistrationAllowedGatekeeper::class,],], 'river/delete'=>[], 'settings/notifications'=>[], 'settings/notifications/subscriptions'=>[], 'user/changepassword'=>['access'=> 'public'], 'user/requestnewpassword'=>['access'=> 'public'], 'useradd'=>['access'=> 'admin'], 'usersettings/save'=>[], 'widgets/add'=>[], 'widgets/delete'=>[], 'widgets/move'=>[], 'widgets/save'=>[],]
Definition: actions.php:73
$table
Definition: user.php:37
foreach( $paths as $path)
Definition: autoloader.php:12
$user
Definition: ban.php:7
$count
Definition: ban.php:24
Elgg Installer.
getNextStepUrl(string $currentStep)
Get the URL of the next step.
run()
Dispatches a request to one of the step controllers.
batchInstall(array $params, bool $create_htaccess=false)
A batch install of Elgg.
const PHP_MINIMAL_VERSION
array $has_completed
runWelcome()
Step controllers.
sanitizeInputValue($input_value)
Sanitize input to help prevent XSS.
runDatabase(array $submissionVars=[])
Database set up controller.
getApp()
Build the application needed by the installer.
getSteps()
Step management.
resumeInstall(string $step)
Check if this is a case of a install being resumed and figure out where to continue from.
continueToNextStep(string $currentStep)
Forwards the browser to the next step.
saveSiteSettings(array $submissionVars)
Initialize the site including site entity, plugins, and configuration.
determineInstallStatus()
Updates $this->has_completed according to the current installation.
const MYSQL_MINIMAL_VERSION
runComplete()
Controller for last step.
validateAdminVars(array $submissionVars, array $formVars)
Validate account form variables.
getNextStep(string $currentStep)
Get the next step as a string.
checkSettingsFile(array &$report=[])
Check that the settings file exists.
validateSettingsVars(array $submissionVars, array $formVars)
Validate the site settings form variables.
createSettingsFile(array $params)
Writes the settings file to the engine directory.
createAdminAccount(array $submissionVars, bool $login=false)
Create a user account for the admin.
render(string $step, array $vars=[])
Renders the data passed by a controller.
checkPhpExtensions(array &$phpReport)
Check the server's PHP extensions.
validateDatabaseVars(array $submissionVars, array $formVars)
Database support methods.
finishBootstrapping(string $step)
Bootstrapping.
runAdmin(array $submissionVars=[])
Admin account controller.
checkInstallCompletion(string $step)
Security check to ensure the installer cannot be run after installation has finished.
getCurrentStep()
Returns current step.
const MARIADB_MINIMAL_VERSION
countNumConditions(array $report, string $condition)
Count the number of failures in the requirements report.
connectToDatabase()
Bootstrap database connection before entire engine is available.
runRequirements(array $vars=[])
Requirements controller.
makeFormSticky(array $formVars=[], array $submissionVars=[])
Action handling methods.
checkPHP(array &$report)
Check version of PHP, extensions, and variables.
loadSettingsFile()
Load settings.
checkPhpDirectives(array &$phpReport)
Check PHP parameters.
checkRewriteRules(array &$report)
Confirm that the rewrite rules are firing.
createDataDirectory(array &$submissionVars, array $formVars)
Site settings support methods.
installDatabase()
Create the database tables.
checkDatabaseSettings(DbConfig $config)
Confirm the settings for the database.
runSettings(array $submissionVars=[])
Site settings controller.
isInstallDirWritable(array &$report)
Indicates whether the webserver can add settings.php on its own or not.
static fromFiles(Config $config)
Create a session stored in files.
Load, boot, and implement a front controller for an Elgg application.
Definition: Application.php:47
Database configuration service.
Definition: DbConfig.php:13
A generic parent class for Configuration exceptions.
Thrown when there is a major problem with the installation.
Could not register a new user for whatever reason.
A generic parent class for database exceptions.
Generic parent class for login exceptions.
Elgg HTTP request.
Definition: Request.php:17
Find Elgg and project paths.
Definition: Paths.php:8
static sanitize($path, $append_slash=true)
Sanitize file paths ensuring that they begin and end with slashes etc.
Definition: Paths.php:76
Test if URL rewriting is working.
elgg_save_config(string $name, $value)
Save a configuration setting.
const ELGG_VALUE_STRING
Definition: constants.php:112
const ELGG_IGNORE_ACCESS
elgg_call() flags
Definition: constants.php:121
const ACCESS_PUBLIC
Definition: constants.php:12
if(! $item instanceof ElggEntity) $link
Definition: container.php:16
foreach($plugin_guids as $guid) if(empty($deactivated_plugins)) $url
Definition: deactivate.php:39
$config
Advanced site settings, debugging section.
Definition: debugging.php:6
$version
foreach($requiredExtensions as $extension) $recommendedExtensions
$extensions
$min_version
foreach($recommendedExtensions as $extension) if(empty(ini_get('session.gc_probability'))||empty(ini_get('session.gc_divisor'))) $db
$tester
$requiredExtensions
$index
Definition: gallery.php:40
$output
Definition: download.php:9
if(elgg_extract('input_type', $vars)) if(elgg_extract('required', $vars)) if(elgg_extract('disabled', $vars)) $field
Definition: field.php:42
elgg()
Bootstrapping and helper procedural code available for use in Elgg core and plugins.
Definition: elgglib.php:12
_elgg_services()
Get the global service provider.
Definition: elgglib.php:353
elgg_call(int $flags, Closure $closure)
Calls a callable autowiring the arguments using public DI services and applying logic based on flags.
Definition: elgglib.php:306
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:256
elgg_add_admin_notice(string $id, string $message)
Write a persistent message to the admin view.
Definition: admin.php:51
elgg_is_valid_email(string $address)
Validates an email address.
Definition: input.php:89
get_input(string $variable, $default=null, bool $filter_result=true)
Parameter input functions.
Definition: input.php:20
elgg_register_user(array $params=[])
Registers a user.
Definition: users.php:154
elgg_get_site_entity()
Get the current site entity.
Definition: entities.php:101
$defaults
Generic entity header upload helper.
Definition: header.php:6
elgg_delete_directory(string $directory, bool $leave_base_directory=false)
Delete a directory and all its contents.
Definition: filestore.php:51
$value
Definition: generic.php:51
$title
Definition: generic.php:50
$step
Definition: time.php:40
$report
$current_step
Install sidebar.
Definition: sidebar.php:9
foreach(array_keys($combine_languages) as $language) $translations
elgg_echo(string $message_key, array $args=[], string $language='')
Elgg language module Functions to manage language and translations.
Definition: languages.php:17
elgg_view_page(string $title, string|array $body, string $page_shell='default', array $vars=[])
Assembles and outputs a full page.
Definition: views.php:235
elgg_view_url(string $href, ?string $text=null, array $options=[])
Helper function for outputting urls.
Definition: views.php:1427
elgg_view(string $view, array $vars=[], string $viewtype='')
Return a parsed view.
Definition: views.php:156
try
Definition: login_as.php:33
if(isset($_COOKIE['elggperm'])) $session
Definition: login_as.php:29
string project
Definition: conf.py:52
$path
Definition: details.php:70
$separator
Definition: tags.php:56
elgg_normalize_url(string $url)
Definition: output.php:163
elgg_format_element(string $tag_name, array $attributes=[], string $text='', array $options=[])
Format an HTML element.
Definition: output.php:145
$lang
Definition: html.php:13
$qb
Definition: queue.php:12
if($container instanceof ElggGroup && $container->guid !=elgg_get_page_owner_guid()) $key
Definition: summary.php:44
if(parse_url(elgg_get_site_url(), PHP_URL_PATH) !=='/') if(file_exists(elgg_get_root_path() . 'robots.txt'))
Set robots.txt.
Definition: robots.php:10
$extension
Definition: default.php:25
elgg_login(\ElggUser $user, bool $persistent=false)
Log in a user.
Definition: sessions.php:81
$plugin
$upgrades
Lists pending upgrades.
Definition: upgrades.php:11
$response
Definition: content.php:10