Elgg  Version 6.3
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 installation 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(DbConfig::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 
872  // make sure PHPUnit testing mode persists
873  $config->testing_mode = $app->internal_services->config->testing_mode;
874 
875  $app->internal_services->set('config', $app->internal_services->initConfig($config));
876 
877  // in case the DB instance is already captured in services, we re-inject its settings.
878  $app->internal_services->db->resetConnections($app->internal_services->dbConfig);
879  } catch (\Exception $e) {
880  throw new InstallationException(elgg_echo('InstallationException:CannotLoadSettings'), 0, $e);
881  }
882  }
883 
896  protected function makeFormSticky(array $formVars = [], array $submissionVars = []): array {
897  foreach ($submissionVars as $field => $value) {
898  $formVars[$field]['value'] = $value;
899  }
900 
901  return $formVars;
902  }
903 
904  /* Requirement checks support methods */
905 
913  protected function isInstallDirWritable(array &$report): bool {
914  if (!is_writable(Paths::projectConfig())) {
915  $report['settings'] = [
916  [
917  'severity' => 'error',
918  'message' => elgg_echo('install:check:installdir', [Paths::PATH_TO_CONFIG]),
919  ],
920  ];
921 
922  return false;
923  }
924 
925  return true;
926  }
927 
935  protected function checkSettingsFile(array &$report = []): bool {
936  if (!is_file(Config::resolvePath())) {
937  return false;
938  }
939 
940  if (!is_readable(Config::resolvePath())) {
941  $report['settings'] = [
942  [
943  'severity' => 'error',
944  'message' => elgg_echo('install:check:readsettings'),
945  ],
946  ];
947  }
948 
949  return true;
950  }
951 
959  protected function checkPHP(array &$report): void {
960  $phpReport = [];
961 
962  if (version_compare(PHP_VERSION, self::PHP_MINIMAL_VERSION, '<')) {
963  $phpReport[] = [
964  'severity' => 'error',
965  'message' => elgg_echo('install:check:php:version', [self::PHP_MINIMAL_VERSION, PHP_VERSION]),
966  ];
967  }
968 
969  $this->checkPhpExtensions($phpReport);
970 
971  $this->checkPhpDirectives($phpReport);
972 
973  if (count($phpReport) == 0) {
974  $phpReport[] = [
975  'severity' => 'success',
976  'message' => elgg_echo('install:check:php:success'),
977  ];
978  }
979 
980  $report['php'] = $phpReport;
981  }
982 
990  protected function checkPhpExtensions(array &$phpReport): void {
991  $extensions = get_loaded_extensions();
993  'pdo_mysql',
994  'json',
995  'xml',
996  'gd',
997  'intl',
998  ];
999  foreach ($requiredExtensions as $extension) {
1000  if (!in_array($extension, $extensions)) {
1001  $phpReport[] = [
1002  'severity' => 'error',
1003  'message' => elgg_echo('install:check:php:extension', [$extension]),
1004  ];
1005  }
1006  }
1007 
1009  'mbstring',
1010  ];
1011  foreach ($recommendedExtensions as $extension) {
1012  if (!in_array($extension, $extensions)) {
1013  $phpReport[] = [
1014  'severity' => 'warning',
1015  'message' => elgg_echo('install:check:php:extension:recommend', [$extension]),
1016  ];
1017  }
1018  }
1019  }
1020 
1028  protected function checkPhpDirectives(array &$phpReport): void {
1029  if (ini_get('open_basedir')) {
1030  $phpReport[] = [
1031  'severity' => 'warning',
1032  'message' => elgg_echo('install:check:php:open_basedir'),
1033  ];
1034  }
1035 
1036  if (ini_get('safe_mode')) {
1037  $phpReport[] = [
1038  'severity' => 'warning',
1039  'message' => elgg_echo('install:check:php:safe_mode'),
1040  ];
1041  }
1042 
1043  if (ini_get('arg_separator.output') !== '&') {
1044  $separator = htmlspecialchars(ini_get('arg_separator.output'));
1045  $phpReport[] = [
1046  'severity' => 'error',
1047  'message' => elgg_echo('install:check:php:arg_separator', [$separator]),
1048  ];
1049  }
1050 
1051  if (ini_get('register_globals')) {
1052  $phpReport[] = [
1053  'severity' => 'error',
1054  'message' => elgg_echo('install:check:php:register_globals'),
1055  ];
1056  }
1057 
1058  if (ini_get('session.auto_start')) {
1059  $phpReport[] = [
1060  'severity' => 'error',
1061  'message' => elgg_echo('install:check:php:session.auto_start'),
1062  ];
1063  }
1064  }
1065 
1073  protected function checkRewriteRules(array &$report): void {
1074  $tester = new RewriteTester();
1075 
1076  $url = $this->getApp()->internal_services->config->wwwroot;
1077  $url .= Request::REWRITE_TEST_TOKEN . '?' . http_build_query([Request::REWRITE_TEST_TOKEN => '1']);
1078 
1079  $report['rewrite'] = [$tester->run($url)];
1080  }
1081 
1090  protected function countNumConditions(array $report, string $condition): int {
1091  $count = 0;
1092  foreach ($report as $checks) {
1093  foreach ($checks as $check) {
1094  if ($check['severity'] === $condition) {
1095  $count++;
1096  }
1097  }
1098  }
1099 
1100  return $count;
1101  }
1102 
1115  protected function validateDatabaseVars(array $submissionVars, array $formVars): bool {
1116 
1117  $app = $this->getApp();
1118 
1119  foreach ($formVars as $field => $info) {
1120  if ($info['required'] === true && !$submissionVars[$field]) {
1121  $name = elgg_echo("install:database:label:{$field}");
1122  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:requiredfield', [$name]));
1123 
1124  return false;
1125  }
1126  }
1127 
1128  if (!empty($submissionVars['wwwroot']) && !\Elgg\Http\Urls::isValidMultiByteUrl($submissionVars['wwwroot'])) {
1129  $save_value = $this->sanitizeInputValue($submissionVars['wwwroot']);
1130  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:wwwroot', [$save_value]));
1131 
1132  return false;
1133  }
1134 
1135  // check that data root is absolute path
1136  if (stripos(PHP_OS, 'win') === 0) {
1137  if (strpos($submissionVars['dataroot'], ':') !== 1) {
1138  $save_value = $this->sanitizeInputValue($submissionVars['dataroot']);
1139  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:relative_path', [$save_value]));
1140 
1141  return false;
1142  }
1143  } else {
1144  if (!str_starts_with($submissionVars['dataroot'], '/')) {
1145  $save_value = $this->sanitizeInputValue($submissionVars['dataroot']);
1146  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:relative_path', [$save_value]));
1147 
1148  return false;
1149  }
1150  }
1151 
1152  // check that data root exists
1153  if (!is_dir($submissionVars['dataroot'])) {
1154  $save_value = $this->sanitizeInputValue($submissionVars['dataroot']);
1155  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:datadirectoryexists', [$save_value]));
1156 
1157  return false;
1158  }
1159 
1160  // check that data root is writable
1161  if (!is_writable($submissionVars['dataroot'])) {
1162  $save_value = $this->sanitizeInputValue($submissionVars['dataroot']);
1163  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:writedatadirectory', [$save_value]));
1164 
1165  return false;
1166  }
1167 
1168  // check that data root is not subdirectory of Elgg root
1169  if (stripos($submissionVars['dataroot'], Paths::project()) === 0) {
1170  $save_value = $this->sanitizeInputValue($submissionVars['dataroot']);
1171  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:locationdatadirectory', [$save_value]));
1172 
1173  return false;
1174  }
1175 
1176  // according to postgres documentation: SQL identifiers and key words must
1177  // begin with a letter (a-z, but also letters with diacritical marks and
1178  // non-Latin letters) or an underscore (_). Subsequent characters in an
1179  // identifier or key word can be letters, underscores, digits (0-9), or dollar signs ($).
1180  // Refs #4994
1181  if (!empty($submissionVars['dbprefix']) && !preg_match('/^[a-zA-Z_][\w]*$/', $submissionVars['dbprefix'])) {
1182  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:database_prefix'));
1183 
1184  return false;
1185  }
1186 
1187  $config = new DbConfig((object) [
1188  'dbhost' => $submissionVars['dbhost'],
1189  'dbport' => $submissionVars['dbport'],
1190  'dbuser' => $submissionVars['dbuser'],
1191  'dbpass' => $submissionVars['dbpassword'],
1192  'dbname' => $submissionVars['dbname'],
1193  'dbencoding' => 'utf8mb4',
1194  ]);
1195 
1196  return $this->checkDatabaseSettings($config);
1197  }
1198 
1206  protected function checkDatabaseSettings(DbConfig $config): bool {
1207  $app = $this->getApp();
1208 
1209  $db = new Database($config, $app->internal_services->queryCache, $app->internal_services->config);
1210 
1211  try {
1212  $db->getConnection(DbConfig::READ)->executeQuery('SELECT 1');
1213  } catch (DatabaseException $e) {
1214  if (str_starts_with($e->getMessage(), "Elgg couldn't connect")) {
1215  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:databasesettings'));
1216  } else {
1217  $database = (string) elgg_extract('database', $config->getConnectionConfig());
1218  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:nodatabase', [$database]));
1219  }
1220 
1221  return false;
1222  }
1223 
1224  // check MySQL version
1225  $version = $db->getServerVersion();
1226  $min_version = $db->isMariaDB() ? self::MARIADB_MINIMAL_VERSION : self::MYSQL_MINIMAL_VERSION;
1227 
1228  if (version_compare($version, $min_version, '<')) {
1229  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:database_version', [$min_version, $version]));
1230 
1231  return false;
1232  }
1233 
1234  return true;
1235  }
1236 
1244  protected function createSettingsFile(array $params): bool {
1245  $app = $this->getApp();
1246 
1247  $template = file_get_contents(Paths::elgg() . 'elgg-config/settings.example.php');
1248  if (!$template) {
1249  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:readsettingsphp'));
1250 
1251  return false;
1252  }
1253 
1254  foreach ($params as $k => $v) {
1255  // do some sanitization
1256  switch ($k) {
1257  case 'dataroot':
1258  $v = Paths::sanitize($v);
1259  break;
1260  case 'dbpassword':
1261  $v = addslashes($v);
1262  break;
1263  }
1264 
1265  $template = str_replace('{{' . $k . '}}', $v, $template);
1266  }
1267 
1268  $result = file_put_contents(Config::resolvePath(), $template);
1269  if ($result === false) {
1270  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:writesettingphp'));
1271 
1272  return false;
1273  }
1274 
1275  $config = (object) [
1276  'dbhost' => elgg_extract('dbhost', $params, 'localhost'),
1277  'dbport' => elgg_extract('dbport', $params, 3306),
1278  'dbuser' => elgg_extract('dbuser', $params),
1279  'dbpass' => elgg_extract('dbpassword', $params),
1280  'dbname' => elgg_extract('dbname', $params),
1281  'dbencoding' => elgg_extract('dbencoding', $params, 'utf8mb4'),
1282  'dbprefix' => elgg_extract('dbprefix', $params, 'elgg_'),
1283  ];
1284 
1285  $dbConfig = new DbConfig($config);
1286  $this->getApp()->internal_services->set('dbConfig', $dbConfig);
1287  $this->getApp()->internal_services->db->resetConnections($dbConfig);
1288 
1289  return true;
1290  }
1291 
1297  protected function connectToDatabase(): bool {
1298  try {
1299  $app = $this->getApp();
1300  $app->internal_services->db->setupConnections();
1301  } catch (DatabaseException $e) {
1302  $app->internal_services->system_messages->addErrorMessage($e->getMessage());
1303 
1304  return false;
1305  }
1306 
1307  return true;
1308  }
1309 
1315  protected function installDatabase(): bool {
1316  try {
1317  return $this->getApp()->migrate();
1318  } catch (\Exception $e) {
1319  return false;
1320  }
1321  }
1322 
1335  protected function createDataDirectory(array &$submissionVars, array $formVars): bool {
1336  // did the user have option of Elgg creating the data directory
1337  if ($formVars['dataroot']['type'] !== 'combo') {
1338  return true;
1339  }
1340 
1341  // did the user select the option
1342  if ($submissionVars['dataroot'] !== 'dataroot-checkbox') {
1343  return true;
1344  }
1345 
1346  $dir = \Elgg\Project\Paths::sanitize($submissionVars['path']) . 'data';
1347  if (file_exists($dir) || mkdir($dir, 0755)) {
1348  $submissionVars['dataroot'] = $dir;
1349  if (!file_exists("{$dir}/.htaccess")) {
1350  $htaccess = "Order Deny,Allow\nDeny from All\n";
1351  if (!file_put_contents("$dir/.htaccess", $htaccess)) {
1352  return false;
1353  }
1354  }
1355 
1356  return true;
1357  }
1358 
1359  return false;
1360  }
1361 
1370  protected function validateSettingsVars(array $submissionVars, array $formVars): bool {
1371  $app = $this->getApp();
1372 
1373  foreach ($formVars as $field => $info) {
1374  $submissionVars[$field] = trim($submissionVars[$field]);
1375  if ($info['required'] === true && $submissionVars[$field] === '') {
1376  $name = elgg_echo("install:settings:label:{$field}");
1377  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:requiredfield', [$name]));
1378 
1379  return false;
1380  }
1381  }
1382 
1383  // check that email address is email address
1384  if ($submissionVars['siteemail'] && !elgg_is_valid_email((string) $submissionVars['siteemail'])) {
1385  $save_value = $this->sanitizeInputValue($submissionVars['siteemail']);
1386  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:emailaddress', [$save_value]));
1387 
1388  return false;
1389  }
1390 
1391  return true;
1392  }
1393 
1401  protected function saveSiteSettings(array $submissionVars): bool {
1402  $app = $this->getApp();
1403 
1405 
1406  if (!$site->guid) {
1407  $site = new \ElggSite();
1408  $site->name = strip_tags($submissionVars['sitename']);
1409  $site->access_id = ACCESS_PUBLIC;
1410  $site->email = $submissionVars['siteemail'];
1411  $site->save();
1412  }
1413 
1414  if ($site->guid !== 1) {
1415  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:createsite'));
1416 
1417  return false;
1418  }
1419 
1420  $app->internal_services->config->site = $site;
1421 
1422  $sets = [
1423  'installed' => time(),
1424  'simplecache_enabled' => 1,
1425  'system_cache_enabled' => 1,
1426  'simplecache_minify_js' => true,
1427  'simplecache_minify_css' => true,
1428  'lastcache' => time(),
1429  'language' => 'en',
1430  'default_access' => $submissionVars['siteaccess'],
1431  'allow_registration' => false,
1432  'require_admin_validation' => false,
1433  'walled_garden' => false,
1434  'allow_user_default_access' => '',
1435  'default_limit' => 10,
1436  ];
1437 
1438  foreach ($sets as $key => $value) {
1440  }
1441 
1442  try {
1443  _elgg_services()->plugins->generateEntities();
1444 
1445  $app->internal_services->reset('plugins');
1446 
1447  if (elgg_extract('activate_plugins', $submissionVars, true)) {
1448  $plugins = $app->internal_services->plugins->find('all');
1449 
1450  foreach ($plugins as $plugin) {
1451  $plugin_config = $plugin->getStaticConfig('plugin', []);
1452  if (!elgg_extract('activate_on_install', $plugin_config, false)) {
1453  continue;
1454  }
1455 
1456  try {
1457  $plugin->activate();
1458  } catch (PluginException $e) {
1459  // do nothing
1460  }
1461  }
1462  }
1463 
1464  // Wo don't need to run upgrades on new installations
1465  $app->internal_services->events->unregisterHandler('create:after', 'object', \Elgg\Upgrade\CreateAdminNoticeHandler::class);
1466  $upgrades = $app->internal_services->upgradeLocator->locate();
1467  foreach ($upgrades as $upgrade) {
1468  $upgrade->setCompleted();
1469  }
1470  } catch (\Exception $e) {
1471  $app->internal_services->logger->log(\Psr\Log\LogLevel::ERROR, $e);
1472  }
1473 
1474  return true;
1475  }
1476 
1485  protected function validateAdminVars(array $submissionVars, array $formVars): bool {
1486 
1487  $app = $this->getApp();
1488 
1489  foreach ($formVars as $field => $info) {
1490  if ($info['required'] === true && !$submissionVars[$field]) {
1491  $name = elgg_echo("install:admin:label:{$field}");
1492  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:requiredfield', [$name]));
1493 
1494  return false;
1495  }
1496  }
1497 
1498  if ($submissionVars['password1'] !== $submissionVars['password2']) {
1499  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:admin:password:mismatch'));
1500 
1501  return false;
1502  }
1503 
1504  if (trim($submissionVars['password1']) === '') {
1505  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:admin:password:empty'));
1506 
1507  return false;
1508  }
1509 
1510  $minLength = $app->internal_services->configTable->get('min_password_length');
1511  if (strlen($submissionVars['password1']) < $minLength) {
1512  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:admin:password:tooshort'));
1513 
1514  return false;
1515  }
1516 
1517  // check that email address is email address
1518  if ($submissionVars['email'] && !elgg_is_valid_email((string) $submissionVars['email'])) {
1519  $save_value = $this->sanitizeInputValue($submissionVars['email']);
1520  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:emailaddress', [$save_value]));
1521 
1522  return false;
1523  }
1524 
1525  return true;
1526  }
1527 
1536  protected function createAdminAccount(array $submissionVars, bool $login = false): bool {
1537  $app = $this->getApp();
1538 
1539  try {
1541  'username' => $submissionVars['username'],
1542  'password' => $submissionVars['password1'],
1543  'name' => $submissionVars['displayname'],
1544  'email' => $submissionVars['email'],
1545  ]);
1546  } catch (RegistrationException $e) {
1547  $app->internal_services->system_messages->addErrorMessage($e->getMessage());
1548 
1549  return false;
1550  }
1551 
1552  elgg_call(ELGG_IGNORE_ACCESS, function() use ($app, $user) {
1553  if (!$user->makeAdmin()) {
1554  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:adminaccess'));
1555  }
1556  });
1557 
1558  // add validation data to satisfy user validation plugins
1559  $user->validated = true;
1560  $user->validated_method = 'admin_user';
1561 
1562  if (!$login) {
1563  return true;
1564  }
1565 
1566  try {
1567  elgg_login($user);
1568  } catch (LoginException $ex) {
1569  $app->internal_services->system_messages->addErrorMessage(elgg_echo('install:error:adminlogin'));
1570 
1571  return false;
1572  }
1573 
1574  return true;
1575  }
1576 
1584  protected function sanitizeInputValue($input_value) {
1585  if (is_array($input_value)) {
1586  return array_map([$this, __FUNCTION__], $input_value);
1587  }
1588 
1589  if (!is_string($input_value)) {
1590  return $input_value;
1591  }
1592 
1593  return htmlspecialchars($input_value);
1594  }
1595 }
$site
Definition: icons.php:5
$vars
Definition: theme.php:3
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
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
foreach( $paths as $path)
Definition: autoloader.php:12
$user
Definition: ban.php:7
$count
Definition: ban.php:24
foreach($categories as $key=> $category) $body
Definition: categories.php:35
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 an installation 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:48
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
$table
Definition: database.php:52
$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:337
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:290
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:240
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:99
$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:1430
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:14
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