Elgg  Version master
ElggPlugin.php
Go to the documentation of this file.
1 <?php
2 
7 use Elgg\Exceptions\InvalidArgumentException as ElggInvalidArgumentException;
9 use Elgg\Includer;
11 use Elgg\Traits\Loggable as ElggLoggable;
12 
17 class ElggPlugin extends ElggObject {
18 
19  const PRIORITY_SETTING_NAME = 'elgg:internal:priority';
20  const STATIC_CONFIG_FILENAME = 'elgg-plugin.php';
21  const PUBLIC_SERVICES_FILENAME = 'elgg-services.php';
22 
23  use ElggLoggable;
24 
31  'README.txt',
32  'CHANGES.txt',
33  'INSTALL.txt',
34  'COPYRIGHT.txt',
35  'LICENSE.txt',
36  'README',
37  'README.md',
38  'README.markdown',
39  ];
40 
44  protected $composer;
45 
49  protected $path;
50 
56  protected $static_config;
57 
61  protected $activated;
62 
66  protected function initializeAttributes() {
67  parent::initializeAttributes();
68 
69  $this->attributes['subtype'] = 'plugin';
70  }
71 
82  public static function fromId(string $plugin_id, ?string $path = null): \ElggPlugin {
83  if (empty($plugin_id)) {
84  throw new ElggInvalidArgumentException('Plugin ID must be set');
85  }
86 
88  if (!$plugin) {
89  $plugin = elgg_call(ELGG_IGNORE_ACCESS, function() use ($plugin_id) {
90  $plugin = new ElggPlugin();
91  $plugin->title = $plugin_id;
92  $plugin->save();
93 
94  return $plugin;
95  });
96  }
97 
98  if (empty($path)) {
100  }
101 
102  $path = Paths::sanitize($path);
103  $plugin->setPath($path . $plugin_id);
104 
105  return $plugin;
106  }
107 
111  public function save(): bool {
112 
114 
115  $this->attributes['owner_guid'] = $site->guid;
116  $this->attributes['container_guid'] = $site->guid;
117  $this->attributes['access_id'] = ACCESS_PUBLIC;
118 
119  $new = !$this->guid;
120  $priority = null;
121  if ($new) {
122  $priority = elgg_extract(self::PRIORITY_SETTING_NAME, $this->temp_metadata, 'new');
123  } elseif ($this->getPriority() === null) {
124  $priority = 'last';
125  }
126 
127  if ($priority) {
128  $this->setPriority($priority);
129  }
130 
131  return parent::save();
132  }
133 
139  public function getID(): string {
140  return (string) $this->title;
141  }
142 
149  public function getDisplayName(): string {
150  $name = elgg_extract('name', $this->getStaticConfig('plugin', []), '');
151  if (!empty($name)) {
152  return $name;
153  }
154 
155  return ucwords(str_replace(['-', '_'], ' ', $this->getID()));
156  }
157 
166  public function setPath(string $path): void {
167  $this->path = Paths::sanitize($path, true);
168  }
169 
175  public function getPath(): string {
176  if (isset($this->path)) {
177  return $this->path;
178  }
179 
180  $this->setPath(elgg_get_plugins_path() . $this->getID());
181  return $this->path;
182  }
183 
190  protected function getLanguagesPath(): string {
191  return $this->getPath() . 'languages/';
192  }
193 
205  public function getStaticConfig(string $key, $default = null) {
206  if ($this->static_config === null) {
207  $this->static_config = [];
208 
209  try {
210  $this->static_config = $this->includeFile(self::STATIC_CONFIG_FILENAME);
211  } catch (PluginException $ex) {
212  elgg_log($ex, \Psr\Log\LogLevel::ERROR);
213  }
214  }
215 
216  return $this->static_config[$key] ?? $default;
217  }
218 
219  // Load Priority
220 
226  public function getPriority(): ?int {
227  $priority = $this->getMetadata(self::PRIORITY_SETTING_NAME);
228  if (isset($priority)) {
229  return (int) $priority;
230  }
231 
232  return null;
233  }
234 
246  public function setPriority($priority): int|false {
247  $priority = $this->normalizePriority($priority);
248 
249  return _elgg_services()->plugins->setPriority($this, $priority);
250  }
251 
260  protected function normalizePriority($priority): int {
261  // if no priority assume a priority of 1
262  $old_priority = $this->getPriority();
263  $old_priority = $old_priority ?: 1;
264  $max_priority = _elgg_services()->plugins->getMaxPriority() ?: 1;
265 
266  // can't use switch here because it's not strict and php evaluates +1 == 1
267  if ($priority === '+1') {
268  $priority = $old_priority + 1;
269  } else if ($priority === '-1') {
270  $priority = $old_priority - 1;
271  } else if ($priority === 'first') {
272  $priority = 1;
273  } else if ($priority === 'last') {
274  $priority = $max_priority;
275  } else if ($priority === 'new') {
276  $max_priority++;
277  $priority = $max_priority;
278  }
279 
280  return min($max_priority, max(1, (int) $priority));
281  }
282 
283  // Plugin settings
284 
294  public function getSetting(string $name, $default = null) {
295  $values = $this->getAllSettings();
296  return elgg_extract($name, $values, $default);
297  }
298 
307  public function getAllSettings(): array {
308  try {
309  if (!$this->isActive()) {
310  return [];
311  }
312 
313  $defaults = (array) $this->getStaticConfig('settings', []);
314 
315  $settings = $this->getAllMetadata();
316 
317  // title, description and priority are not considered settings
318  unset($settings['title'], $settings['description'], $settings[self::PRIORITY_SETTING_NAME]);
319 
320  return array_merge($defaults, $settings);
321  } catch (DatabaseException $ex) {
322  return [];
323  }
324  }
325 
334  public function setSetting(string $name, $value): bool {
335 
336  $value = _elgg_services()->events->triggerResults('setting', 'plugin', [
337  'plugin_id' => $this->getID(),
338  'plugin' => $this,
339  'name' => $name,
340  'value' => $value,
341  ], $value);
342 
343  if (is_array($value)) {
344  elgg_log('Plugin settings cannot store arrays.', \Psr\Log\LogLevel::ERROR);
345 
346  return false;
347  }
348 
349  return elgg_call(ELGG_DISABLE_SYSTEM_LOG, function() use ($name, $value) {
350  return $this->setMetadata($name, $value);
351  });
352  }
353 
361  public function unsetSetting(string $name): bool {
362  return elgg_call(ELGG_DISABLE_SYSTEM_LOG, function() use ($name) {
363  return (bool) $this->deleteMetadata($name);
364  });
365  }
366 
371  public function unsetAllSettings(): bool {
372  $settings = $this->getAllMetadata();
373 
374  // title, description and priority are not considered settings
375  unset($settings['title'], $settings['description'], $settings[self::PRIORITY_SETTING_NAME]);
376 
377  foreach ($settings as $name => $value) {
378  $this->unsetSetting($name);
379  }
380 
381  return true;
382  }
383 
390  public function unsetAllEntityAndPluginSettings(): bool {
391  // remove all plugin settings
392  $result = $this->unsetAllSettings();
393 
394  // entity plugin settings are stored with the entity
395  $delete = Delete::fromTable(MetadataTable::TABLE_NAME);
396  $delete->andWhere($delete->compare('name', 'like', "plugin:%_setting:{$this->getID()}:%", ELGG_VALUE_STRING));
397 
398  try {
399  _elgg_services()->db->deleteData($delete);
400  _elgg_services()->metadataCache->clear();
401 
402  $result &= true;
403  } catch (DatabaseException $e) {
404  elgg_log($e, \Psr\Log\LogLevel::ERROR);
405 
406  $result &= false;
407  }
408 
409  // trigger an event, so plugin devs can also remove settings
410  $params = [
411  'entity' => $this,
412  ];
413  return (bool) _elgg_services()->events->triggerResults('remove:settings', 'plugin', $params, $result);
414  }
415 
422  public function isValid(): bool {
423  try {
424  $this->assertValid();
425  return true;
426  } catch (PluginException $e) {
427  return false;
428  }
429  }
430 
439  public function assertValid(): void {
440  if (!$this->getID()) {
441  throw PluginException::factory([
442  'message' => elgg_echo('ElggPlugin:MissingID', [$this->guid]),
443  'plugin' => $this,
444  ]);
445  }
446 
447  $this->getComposer()->assertPluginId();
448 
449  if (file_exists($this->getPath() . 'start.php')) {
450  throw PluginException::factory([
451  'message' => elgg_echo('ElggPlugin:StartFound', [$this->getID()]),
452  'plugin' => $this,
453  ]);
454  }
455  }
456 
462  public function isActive(): bool {
463  if (isset($this->activated)) {
464  return $this->activated;
465  }
466 
467  $this->activated = elgg_is_active_plugin($this->getID());
468  return $this->activated;
469  }
470 
477  public function canActivate(): bool {
478  if ($this->isActive()) {
479  return false;
480  }
481 
482  try {
483  $this->assertCanActivate();
484  return true;
485  } catch (PluginException $e) {
486  return false;
487  }
488  }
489 
498  public function assertCanActivate(): void {
499  $this->assertValid();
500  $this->assertDependencies();
501  }
502 
503  // activating and deactivating
504 
511  public function activate(): bool {
512  if ($this->isActive()) {
513  return false;
514  }
515 
516  $this->assertCanActivate();
517 
518  // Check this before setting status because the file could potentially throw
519  $this->assertStaticConfigValid();
520 
521  if (!$this->setStatus(true)) {
522  return false;
523  }
524 
525  // perform tasks and emit events
526  // emit an event. returning false will make this not be activated.
527  // we need to do this after it's been fully activated
528  // or the deactivate will be confused.
529  $params = [
530  'plugin_id' => $this->getID(),
531  'plugin_entity' => $this,
532  ];
533 
534  $return = _elgg_services()->events->trigger('activate', 'plugin', $params);
535 
536  // if there are any on_enable functions, start the plugin now and run them
537  // Note: this will not run re-run the init events!
538  if ($return) {
539  try {
541 
542  $this->register();
543 
544  // directly load languages to have them available during runtime
545  $this->loadLanguages();
546 
547  $this->boot();
548 
549  $this->getBootstrap()->activate();
550 
551  $this->init();
552  } catch (PluginException $ex) {
553  elgg_log($ex, \Psr\Log\LogLevel::ERROR);
554 
555  $return = false;
556  }
557  }
558 
559  if ($return === false) {
560  $this->deactivate();
561  } else {
562  elgg_delete_admin_notice("cannot_start {$this->getID()}");
563 
565  _elgg_services()->logger->notice("Plugin {$this->getID()} has been activated");
566  }
567 
568  return $return;
569  }
570 
576  public function getDependencies(): array {
577  $plugin_config = $this->getStaticConfig('plugin', []);
578  return (array) elgg_extract('dependencies', $plugin_config, []);
579  }
580 
588  public function canDeactivate(): bool {
589  if (!$this->isActive()) {
590  return false;
591  }
592 
593  try {
594  $this->assertcanDeactivate();
595  return true;
596  } catch (PluginException $e) {
597  return false;
598  }
599  }
600 
609  public function assertCanDeactivate(): void {
610  $dependents = [];
611 
612  $active_plugins = elgg_get_plugins();
613 
614  foreach ($active_plugins as $plugin) {
615  $dependencies = $plugin->getDependencies();
616  if (!array_key_exists($this->getID(), $dependencies)) {
617  continue;
618  }
619 
620  if (elgg_extract('must_be_active', $dependencies[$this->getID()], true)) {
621  $dependents[$plugin->getID()] = $plugin;
622  }
623  }
624 
625  if (empty($dependents)) {
626  return;
627  }
628 
629  $list = array_map(function (\ElggPlugin $plugin) {
630  $css_id = preg_replace('/[^a-z0-9-]/i', '-', $plugin->getID());
631 
632  return elgg_view('output/url', [
633  'text' => $plugin->getDisplayName(),
634  'href' => "#{$css_id}",
635  ]);
636  }, $dependents);
637 
638  $list = implode(', ', $list);
639  throw PluginException::factory([
640  'message' => elgg_echo('ElggPlugin:Dependencies:ActiveDependent', [$this->getDisplayName(), $list]),
641  'plugin' => $this,
642  ]);
643  }
644 
652  public function deactivate(): bool {
653  if (!$this->isActive()) {
654  return false;
655  }
656 
657  $this->assertCanDeactivate();
658 
659  // emit an event. returning false will cause this to not be deactivated.
660  $params = [
661  'plugin_id' => $this->getID(),
662  'plugin_entity' => $this,
663  ];
664 
665  $return = _elgg_services()->events->trigger('deactivate', 'plugin', $params);
666  if ($return === false) {
667  return false;
668  }
669 
670  $this->getBootstrap()->deactivate();
671 
672  $this->deactivateEntities();
673 
675 
676  _elgg_services()->logger->notice("Plugin {$this->getID()} has been deactivated");
677 
678  return $this->setStatus(false);
679  }
680 
689  public function getBootstrap(): \Elgg\PluginBootstrapInterface {
690  $bootstrap = $this->getStaticConfig('bootstrap');
691  if ($bootstrap) {
692  if (!is_subclass_of($bootstrap, \Elgg\PluginBootstrapInterface::class)) {
693  throw PluginException::factory([
694  'message' => elgg_echo('LogicException:InterfaceNotImplemented', [
695  $bootstrap,
696  \Elgg\PluginBootstrapInterface::class
697  ]),
698  'plugin' => $this,
699  ]);
700  }
701 
702  return new $bootstrap($this, elgg());
703  }
704 
705  return new \Elgg\DefaultPluginBootstrap($this, elgg());
706  }
707 
714  public function autoload(): void {
715  $this->registerClasses();
716 
717  $autoload_file = 'vendor/autoload.php';
718  if (!$this->canReadFile($autoload_file)) {
719  return;
720  }
721 
722  $autoloader = Includer::requireFileOnce("{$this->getPath()}{$autoload_file}");
723 
724  if (!$autoloader instanceof \Composer\Autoload\ClassLoader) {
725  return;
726  }
727 
728  $autoloader->unregister();
729 
730  // plugins should be appended, composer defaults to prepend
731  $autoloader->register(false);
732  }
733 
742  public function register(): void {
743  $this->autoload();
744 
745  $this->registerPublicServices();
746  $this->activateEntities();
747  $this->registerLanguages();
748  $this->registerViews();
749 
750  $this->getBootstrap()->load();
751  }
752 
759  public function boot(): void {
760  $this->getBootstrap()->boot();
761  }
762 
769  public function init(): void {
770  $this->registerRoutes();
771  $this->registerActions();
772  $this->registerEntities();
773  $this->registerWidgets();
774  $this->registerEvents();
775  $this->registerViewExtensions();
776  $this->registerGroupTools();
777  $this->registerViewOptions();
778  $this->registerNotifications();
779 
780  $this->getBootstrap()->init();
781  }
782 
791  protected function includeFile(string $filename) {
792  $filepath = "{$this->getPath()}{$filename}";
793 
794  if (!$this->canReadFile($filename)) {
795  $msg = elgg_echo(
796  'ElggPlugin:Exception:CannotIncludeFile',
797  [$filename, $this->getID(), $this->guid, $this->getPath()]
798  );
799 
800  throw PluginException::factory([
801  'message' => $msg,
802  'plugin' => $this,
803  ]);
804  }
805 
806  try {
807  $ret = Includer::requireFile($filepath);
808  } catch (Exception $e) {
809  $msg = elgg_echo(
810  'ElggPlugin:Exception:IncludeFileThrew',
811  [$filename, $this->getID(), $this->guid, $this->getPath()]
812  );
813 
814  throw PluginException::factory([
815  'message' => $msg,
816  'previous' => $e,
817  'plugin' => $this,
818  ]);
819  }
820 
821  return $ret;
822  }
823 
831  protected function canReadFile(string $filename): bool {
832  return is_file("{$this->getPath()}{$filename}");
833  }
834 
841  protected function assertStaticConfigValid(): void {
842  if (!$this->canReadFile(self::STATIC_CONFIG_FILENAME)) {
843  return;
844  }
845 
846  ob_start();
847  $value = $this->includeFile(self::STATIC_CONFIG_FILENAME);
848  if (ob_get_clean() !== '') {
849  throw PluginException::factory([
850  'message' => elgg_echo('ElggPlugin:activate:ConfigSentOutput'),
851  'plugin' => $this,
852  ]);
853  }
854 
855  // make sure can serialize
856  $value = @unserialize(serialize($value));
857  if (!is_array($value)) {
858  throw PluginException::factory([
859  'message' => elgg_echo('ElggPlugin:activate:BadConfigFormat'),
860  'plugin' => $this,
861  ]);
862  }
863  }
864 
870  protected function registerPublicServices(): void {
871  $services_path = $this->getPath() . self::PUBLIC_SERVICES_FILENAME;
872  if (!is_file($services_path)) {
873  return;
874  }
875 
876  $services = Includer::includeFile($services_path);
877  foreach ($services as $name => $definition) {
878  elgg()->set($name, $definition);
879  }
880  }
881 
888  protected function registerViews(): void {
889  if (_elgg_services()->views->isViewLocationsLoadedFromCache()) {
890  return;
891  }
892 
893  $views = _elgg_services()->views;
894 
895  // Declared views first
896  $spec = $this->getStaticConfig('views');
897  if ($spec) {
898  $views->mergeViewsSpec($spec);
899  }
900 
901  // Allow /views directory files to override
902  if (!$views->registerViewsFromPath($this->getPath())) {
903  $msg = elgg_echo('ElggPlugin:Exception:CannotRegisterViews', [$this->getID(), $this->guid, $this->getPath()]);
904 
905  throw PluginException::factory([
906  'message' => $msg,
907  'plugin' => $this,
908  ]);
909  }
910  }
911 
917  protected function registerEntities(): void {
918  $spec = (array) $this->getStaticConfig('entities', []);
919 
920  foreach ($spec as $entity) {
921  if (!isset($entity['type']) || !isset($entity['subtype'])) {
922  continue;
923  }
924 
925  $capabilities = elgg_extract('capabilities', $entity, []);
926  foreach ($capabilities as $capability => $value) {
927  _elgg_services()->entity_capabilities->setCapability($entity['type'], $entity['subtype'], $capability, $value);
928  }
929  }
930  }
931 
937  protected function registerActions(): void {
938  $actions = _elgg_services()->actions;
939  $root_path = $this->getPath();
940 
941  $spec = (array) $this->getStaticConfig('actions', []);
942 
943  foreach ($spec as $action => $action_spec) {
944  if (!is_array($action_spec)) {
945  continue;
946  }
947 
948  $access = elgg_extract('access', $action_spec, 'logged_in');
949  $handler = elgg_extract('controller', $action_spec);
950  if (!$handler) {
951  $handler = elgg_extract('filename', $action_spec);
952  if (!$handler) {
953  $handler = "{$root_path}actions/{$action}.php";
954  }
955  }
956 
957  // unset handled action specs, pass the rest to the action service
958  unset($action_spec['access']);
959  unset($action_spec['controller']);
960  unset($action_spec['filename']);
961 
962  $actions->register($action, $handler, $access, $action_spec);
963  }
964  }
965 
971  protected function registerRoutes(): void {
972  $routes = _elgg_services()->routes;
973 
974  $spec = (array) $this->getStaticConfig('routes', []);
975  foreach ($spec as $name => $route_spec) {
976  if (!is_array($route_spec)) {
977  continue;
978  }
979 
980  $routes->register($name, $route_spec);
981  }
982  }
983 
989  protected function registerWidgets(): void {
990  $widgets = _elgg_services()->widgets;
991 
992  $spec = (array) $this->getStaticConfig('widgets', []);
993  foreach ($spec as $widget_id => $widget_definition) {
994  if (!is_array($widget_definition)) {
995  continue;
996  }
997 
998  if (!isset($widget_definition['id'])) {
999  $widget_definition['id'] = $widget_id;
1000  }
1001 
1002  $definition = \Elgg\WidgetDefinition::factory($widget_definition);
1003 
1004  $widgets->registerType($definition);
1005  }
1006  }
1007 
1015  public function registerLanguages(): void {
1016  _elgg_services()->translator->registerLanguagePath($this->getLanguagesPath());
1017  }
1018 
1028  protected function loadLanguages(): void {
1029  $languages_path = $this->getLanguagesPath();
1030  if (!is_dir($languages_path)) {
1031  return;
1032  }
1033 
1034  _elgg_services()->translator->registerTranslations($languages_path);
1035  }
1036 
1042  protected function registerClasses(): void {
1043  _elgg_services()->autoloadManager->addClasses("{$this->getPath()}classes");
1044  }
1045 
1051  protected function activateEntities(): void {
1052  $spec = (array) $this->getStaticConfig('entities', []);
1053 
1054  foreach ($spec as $entity) {
1055  if (isset($entity['type'], $entity['subtype'], $entity['class'])) {
1056  _elgg_services()->entityTable->setEntityClass($entity['type'], $entity['subtype'], $entity['class']);
1057  }
1058  }
1059  }
1060 
1066  protected function deactivateEntities(): void {
1067  $spec = (array) $this->getStaticConfig('entities', []);
1068 
1069  foreach ($spec as $entity) {
1070  if (isset($entity['type'], $entity['subtype'], $entity['class'])) {
1071  _elgg_services()->entityTable->setEntityClass($entity['type'], $entity['subtype']);
1072  }
1073  }
1074  }
1075 
1081  protected function registerEvents(): void {
1082  $events = _elgg_services()->events;
1083 
1084  $spec = (array) $this->getStaticConfig('events', []);
1085 
1086  foreach ($spec as $name => $types) {
1087  foreach ($types as $type => $callbacks) {
1088  foreach ($callbacks as $callback => $event_spec) {
1089  if (!is_array($event_spec)) {
1090  continue;
1091  }
1092 
1093  $unregister = (bool) elgg_extract('unregister', $event_spec, false);
1094 
1095  if ($unregister) {
1096  $events->unregisterHandler($name, $type, $callback);
1097  } else {
1098  $priority = (int) elgg_extract('priority', $event_spec, 500);
1099 
1100  $events->registerHandler($name, $type, $callback, $priority);
1101  }
1102  }
1103  }
1104  }
1105  }
1106 
1112  protected function registerViewExtensions(): void {
1113  $views = _elgg_services()->views;
1114 
1115  $spec = (array) $this->getStaticConfig('view_extensions', []);
1116 
1117  foreach ($spec as $src_view => $extensions) {
1118  foreach ($extensions as $extention => $extention_spec) {
1119  if (!is_array($extention_spec)) {
1120  continue;
1121  }
1122 
1123  $unextend = (bool) elgg_extract('unextend', $extention_spec, false);
1124 
1125  if ($unextend) {
1126  $views->unextendView($src_view, $extention);
1127  } else {
1128  $priority = (int) elgg_extract('priority', $extention_spec, 501);
1129 
1130  $views->extendView($src_view, $extention, $priority);
1131  }
1132  }
1133  }
1134  }
1135 
1141  protected function registerGroupTools(): void {
1142  $tools = _elgg_services()->group_tools;
1143 
1144  $spec = (array) $this->getStaticConfig('group_tools', []);
1145 
1146  foreach ($spec as $tool_name => $tool_options) {
1147  if (!is_array($tool_options)) {
1148  continue;
1149  }
1150 
1151  $unregister = (bool) elgg_extract('unregister', $tool_options, false);
1152 
1153  if ($unregister) {
1154  $tools->unregister($tool_name);
1155  } else {
1156  $tools->register($tool_name, $tool_options);
1157  }
1158  }
1159  }
1160 
1166  protected function registerViewOptions(): void {
1167  $spec = (array) $this->getStaticConfig('view_options', []);
1168 
1169  foreach ($spec as $view_name => $options) {
1170  if (!is_array($options)) {
1171  continue;
1172  }
1173 
1174  if (isset($options['ajax'])) {
1175  if ($options['ajax'] === true) {
1176  _elgg_services()->ajax->registerView($view_name);
1177  } else {
1178  _elgg_services()->ajax->unregisterView($view_name);
1179  }
1180  }
1181 
1182  if (isset($options['simplecache']) && $options['simplecache'] === true) {
1183  _elgg_services()->simpleCache->registerCacheableView($view_name);
1184  }
1185  }
1186  }
1187 
1193  protected function registerNotifications(): void {
1194  $spec = (array) $this->getStaticConfig('notifications', []);
1195 
1196  foreach ($spec as $type => $subtypes) {
1197  foreach ($subtypes as $subtype => $actions) {
1198  foreach ($actions as $action => $callback) {
1199  if ($callback === false) {
1200  _elgg_services()->notifications->unregisterEvent($type, $subtype, [$action]);
1201  } elseif ($callback === true) {
1202  _elgg_services()->notifications->registerEvent($type, $subtype, [$action]);
1203  } else {
1204  _elgg_services()->notifications->registerEvent($type, $subtype, [$action], $callback);
1205  }
1206  }
1207  }
1208  }
1209  }
1210 
1218  public function __get($name) {
1219  // See if its in our base attribute
1220  if (array_key_exists($name, $this->attributes)) {
1221  return $this->attributes[$name];
1222  }
1223 
1224  // object title and description are stored as metadata
1225  if (in_array($name, ['title', 'description'])) {
1226  return parent::__get($name);
1227  }
1228 
1229  return $this->getSetting($name);
1230  }
1231 
1240  public function __set($name, $value) {
1241  if (array_key_exists($name, $this->attributes)) {
1242  // Check that we're not trying to change the guid!
1243  if ((array_key_exists('guid', $this->attributes)) && ($name == 'guid')) {
1244  return;
1245  }
1246 
1247  $this->attributes[$name] = $value;
1248 
1249  return;
1250  }
1251 
1252  // object title and description are stored as metadata
1253  if (in_array($name, ['title', 'description'])) {
1254  parent::__set($name, $value);
1255 
1256  return;
1257  }
1258 
1259  // to make sure we trigger the correct events
1260  $this->setSetting($name, $value);
1261  }
1262 
1270  protected function setStatus(bool $active): bool {
1271  if (!$this->guid) {
1272  return false;
1273  }
1274 
1276  if ($active) {
1277  $result = $this->addRelationship($site->guid, 'active_plugin');
1278  } else {
1279  $result = $this->removeRelationship($site->guid, 'active_plugin');
1280  }
1281 
1282  if ($result) {
1283  $this->activated = $active;
1284  }
1285 
1286  $this->invalidateCache();
1287 
1288  return $result;
1289  }
1290 
1294  public function isCacheable(): bool {
1295  return true;
1296  }
1297 
1301  public function invalidateCache(): void {
1302  _elgg_services()->boot->clearCache();
1303  _elgg_services()->pluginsCache->delete($this->getID());
1304 
1305  parent::invalidateCache();
1306  }
1307 
1315  protected function getComposer(): \Elgg\Plugin\Composer {
1316  if (isset($this->composer)) {
1317  return $this->composer;
1318  }
1319 
1320  $this->composer = new \Elgg\Plugin\Composer($this);
1321  return $this->composer;
1322  }
1323 
1331  public function meetsDependencies(): bool {
1332  try {
1333  $this->assertDependencies();
1334 
1335  return true;
1336  } catch (PluginException $e) {
1337  return false;
1338  }
1339  }
1340 
1349  public function assertDependencies(): void {
1350  $this->getComposer()->assertConflicts();
1351  $this->getComposer()->assertActivePluginConflicts();
1352  $this->getComposer()->assertRequiredPhpVersion();
1353  $this->getComposer()->assertRequiredPhpExtensions();
1354  $this->assertPluginDependencies();
1355  }
1356 
1365  protected function assertPluginDependencies(): void {
1366  foreach ($this->getDependencies() as $plugin_id => $plugin_dep) {
1367  $must_be_active = elgg_extract('must_be_active', $plugin_dep, true);
1368  $position = elgg_extract('position', $plugin_dep);
1369 
1370  $dependent_plugin = elgg_get_plugin_from_id($plugin_id);
1371 
1372  if ($must_be_active && (!$dependent_plugin instanceof \ElggPlugin || !$dependent_plugin->isActive())) {
1373  throw PluginException::factory([
1374  'message' => elgg_echo('PluginException:PluginMustBeActive', [$plugin_id]),
1375  'plugin' => $this,
1376  ]);
1377  }
1378 
1379  if ($dependent_plugin instanceof \ElggPlugin && $position && $dependent_plugin->isActive()) {
1380  if ($position == 'after' && ($this->getPriority() < $dependent_plugin->getPriority())) {
1381  throw PluginException::factory([
1382  'message' => elgg_echo('PluginException:PluginMustBeAfter', [$plugin_id]),
1383  'plugin' => $this,
1384  ]);
1385  } elseif ($position == 'before' && ($this->getPriority() > $dependent_plugin->getPriority())) {
1386  throw PluginException::factory([
1387  'message' => elgg_echo('PluginException:PluginMustBeBefore', [$plugin_id]),
1388  'plugin' => $this,
1389  ]);
1390  }
1391  }
1392  }
1393  }
1394 
1400  public function getVersion(): string {
1401  // composer version
1402  $version = $this->getComposer()->getConfiguration()->version();
1403  if (!elgg_is_empty($version)) {
1404  return $version;
1405  }
1406 
1407  // elgg-plugin version
1408  $plugin_config = $this->getStaticConfig('plugin', []);
1409  $version = elgg_extract('version', $plugin_config);
1410  if (!elgg_is_empty($version)) {
1411  return $version;
1412  }
1413 
1414  // bundled plugins use elgg version
1415  if (in_array($this->getID(), Plugins::BUNDLED_PLUGINS)) {
1416  return elgg_get_release();
1417  }
1418 
1419  return '0.1';
1420  }
1421 
1429  public function getCategories(): array {
1430  return $this->getComposer()->getCategories();
1431  }
1432 
1440  public function getLicense(): string {
1441  return $this->getComposer()->getLicense();
1442  }
1443 
1451  public function getDescription(): string {
1452  return (string) $this->getComposer()->getConfiguration()->description();
1453  }
1454 
1462  public function getRepositoryURL(): string {
1463  return (string) $this->getComposer()->getConfiguration()->support()->source();
1464  }
1465 
1473  public function getBugTrackerURL(): string {
1474  return (string) $this->getComposer()->getConfiguration()->support()->issues();
1475  }
1476 
1484  public function getWebsite(): string {
1485  return (string) $this->getComposer()->getConfiguration()->homepage();
1486  }
1487 
1495  public function getAuthors(): array {
1496  return (array) $this->getComposer()->getConfiguration()->authors();
1497  }
1498 
1506  public function getConflicts(): array {
1507  return $this->getComposer()->getConflicts();
1508  }
1509 }
$site
Definition: icons.php:5
$site description
Definition: settings.php:14
$entity
Definition: reset.php:8
$guid
Reset an ElggUpgrade.
Definition: reset.php:6
if(! $user||! $user->canDelete()) $name
Definition: delete.php:22
$subtype
Definition: delete.php:22
$type
Definition: delete.php:21
$plugin_id
Remove all user and plugin settings from the give plugin ID.
Definition: remove.php:8
$params
Saves global plugin settings.
Definition: save.php:13
$position
Definition: add.php:11
$handler
Definition: add.php:7
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
$delete
foreach( $paths as $path)
Definition: autoloader.php:12
elgg_invalidate_caches()
Invalidate all the registered caches.
Definition: cache.php:88
Plugin class containing helper functions for plugin activation/deactivation, dependency checking capa...
Definition: ElggPlugin.php:17
getComposer()
Returns the composer parser.
getWebsite()
Return the website.
assertCanActivate()
Asserts if a plugin can activate.
Definition: ElggPlugin.php:498
loadLanguages()
Loads the plugin's translations.
setPriority($priority)
Sets the priority of the plugin Returns the new priority or false on error.
Definition: ElggPlugin.php:246
init()
Init the plugin.
Definition: ElggPlugin.php:769
getCategories()
Returns an array with categories.
canReadFile(string $filename)
Checks whether a plugin file with the given name exists.
Definition: ElggPlugin.php:831
getDescription()
Return the description.
assertCanDeactivate()
Asserts if a plugin can be deactivated.
Definition: ElggPlugin.php:609
boot()
Boot the plugin.
Definition: ElggPlugin.php:759
unsetSetting(string $name)
Removes a plugin setting name and value.
Definition: ElggPlugin.php:361
const PRIORITY_SETTING_NAME
Definition: ElggPlugin.php:19
autoload()
Register plugin classes and require composer autoloader.
Definition: ElggPlugin.php:714
getPath()
Returns the plugin's full path with trailing slash.
Definition: ElggPlugin.php:175
registerViewExtensions()
Registers the plugin's view extensions provided in the plugin config file.
setStatus(bool $active)
Sets the plugin to active or inactive.
__set($name, $value)
Set a value as attribute or setting.
__get($name)
Get an attribute or setting value.
setSetting(string $name, $value)
Set a plugin setting for the plugin.
Definition: ElggPlugin.php:334
const ADDITIONAL_TEXT_FILES
Definition: ElggPlugin.php:30
getLicense()
Returns the license.
normalizePriority($priority)
Normalize and validate new priority.
Definition: ElggPlugin.php:260
registerEvents()
Registers the plugin's events provided in the plugin config file.
unsetAllSettings()
Removes all settings for this plugin.
Definition: ElggPlugin.php:371
assertStaticConfigValid()
If a static config file is present, is it a serializable array?
Definition: ElggPlugin.php:841
registerLanguages()
Registers the plugin's languages.
getVersion()
Returns the plugin version.
registerActions()
Registers the plugin's actions provided in the plugin config file.
Definition: ElggPlugin.php:937
getBootstrap()
Bootstrap object.
Definition: ElggPlugin.php:689
assertDependencies()
Assert plugin dependencies.
getDependencies()
Returns an array of dependencies as configured in the static config.
Definition: ElggPlugin.php:576
invalidateCache()
{Invalidate cache for entity.void}
registerViews()
Registers the plugin's views.
Definition: ElggPlugin.php:888
canDeactivate()
Checks if this plugin can be deactivated on the current Elgg installation.
Definition: ElggPlugin.php:588
initializeAttributes()
{{Initialize the attributes array.This is vital to distinguish between metadata and base parameters....
Definition: ElggPlugin.php:66
assertValid()
Asserts if a plugin is valid.
Definition: ElggPlugin.php:439
isActive()
Is this plugin active?
Definition: ElggPlugin.php:462
registerClasses()
Registers the plugin's classes.
static fromId(string $plugin_id, ?string $path=null)
Load a plugin object from its ID Create a new plugin entity if it doesn't exist.
Definition: ElggPlugin.php:82
getRepositoryURL()
Returns the repository url.
isCacheable()
{Is entity cacheable in the runtime cache.bool}
activateEntities()
Activates the plugin's entities.
registerGroupTools()
Registers the plugin's group tools provided in the plugin config file.
getID()
Returns the ID (dir name) of this plugin.
Definition: ElggPlugin.php:139
isValid()
Returns if the plugin is complete, meaning has all required files and Elgg can read them and they mak...
Definition: ElggPlugin.php:422
unsetAllEntityAndPluginSettings()
Remove all entity and plugin settings for this plugin.
Definition: ElggPlugin.php:390
const PUBLIC_SERVICES_FILENAME
Definition: ElggPlugin.php:21
registerViewOptions()
Registers the plugin's view options provided in the plugin config file.
registerNotifications()
Registers the plugin's notification events.
getBugTrackerURL()
Returns the bug tracker page.
getDisplayName()
Returns the name from elgg-plugin.php if available, otherwise a nicely formatted ID.
Definition: ElggPlugin.php:149
registerEntities()
Registers the plugin's entities.
Definition: ElggPlugin.php:917
getPriority()
Gets the plugin's load priority.
Definition: ElggPlugin.php:226
getConflicts()
Returns an array of projectnames with their conflicting version.
registerPublicServices()
Registers the plugin public services.
Definition: ElggPlugin.php:870
activate()
Activates the plugin for the current site.
Definition: ElggPlugin.php:511
getAuthors()
Returns an array of authors.
canActivate()
Checks if this plugin can be activated on the current Elgg installation.
Definition: ElggPlugin.php:477
getAllSettings()
Returns an array of all settings saved for this plugin when the plugin is active.
Definition: ElggPlugin.php:307
getStaticConfig(string $key, $default=null)
Get a value from the plugins' static config file.
Definition: ElggPlugin.php:205
setPath(string $path)
Set path.
Definition: ElggPlugin.php:166
registerRoutes()
Registers the plugin's routes provided in the plugin config file.
Definition: ElggPlugin.php:971
meetsDependencies()
Checks if dependencies are met.
assertPluginDependencies()
Assert required plugins or plugin position.
save()
{Save this data to the appropriate database table.bool}
Definition: ElggPlugin.php:111
getLanguagesPath()
Returns the plugin's languages directory full path with trailing slash.
Definition: ElggPlugin.php:190
deactivateEntities()
Deactivates the plugin's entities.
registerWidgets()
Registers the plugin's widgets provided in the plugin config file.
Definition: ElggPlugin.php:989
const STATIC_CONFIG_FILENAME
Definition: ElggPlugin.php:20
getSetting(string $name, $default=null)
Returns a plugin setting when the plugin is active.
Definition: ElggPlugin.php:294
includeFile(string $filename)
Includes one of the plugins files.
Definition: ElggPlugin.php:791
deactivate()
Deactivates the plugin.
Definition: ElggPlugin.php:652
Query builder for updating data in the database.
Definition: Delete.php:8
This class interfaces with the database to perform CRUD operations on metadata.
Persistent, installation-wide key-value storage.
Definition: Plugins.php:28
A generic parent class for database exceptions.
Exception thrown if an argument is not of the expected type.
Allow executing scripts without $this context or local vars.
Definition: Includer.php:10
Find Elgg and project paths.
Definition: Paths.php:8
static factory(array $options)
Create an WidgetDefinition from an associative array.
$subtypes
elgg_get_plugins_path()
Get the plugin path for this installation, ending with slash.
elgg_get_release()
Get the current Elgg release.
const ELGG_VALUE_STRING
Definition: constants.php:112
const ELGG_IGNORE_ACCESS
elgg_call() flags
Definition: constants.php:121
const ELGG_DISABLE_SYSTEM_LOG
Definition: constants.php:125
const ACCESS_PUBLIC
Definition: constants.php:12
if($who_can_change_language==='nobody') elseif($who_can_change_language==='admin_only' &&!elgg_is_admin_logged_in()) $options
Definition: language.php:20
$version
$extensions
$views
Definition: item.php:17
if($item instanceof \ElggEntity) elseif($item instanceof \ElggRiverItem) elseif($item instanceof \ElggRelationship) elseif(is_callable([ $item, 'getType']))
Definition: item.php:48
elgg()
Bootstrapping and helper procedural code available for use in Elgg core and plugins.
Definition: elgglib.php:12
elgg_log($message, $level=\Psr\Log\LogLevel::NOTICE)
Log a message.
Definition: elgglib.php:88
_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_delete_admin_notice(string $id)
Remove an admin notice by ID.
Definition: admin.php:63
elgg_is_empty($value)
Check if a value isn't empty, but allow 0 and '0'.
Definition: input.php:176
elgg_get_plugins(string $status='active')
Returns an ordered list of plugins.
Definition: plugins.php:39
elgg_get_plugin_from_id(string $plugin_id)
Elgg plugins library Contains functions for managing plugins.
Definition: plugins.php:15
elgg_is_active_plugin(string $plugin_id)
Returns if a plugin is active for a current site.
Definition: plugins.php:27
elgg_get_site_entity()
Get the current site entity.
Definition: entities.php:101
$defaults
Generic entity header upload helper.
Definition: header.php:6
$value
Definition: generic.php:51
$default
Definition: checkbox.php:30
elgg_echo(string $message_key, array $args=[], string $language='')
Elgg language module Functions to manage language and translations.
Definition: languages.php:17
elgg_view(string $view, array $vars=[], string $viewtype='')
Return a parsed view.
Definition: views.php:156
try
Definition: login_as.php:33
setMetadata(string $name, mixed $value, string $value_type='', bool $multiple=false)
Set metadata on this entity.
Definition: Metadata.php:78
getMetadata(string $name)
Return the value of a piece of metadata.
Definition: Metadata.php:27
getAllMetadata()
Get all entity metadata.
Definition: Metadata.php:37
deleteMetadata(?string $name=null)
Deletes all metadata on this object (metadata.entity_guid = $this->guid).
Definition: Metadata.php:198
string version
Definition: conf.py:60
$path
Definition: details.php:70
$active
Definition: full.php:20
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
$priority
$plugin
$action
Definition: subscribe.php:11
if(! $menu instanceof \Elgg\Menu\PreparedMenu) $actions
Definition: user_hover.php:21
if($type !='user') $settings
Definition: save.php:16
if(elgg_view_exists("widgets/{$widget->handler}/edit")) $access
Definition: save.php:19
if(empty($page_owner)|| $owner->guid !==$page_owner->guid) $widgets
Definition: widgets.php:40