Elgg  Version master
ElggPlugin.php
Go to the documentation of this file.
1 <?php
2 
8 use Elgg\Includer;
11 
16 class ElggPlugin extends ElggObject {
17 
18  const PRIORITY_SETTING_NAME = 'elgg:internal:priority';
19  const STATIC_CONFIG_FILENAME = 'elgg-plugin.php';
20  const PUBLIC_SERVICES_FILENAME = 'elgg-services.php';
21 
22  use ElggLoggable;
23 
30  'README.txt',
31  'CHANGES.txt',
32  'INSTALL.txt',
33  'COPYRIGHT.txt',
34  'LICENSE.txt',
35  'README',
36  'README.md',
37  'README.markdown',
38  ];
39 
43  protected $composer;
44 
48  protected $path;
49 
55  protected $static_config;
56 
60  protected $activated;
61 
65  protected function initializeAttributes() {
66  parent::initializeAttributes();
67 
68  $this->attributes['subtype'] = 'plugin';
69  }
70 
81  public static function fromId(string $plugin_id, string $path = null): \ElggPlugin {
82  if (empty($plugin_id)) {
83  throw new ElggInvalidArgumentException('Plugin ID must be set');
84  }
85 
86  $plugin = elgg_get_plugin_from_id($plugin_id);
87  if (!$plugin) {
88  $plugin = elgg_call(ELGG_IGNORE_ACCESS, function() use ($plugin_id) {
89  $plugin = new ElggPlugin();
90  $plugin->title = $plugin_id;
91  $plugin->save();
92 
93  return $plugin;
94  });
95  }
96 
97  if (empty($path)) {
99  }
100 
101  $path = Paths::sanitize($path);
102  $plugin->setPath($path . $plugin_id);
103 
104  return $plugin;
105  }
106 
110  public function save(): bool {
111 
113 
114  $this->attributes['owner_guid'] = $site->guid;
115  $this->attributes['container_guid'] = $site->guid;
116  $this->attributes['access_id'] = ACCESS_PUBLIC;
117 
118  $new = !$this->guid;
119  $priority = null;
120  if ($new) {
121  $priority = elgg_extract(self::PRIORITY_SETTING_NAME, $this->temp_metadata, 'new');
122  } elseif ($this->getPriority() === null) {
123  $priority = 'last';
124  }
125 
126  if ($priority) {
127  $this->setPriority($priority);
128  }
129 
130  return parent::save();
131  }
132 
138  public function getID(): string {
139  return (string) $this->title;
140  }
141 
148  public function getDisplayName(): string {
149  $name = elgg_extract('name', $this->getStaticConfig('plugin', []), '');
150  if (!empty($name)) {
151  return $name;
152  }
153 
154  return ucwords(str_replace(['-', '_'], ' ', $this->getID()));
155  }
156 
165  public function setPath(string $path): void {
166  $this->path = Paths::sanitize($path, true);
167  }
168 
174  public function getPath(): string {
175  if (isset($this->path)) {
176  return $this->path;
177  }
178 
179  $this->setPath(elgg_get_plugins_path() . $this->getID());
180  return $this->path;
181  }
182 
189  protected function getLanguagesPath(): string {
190  return $this->getPath() . 'languages/';
191  }
192 
204  public function getStaticConfig(string $key, $default = null) {
205  if ($this->static_config === null) {
206  $this->static_config = [];
207 
208  try {
209  $this->static_config = $this->includeFile(self::STATIC_CONFIG_FILENAME);
210  } catch (PluginException $ex) {
211  elgg_log($ex, \Psr\Log\LogLevel::ERROR);
212  }
213  }
214 
215  return $this->static_config[$key] ?? $default;
216  }
217 
218  // Load Priority
219 
225  public function getPriority(): ?int {
226  $priority = $this->getMetadata(self::PRIORITY_SETTING_NAME);
227  if (isset($priority)) {
228  return (int) $priority;
229  }
230 
231  return null;
232  }
233 
245  public function setPriority($priority): int|false {
247 
248  return _elgg_services()->plugins->setPriority($this, $priority);
249  }
250 
259  protected function normalizePriority($priority): int {
260  // if no priority assume a priority of 1
261  $old_priority = $this->getPriority();
262  $old_priority = $old_priority ?: 1;
263  $max_priority = _elgg_services()->plugins->getMaxPriority() ?: 1;
264 
265  // can't use switch here because it's not strict and php evaluates +1 == 1
266  if ($priority === '+1') {
267  $priority = $old_priority + 1;
268  } else if ($priority === '-1') {
269  $priority = $old_priority - 1;
270  } else if ($priority === 'first') {
271  $priority = 1;
272  } else if ($priority === 'last') {
273  $priority = $max_priority;
274  } else if ($priority === 'new') {
275  $max_priority++;
276  $priority = $max_priority;
277  }
278 
279  return min($max_priority, max(1, (int) $priority));
280  }
281 
282  // Plugin settings
283 
292  public function getSetting(string $name, $default = null) {
293  $values = $this->getAllSettings();
294  return elgg_extract($name, $values, $default);
295  }
296 
304  public function getAllSettings(): array {
305 
306  try {
307  $defaults = [];
308  if ($this->isActive()) {
309  // only load settings from static config for active plugins to prevent issues
310  // with internal plugin references ie. classes and language keys
311  $defaults = $this->getStaticConfig('settings', []);
312  }
313 
314  $settings = $this->getAllMetadata();
315 
316  // title and description are not considered settings
317  unset($settings['title'], $settings['description']);
318 
319  return array_merge($defaults, $settings);
320  } catch (DatabaseException $ex) {
321  return [];
322  }
323  }
324 
333  public function setSetting(string $name, $value): bool {
334 
335  $value = _elgg_services()->events->triggerResults('setting', 'plugin', [
336  'plugin_id' => $this->getID(),
337  'plugin' => $this,
338  'name' => $name,
339  'value' => $value,
340  ], $value);
341 
342  if (is_array($value)) {
343  elgg_log('Plugin settings cannot store arrays.', 'ERROR');
344 
345  return false;
346  }
347 
348  return $this->setMetadata($name, $value);
349  }
350 
358  public function unsetSetting(string $name): bool {
359  return (bool) $this->deleteMetadata($name);
360  }
361 
366  public function unsetAllSettings(): bool {
367  $settings = $this->getAllSettings();
368 
369  foreach ($settings as $name => $value) {
370  if (str_starts_with($name, 'elgg:internal:')) {
371  continue;
372  }
373 
374  $this->unsetSetting($name);
375  }
376 
377  return true;
378  }
379 
386  public function unsetAllEntityAndPluginSettings(): bool {
387  // remove all plugin settings
388  $result = $this->unsetAllSettings();
389 
390  // entity plugin settings are stored with the entity
391  $delete = Delete::fromTable('metadata');
392  $delete->andWhere($delete->compare('name', 'like', "plugin:%_setting:{$this->getID()}:%", ELGG_VALUE_STRING));
393 
394  try {
395  _elgg_services()->db->deleteData($delete);
396  _elgg_services()->dataCache->metadata->clear();
397 
398  $result &= true;
399  } catch (DatabaseException $e) {
400  elgg_log($e, 'ERROR');
401 
402  $result &= false;
403  }
404 
405  // trigger an event, so plugin devs can also remove settings
406  $params = [
407  'entity' => $this,
408  ];
409  return (bool) _elgg_services()->events->triggerResults('remove:settings', 'plugin', $params, $result);
410  }
411 
418  public function isValid(): bool {
419  try {
420  $this->assertValid();
421  return true;
422  } catch (PluginException $e) {
423  return false;
424  }
425  }
426 
435  public function assertValid(): void {
436  if (!$this->getID()) {
437  throw PluginException::factory([
438  'message' => elgg_echo('ElggPlugin:MissingID', [$this->guid]),
439  'plugin' => $this,
440  ]);
441  }
442 
443  $this->getComposer()->assertPluginId();
444 
445  if (file_exists($this->getPath() . 'start.php')) {
446  throw PluginException::factory([
447  'message' => elgg_echo('ElggPlugin:StartFound', [$this->getID()]),
448  'plugin' => $this,
449  ]);
450  }
451  }
452 
458  public function isActive(): bool {
459  if (isset($this->activated)) {
460  return $this->activated;
461  }
462 
463  $this->activated = elgg_is_active_plugin($this->getID());
464  return $this->activated;
465  }
466 
473  public function canActivate(): bool {
474  if ($this->isActive()) {
475  return false;
476  }
477 
478  try {
479  $this->assertCanActivate();
480  return true;
481  } catch (PluginException $e) {
482  return false;
483  }
484  }
485 
494  public function assertCanActivate(): void {
495  $this->assertValid();
496  $this->assertDependencies();
497  }
498 
499  // activating and deactivating
500 
507  public function activate(): bool {
508  if ($this->isActive()) {
509  return false;
510  }
511 
512  $this->assertCanActivate();
513 
514  // Check this before setting status because the file could potentially throw
515  $this->assertStaticConfigValid();
516 
517  if (!$this->setStatus(true)) {
518  return false;
519  }
520 
521  // perform tasks and emit events
522  // emit an event. returning false will make this not be activated.
523  // we need to do this after it's been fully activated
524  // or the deactivate will be confused.
525  $params = [
526  'plugin_id' => $this->getID(),
527  'plugin_entity' => $this,
528  ];
529 
530  $return = _elgg_services()->events->trigger('activate', 'plugin', $params);
531 
532  // if there are any on_enable functions, start the plugin now and run them
533  // Note: this will not run re-run the init events!
534  if ($return) {
535  try {
537 
538  $this->register();
539 
540  // directly load languages to have them available during runtime
541  $this->loadLanguages();
542 
543  $this->boot();
544 
545  $this->getBootstrap()->activate();
546 
547  $this->init();
548  } catch (PluginException $ex) {
549  elgg_log($ex, \Psr\Log\LogLevel::ERROR);
550 
551  $return = false;
552  }
553  }
554 
555  if ($return === false) {
556  $this->deactivate();
557  } else {
558  elgg_delete_admin_notice("cannot_start {$this->getID()}");
559 
561  _elgg_services()->logger->notice("Plugin {$this->getID()} has been activated");
562  }
563 
564  return $return;
565  }
566 
572  public function getDependencies(): array {
573  $plugin_config = $this->getStaticConfig('plugin', []);
574  return (array) elgg_extract('dependencies', $plugin_config, []);
575  }
576 
584  public function canDeactivate(): bool {
585  if (!$this->isActive()) {
586  return false;
587  }
588 
589  try {
590  $this->assertcanDeactivate();
591  return true;
592  } catch (PluginException $e) {
593  return false;
594  }
595  }
596 
605  public function assertCanDeactivate(): void {
606  $dependents = [];
607 
608  $active_plugins = elgg_get_plugins();
609 
610  foreach ($active_plugins as $plugin) {
611  $dependencies = $plugin->getDependencies();
612  if (!array_key_exists($this->getID(), $dependencies)) {
613  continue;
614  }
615 
616  if (elgg_extract('must_be_active', $dependencies[$this->getID()], true)) {
617  $dependents[$plugin->getID()] = $plugin;
618  }
619  }
620 
621  if (empty($dependents)) {
622  return;
623  }
624 
625  $list = array_map(function (\ElggPlugin $plugin) {
626  $css_id = preg_replace('/[^a-z0-9-]/i', '-', $plugin->getID());
627 
628  return elgg_view('output/url', [
629  'text' => $plugin->getDisplayName(),
630  'href' => "#{$css_id}",
631  ]);
632  }, $dependents);
633 
634  $list = implode(', ', $list);
635  throw PluginException::factory([
636  'message' => elgg_echo('ElggPlugin:Dependencies:ActiveDependent', [$this->getDisplayName(), $list]),
637  'plugin' => $this,
638  ]);
639  }
640 
648  public function deactivate(): bool {
649  if (!$this->isActive()) {
650  return false;
651  }
652 
653  $this->assertCanDeactivate();
654 
655  // emit an event. returning false will cause this to not be deactivated.
656  $params = [
657  'plugin_id' => $this->getID(),
658  'plugin_entity' => $this,
659  ];
660 
661  $return = _elgg_services()->events->trigger('deactivate', 'plugin', $params);
662  if ($return === false) {
663  return false;
664  }
665 
666  $this->getBootstrap()->deactivate();
667 
668  $this->deactivateEntities();
669 
671 
672  _elgg_services()->logger->notice("Plugin {$this->getID()} has been deactivated");
673 
674  return $this->setStatus(false);
675  }
676 
685  public function getBootstrap(): \Elgg\PluginBootstrapInterface {
686  $bootstrap = $this->getStaticConfig('bootstrap');
687  if ($bootstrap) {
688  if (!is_subclass_of($bootstrap, \Elgg\PluginBootstrapInterface::class)) {
689  throw PluginException::factory([
690  'message' => elgg_echo('LogicException:InterfaceNotImplemented', [
691  $bootstrap,
692  \Elgg\PluginBootstrapInterface::class
693  ]),
694  'plugin' => $this,
695  ]);
696  }
697 
698  return new $bootstrap($this, elgg());
699  }
700 
701  return new \Elgg\DefaultPluginBootstrap($this, elgg());
702  }
703 
710  public function autoload(): void {
711  $this->registerClasses();
712 
713  $autoload_file = 'vendor/autoload.php';
714  if (!$this->canReadFile($autoload_file)) {
715  return;
716  }
717 
718  $autoloader = Includer::requireFileOnce("{$this->getPath()}{$autoload_file}");
719 
720  if (!$autoloader instanceof \Composer\Autoload\ClassLoader) {
721  return;
722  }
723 
724  $autoloader->unregister();
725 
726  // plugins should be appended, composer defaults to prepend
727  $autoloader->register(false);
728  }
729 
738  public function register(): void {
739  $this->autoload();
740 
741  $this->registerPublicServices();
742  $this->activateEntities();
743  $this->registerLanguages();
744  $this->registerViews();
745 
746  $this->getBootstrap()->load();
747  }
748 
755  public function boot(): void {
756  $this->getBootstrap()->boot();
757  }
758 
765  public function init(): void {
766  $this->registerRoutes();
767  $this->registerActions();
768  $this->registerEntities();
769  $this->registerWidgets();
770  $this->registerHooks();
771  $this->registerEvents();
772  $this->registerViewExtensions();
773  $this->registerGroupTools();
774  $this->registerViewOptions();
775  $this->registerNotifications();
776 
777  $this->getBootstrap()->init();
778  }
779 
788  protected function includeFile(string $filename) {
789  $filepath = "{$this->getPath()}{$filename}";
790 
791  if (!$this->canReadFile($filename)) {
792  $msg = elgg_echo(
793  'ElggPlugin:Exception:CannotIncludeFile',
794  [$filename, $this->getID(), $this->guid, $this->getPath()]
795  );
796 
797  throw PluginException::factory([
798  'message' => $msg,
799  'plugin' => $this,
800  ]);
801  }
802 
803  try {
804  $ret = Includer::requireFile($filepath);
805  } catch (Exception $e) {
806  $msg = elgg_echo(
807  'ElggPlugin:Exception:IncludeFileThrew',
808  [$filename, $this->getID(), $this->guid, $this->getPath()]
809  );
810 
811  throw PluginException::factory([
812  'message' => $msg,
813  'previous' => $e,
814  'plugin' => $this,
815  ]);
816  }
817 
818  return $ret;
819  }
820 
828  protected function canReadFile(string $filename): bool {
829  return is_file("{$this->getPath()}{$filename}");
830  }
831 
838  protected function assertStaticConfigValid(): void {
839  if (!$this->canReadFile(self::STATIC_CONFIG_FILENAME)) {
840  return;
841  }
842 
843  ob_start();
844  $value = $this->includeFile(self::STATIC_CONFIG_FILENAME);
845  if (ob_get_clean() !== '') {
846  throw PluginException::factory([
847  'message' => elgg_echo('ElggPlugin:activate:ConfigSentOutput'),
848  'plugin' => $this,
849  ]);
850  }
851 
852  // make sure can serialize
853  $value = @unserialize(serialize($value));
854  if (!is_array($value)) {
855  throw PluginException::factory([
856  'message' => elgg_echo('ElggPlugin:activate:BadConfigFormat'),
857  'plugin' => $this,
858  ]);
859  }
860  }
861 
867  protected function registerPublicServices(): void {
868  $services_path = $this->getPath() . self::PUBLIC_SERVICES_FILENAME;
869  if (!is_file($services_path)) {
870  return;
871  }
872 
873  $services = Includer::includeFile($services_path);
874  foreach ($services as $name => $definition) {
875  elgg()->set($name, $definition);
876  }
877  }
878 
885  protected function registerViews(): void {
886  if (_elgg_services()->config->system_cache_loaded) {
887  return;
888  }
889 
890  $views = _elgg_services()->views;
891 
892  // Declared views first
893  $spec = $this->getStaticConfig('views');
894  if ($spec) {
895  $views->mergeViewsSpec($spec);
896  }
897 
898  // Allow /views directory files to override
899  if (!$views->registerPluginViews($this->getPath())) {
900  $msg = elgg_echo('ElggPlugin:Exception:CannotRegisterViews', [$this->getID(), $this->guid, $this->getPath()]);
901 
902  throw PluginException::factory([
903  'message' => $msg,
904  'plugin' => $this,
905  ]);
906  }
907  }
908 
914  protected function registerEntities(): void {
915  $spec = (array) $this->getStaticConfig('entities', []);
916 
917  foreach ($spec as $entity) {
918  if (!isset($entity['type']) || !isset($entity['subtype'])) {
919  continue;
920  }
921 
922  $capabilities = elgg_extract('capabilities', $entity, []);
923  foreach ($capabilities as $capability => $value) {
924  _elgg_services()->entity_capabilities->setCapability($entity['type'], $entity['subtype'], $capability, $value);
925  }
926  }
927  }
928 
934  protected function registerActions(): void {
935  $actions = _elgg_services()->actions;
936  $root_path = $this->getPath();
937 
938  $spec = (array) $this->getStaticConfig('actions', []);
939 
940  foreach ($spec as $action => $action_spec) {
941  if (!is_array($action_spec)) {
942  continue;
943  }
944 
945  $access = elgg_extract('access', $action_spec, 'logged_in');
946  $handler = elgg_extract('controller', $action_spec);
947  if (!$handler) {
948  $handler = elgg_extract('filename', $action_spec);
949  if (!$handler) {
950  $handler = "{$root_path}actions/{$action}.php";
951  }
952  }
953 
954  // unset handled action specs, pass the rest to the action service
955  unset($action_spec['access']);
956  unset($action_spec['controller']);
957  unset($action_spec['filename']);
958 
959  $actions->register($action, $handler, $access, $action_spec);
960  }
961  }
962 
968  protected function registerRoutes(): void {
969  $routes = _elgg_services()->routes;
970 
971  $spec = (array) $this->getStaticConfig('routes', []);
972  foreach ($spec as $name => $route_spec) {
973  if (!is_array($route_spec)) {
974  continue;
975  }
976 
977  $routes->register($name, $route_spec);
978  }
979  }
980 
986  protected function registerWidgets(): void {
987  $widgets = _elgg_services()->widgets;
988 
989  $spec = (array) $this->getStaticConfig('widgets', []);
990  foreach ($spec as $widget_id => $widget_definition) {
991  if (!is_array($widget_definition)) {
992  continue;
993  }
994 
995  if (!isset($widget_definition['id'])) {
996  $widget_definition['id'] = $widget_id;
997  }
998 
999  $definition = \Elgg\WidgetDefinition::factory($widget_definition);
1000 
1001  $widgets->registerType($definition);
1002  }
1003  }
1004 
1012  public function registerLanguages(): void {
1013  _elgg_services()->translator->registerLanguagePath($this->getLanguagesPath());
1014  }
1015 
1025  protected function loadLanguages(): void {
1026  $languages_path = $this->getLanguagesPath();
1027  if (!is_dir($languages_path)) {
1028  return;
1029  }
1030 
1031  _elgg_services()->translator->registerTranslations($languages_path);
1032  }
1033 
1039  protected function registerClasses(): void {
1040  _elgg_services()->autoloadManager->addClasses("{$this->getPath()}classes");
1041  }
1042 
1048  protected function activateEntities(): void {
1049  $spec = (array) $this->getStaticConfig('entities', []);
1050 
1051  foreach ($spec as $entity) {
1052  if (isset($entity['type'], $entity['subtype'], $entity['class'])) {
1053  _elgg_services()->entityTable->setEntityClass($entity['type'], $entity['subtype'], $entity['class']);
1054  }
1055  }
1056  }
1057 
1063  protected function deactivateEntities(): void {
1064  $spec = (array) $this->getStaticConfig('entities', []);
1065 
1066  foreach ($spec as $entity) {
1067  if (isset($entity['type'], $entity['subtype'], $entity['class'])) {
1068  _elgg_services()->entityTable->setEntityClass($entity['type'], $entity['subtype']);
1069  }
1070  }
1071  }
1072 
1080  protected function registerHooks(): void {
1081  $events = _elgg_services()->events;
1082 
1083  $spec = (array) $this->getStaticConfig('hooks', []);
1084 
1085  if (!empty($spec)) {
1086  elgg_deprecated_notice("The plugin {$this->getID()} still has hooks definitions in the elgg-plugin.php. This should be moved to the events configuration.", '5.0');
1087  }
1088 
1089  foreach ($spec as $name => $types) {
1090  foreach ($types as $type => $callbacks) {
1091  foreach ($callbacks as $callback => $hook_spec) {
1092  if (!is_array($hook_spec)) {
1093  continue;
1094  }
1095 
1096  $unregister = (bool) elgg_extract('unregister', $hook_spec, false);
1097 
1098  if ($unregister) {
1099  $events->unregisterHandler($name, $type, $callback);
1100  } else {
1101  $priority = (int) elgg_extract('priority', $hook_spec, 500);
1102 
1103  $events->registerHandler($name, $type, $callback, $priority);
1104  }
1105  }
1106  }
1107  }
1108  }
1109 
1115  protected function registerEvents(): void {
1116  $events = _elgg_services()->events;
1117 
1118  $spec = (array) $this->getStaticConfig('events', []);
1119 
1120  foreach ($spec as $name => $types) {
1121  foreach ($types as $type => $callbacks) {
1122  foreach ($callbacks as $callback => $event_spec) {
1123  if (!is_array($event_spec)) {
1124  continue;
1125  }
1126 
1127  $unregister = (bool) elgg_extract('unregister', $event_spec, false);
1128 
1129  if ($unregister) {
1130  $events->unregisterHandler($name, $type, $callback);
1131  } else {
1132  $priority = (int) elgg_extract('priority', $event_spec, 500);
1133 
1134  $events->registerHandler($name, $type, $callback, $priority);
1135  }
1136  }
1137  }
1138  }
1139  }
1140 
1146  protected function registerViewExtensions(): void {
1147  $views = _elgg_services()->views;
1148 
1149  $spec = (array) $this->getStaticConfig('view_extensions', []);
1150 
1151  foreach ($spec as $src_view => $extensions) {
1152  foreach ($extensions as $extention => $extention_spec) {
1153  if (!is_array($extention_spec)) {
1154  continue;
1155  }
1156 
1157  $unextend = (bool) elgg_extract('unextend', $extention_spec, false);
1158 
1159  if ($unextend) {
1160  $views->unextendView($src_view, $extention);
1161  } else {
1162  $priority = (int) elgg_extract('priority', $extention_spec, 501);
1163 
1164  $views->extendView($src_view, $extention, $priority);
1165  }
1166  }
1167  }
1168  }
1169 
1175  protected function registerGroupTools(): void {
1176  $tools = _elgg_services()->group_tools;
1177 
1178  $spec = (array) $this->getStaticConfig('group_tools', []);
1179 
1180  foreach ($spec as $tool_name => $tool_options) {
1181  if (!is_array($tool_options)) {
1182  continue;
1183  }
1184 
1185  $unregister = (bool) elgg_extract('unregister', $tool_options, false);
1186 
1187  if ($unregister) {
1188  $tools->unregister($tool_name);
1189  } else {
1190  $tools->register($tool_name, $tool_options);
1191  }
1192  }
1193  }
1194 
1200  protected function registerViewOptions(): void {
1201  $spec = (array) $this->getStaticConfig('view_options', []);
1202 
1203  foreach ($spec as $view_name => $options) {
1204  if (!is_array($options)) {
1205  continue;
1206  }
1207 
1208  if (isset($options['ajax'])) {
1209  if ($options['ajax'] === true) {
1210  _elgg_services()->ajax->registerView($view_name);
1211  } else {
1212  _elgg_services()->ajax->unregisterView($view_name);
1213  }
1214  }
1215 
1216  if (isset($options['simplecache']) && $options['simplecache'] === true) {
1217  _elgg_services()->views->registerCacheableView($view_name);
1218  }
1219  }
1220  }
1221 
1227  protected function registerNotifications(): void {
1228  $spec = (array) $this->getStaticConfig('notifications', []);
1229 
1230  foreach ($spec as $type => $subtypes) {
1231  foreach ($subtypes as $subtype => $actions) {
1232  foreach ($actions as $action => $callback) {
1233  if ($callback === false) {
1234  _elgg_services()->notifications->unregisterEvent($type, $subtype, [$action]);
1235  } elseif ($callback === true) {
1236  _elgg_services()->notifications->registerEvent($type, $subtype, [$action]);
1237  } else {
1238  _elgg_services()->notifications->registerEvent($type, $subtype, [$action], $callback);
1239  }
1240  }
1241  }
1242  }
1243  }
1244 
1252  public function __get($name) {
1253  // See if its in our base attribute
1254  if (array_key_exists($name, $this->attributes)) {
1255  return $this->attributes[$name];
1256  }
1257 
1258  // object title and description are stored as metadata
1259  if (in_array($name, ['title', 'description'])) {
1260  return parent::__get($name);
1261  }
1262 
1263  $result = $this->getSetting($name);
1264  if ($result !== null) {
1265  return $result;
1266  }
1267 
1268  $defaults = $this->getStaticConfig('settings', []);
1269 
1270  return elgg_extract($name, $defaults, $result);
1271  }
1272 
1281  public function __set($name, $value) {
1282  if (array_key_exists($name, $this->attributes)) {
1283  // Check that we're not trying to change the guid!
1284  if ((array_key_exists('guid', $this->attributes)) && ($name == 'guid')) {
1285  return;
1286  }
1287 
1288  $this->attributes[$name] = $value;
1289 
1290  return;
1291  }
1292 
1293  // object title and description are stored as metadata
1294  if (in_array($name, ['title', 'description'])) {
1295  parent::__set($name, $value);
1296 
1297  return;
1298  }
1299 
1300  // to make sure we trigger the correct events
1301  $this->setSetting($name, $value);
1302  }
1303 
1311  protected function setStatus(bool $active): bool {
1312  if (!$this->guid) {
1313  return false;
1314  }
1315 
1317  if ($active) {
1318  $result = _elgg_services()->relationshipsTable->add($this->guid, 'active_plugin', $site->guid);
1319  } else {
1320  $result = _elgg_services()->relationshipsTable->remove($this->guid, 'active_plugin', $site->guid);
1321  }
1322 
1323  if ($result) {
1324  $this->activated = $active;
1325  }
1326 
1327  $this->invalidateCache();
1328 
1329  return $result;
1330  }
1331 
1335  public function isCacheable(): bool {
1336  return true;
1337  }
1338 
1342  public function cache(bool $persist = true): void {
1343  _elgg_services()->plugins->cache($this);
1344 
1345  parent::cache($persist);
1346  }
1347 
1351  public function invalidateCache(): void {
1352 
1353  _elgg_services()->boot->clearCache();
1354  _elgg_services()->plugins->invalidateCache($this->getID());
1355 
1356  parent::invalidateCache();
1357  }
1358 
1366  protected function getComposer(): \Elgg\Plugin\Composer {
1367  if (isset($this->composer)) {
1368  return $this->composer;
1369  }
1370 
1371  $this->composer = new \Elgg\Plugin\Composer($this);
1372  return $this->composer;
1373  }
1374 
1382  public function meetsDependencies(): bool {
1383  try {
1384  $this->assertDependencies();
1385 
1386  return true;
1387  } catch (PluginException $e) {
1388  return false;
1389  }
1390  }
1391 
1400  public function assertDependencies(): void {
1401  $this->getComposer()->assertConflicts();
1402  $this->getComposer()->assertActivePluginConflicts();
1403  $this->getComposer()->assertRequiredPhpVersion();
1404  $this->getComposer()->assertRequiredPhpExtensions();
1405  $this->assertPluginDependencies();
1406  }
1407 
1416  protected function assertPluginDependencies(): void {
1417  foreach ($this->getDependencies() as $plugin_id => $plugin_dep) {
1418  $must_be_active = elgg_extract('must_be_active', $plugin_dep, true);
1419  $position = elgg_extract('position', $plugin_dep);
1420 
1421  $dependent_plugin = elgg_get_plugin_from_id($plugin_id);
1422 
1423  if ($must_be_active && (!$dependent_plugin instanceof \ElggPlugin || !$dependent_plugin->isActive())) {
1424  throw PluginException::factory([
1425  'message' => elgg_echo('PluginException:PluginMustBeActive', [$plugin_id]),
1426  'plugin' => $this,
1427  ]);
1428  }
1429 
1430  if ($dependent_plugin instanceof \ElggPlugin && $position && $dependent_plugin->isActive()) {
1431  if ($position == 'after' && ($this->getPriority() < $dependent_plugin->getPriority())) {
1432  throw PluginException::factory([
1433  'message' => elgg_echo('PluginException:PluginMustBeAfter', [$plugin_id]),
1434  'plugin' => $this,
1435  ]);
1436  } elseif ($position == 'before' && ($this->getPriority() > $dependent_plugin->getPriority())) {
1437  throw PluginException::factory([
1438  'message' => elgg_echo('PluginException:PluginMustBeBefore', [$plugin_id]),
1439  'plugin' => $this,
1440  ]);
1441  }
1442  }
1443  }
1444  }
1445 
1451  public function getVersion(): string {
1452  // composer version
1453  $version = $this->getComposer()->getConfiguration()->version();
1454  if (!elgg_is_empty($version)) {
1455  return $version;
1456  }
1457 
1458  // elgg-plugin version
1459  $plugin_config = $this->getStaticConfig('plugin', []);
1460  $version = elgg_extract('version', $plugin_config);
1461  if (!elgg_is_empty($version)) {
1462  return $version;
1463  }
1464 
1465  // bundled plugins use elgg version
1466  if (in_array($this->getID(), Plugins::BUNDLED_PLUGINS)) {
1467  return elgg_get_release();
1468  }
1469 
1470  return '0.1';
1471  }
1472 
1480  public function getCategories(): array {
1481  return $this->getComposer()->getCategories();
1482  }
1483 
1491  public function getLicense(): string {
1492  return $this->getComposer()->getLicense();
1493  }
1494 
1502  public function getDescription(): string {
1503  return (string) $this->getComposer()->getConfiguration()->description();
1504  }
1505 
1513  public function getRepositoryURL(): string {
1514  return (string) $this->getComposer()->getConfiguration()->support()->source();
1515  }
1516 
1524  public function getBugTrackerURL(): string {
1525  return (string) $this->getComposer()->getConfiguration()->support()->issues();
1526  }
1527 
1535  public function getWebsite(): string {
1536  return (string) $this->getComposer()->getConfiguration()->homepage();
1537  }
1538 
1546  public function getAuthors(): array {
1547  return (array) $this->getComposer()->getConfiguration()->authors();
1548  }
1549 
1557  public function getConflicts(): array {
1558  return $this->getComposer()->getConflicts();
1559  }
1560 }
$default
Definition: checkbox.php:31
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:299
registerViews()
Registers the plugin&#39;s views.
Definition: ElggPlugin.php:885
registerEntities()
Registers the plugin&#39;s entities.
Definition: ElggPlugin.php:914
getID()
Returns the ID (dir name) of this plugin.
Definition: ElggPlugin.php:138
elgg_get_release()
Get the current Elgg release.
elgg_get_plugins(string $status= 'active')
Returns an ordered list of plugins.
Definition: plugins.php:55
setMetadata(string $name, $value, string $value_type= '', bool $multiple=false)
Set metadata on this entity.
Definition: ElggEntity.php:370
$plugin
registerActions()
Registers the plugin&#39;s actions provided in the plugin config file.
Definition: ElggPlugin.php:934
cache(bool $persist=true)
{}
$params
Saves global plugin settings.
Definition: save.php:13
elgg_deprecated_notice(string $msg, string $dep_version)
Log a notice about deprecated use of a function, view, etc.
Definition: elgglib.php:115
$position
Definition: add.php:11
getLanguagesPath()
Returns the plugin&#39;s languages directory full path with trailing slash.
Definition: ElggPlugin.php:189
registerClasses()
Registers the plugin&#39;s classes.
assertCanDeactivate()
Asserts if a plugin can be deactivated.
Definition: ElggPlugin.php:605
const STATIC_CONFIG_FILENAME
Definition: ElggPlugin.php:19
$defaults
Generic entity header upload helper.
Definition: header.php:6
setSetting(string $name, $value)
Set a plugin setting for the plugin.
Definition: ElggPlugin.php:333
if(!$user||!$user->canDelete()) $name
Definition: delete.php:22
elgg_get_plugin_from_id(string $plugin_id)
Elgg plugins library Contains functions for managing plugins.
Definition: plugins.php:15
getAllSettings()
Returns an array of all settings saved for this plugin.
Definition: ElggPlugin.php:304
$title
Definition: generic.php:50
if(elgg_view_exists("widgets/{$widget->handler}/edit")) $access
Definition: save.php:19
$version
getConflicts()
Returns an array of projectnames with their conflicting version.
c Accompany it with the information you received as to the offer to distribute corresponding source complete source code means all the source code for all modules it plus any associated interface definition plus the scripts used to control compilation and installation of the executable as a special the source code distributed need not include anything that is normally and so on of the operating system on which the executable unless that component itself accompanies the executable If distribution of executable or object code is made by offering access to copy from a designated then offering equivalent access to copy the source code from the same place counts as distribution of the source even though third parties are not compelled to copy the source along with the object code You may not or distribute the Program except as expressly provided under this License Any attempt otherwise to sublicense or distribute the Program is void
Definition: LICENSE.txt:215
assertValid()
Asserts if a plugin is valid.
Definition: ElggPlugin.php:435
elgg_echo(string $message_key, array $args=[], string $language= '')
Elgg language module Functions to manage language and translations.
Definition: languages.php:17
invalidateCache()
{}
registerPublicServices()
Registers the plugin public services.
Definition: ElggPlugin.php:867
meetsDependencies()
Checks if dependencies are met.
$delete
$type
Definition: delete.php:22
unsetSetting(string $name)
Removes a plugin setting name and value.
Definition: ElggPlugin.php:358
getDescription()
Return the description.
registerRoutes()
Registers the plugin&#39;s routes provided in the plugin config file.
Definition: ElggPlugin.php:968
setPriority($priority)
Sets the priority of the plugin Returns the new priority or false on error.
Definition: ElggPlugin.php:245
$site
Definition: icons.php:5
isCacheable()
{}
activateEntities()
Activates the plugin&#39;s entities.
if($type!= 'user') $settings
Definition: save.php:16
assertCanActivate()
Asserts if a plugin can activate.
Definition: ElggPlugin.php:494
const PUBLIC_SERVICES_FILENAME
Definition: ElggPlugin.php:20
if($item instanceof\ElggEntity) elseif($item instanceof\ElggRiverItem) elseif($item instanceof\ElggRelationship) elseif(is_callable([$item, 'getType']))
Definition: item.php:48
$options
Elgg admin footer.
Definition: footer.php:6
$plugin_id
Remove all user and plugin settings from the give plugin ID.
Definition: remove.php:8
elgg_delete_admin_notice(string $id)
Remove an admin notice by ID.
Definition: admin.php:63
if(empty($page_owner)||$owner->guid!=$page_owner->guid) $widgets
Definition: widgets.php:40
deactivateEntities()
Deactivates the plugin&#39;s entities.
$value
Definition: generic.php:51
elgg_is_empty($value)
Check if a value isn&#39;t empty, but allow 0 and &#39;0&#39;.
Definition: input.php:176
canDeactivate()
Checks if this plugin can be deactivated on the current Elgg installation.
Definition: ElggPlugin.php:584
elgg_invalidate_caches()
Invalidate all the registered caches.
Definition: cache.php:183
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:254
getDependencies()
Returns an array of dependencies as configured in the static config.
Definition: ElggPlugin.php:572
const ADDITIONAL_TEXT_FILES
Definition: ElggPlugin.php:29
getDisplayName()
Returns the name from elgg-plugin.php if available, otherwise a nicely formatted ID.
Definition: ElggPlugin.php:148
assertStaticConfigValid()
If a static config file is present, is it a serializable array?
Definition: ElggPlugin.php:838
setPath(string $path)
Set path.
Definition: ElggPlugin.php:165
const ELGG_IGNORE_ACCESS
elgg_call() flags
Definition: constants.php:130
elgg_view(string $view, array $vars=[], string $viewtype= '')
Return a parsed view.
Definition: views.php:177
assertDependencies()
Assert plugin dependencies.
isActive()
Is this plugin active?
Definition: ElggPlugin.php:458
$entity
Definition: reset.php:8
getRepositoryURL()
Returns the repository url.
static factory(array $options)
Create an WidgetDefinition from an associative array.
registerLanguages()
Registers the plugin&#39;s languages.
getCategories()
Returns an array with categories.
elgg_is_active_plugin(string $plugin_id)
Returns if a plugin is active for a current site.
Definition: plugins.php:43
$active
Definition: full.php:17
setStatus(bool $active)
Sets the plugin to active or inactive.
init()
Init the plugin.
Definition: ElggPlugin.php:765
elgg_log($message, $level=\Psr\Log\LogLevel::NOTICE)
Log a message.
Definition: elgglib.php:86
initializeAttributes()
{}
Definition: ElggPlugin.php:65
A generic parent class for database exceptions.
normalizePriority($priority)
Normalize and validate new priority.
Definition: ElggPlugin.php:259
unsetAllSettings()
Removes all settings for this plugin.
Definition: ElggPlugin.php:366
deleteMetadata(string $name=null)
Deletes all metadata on this object (metadata.entity_guid = $this->guid).
Definition: ElggEntity.php:483
getBugTrackerURL()
Returns the bug tracker page.
if(!$menu instanceof\Elgg\Menu\PreparedMenu) $actions
Definition: user_hover.php:16
getWebsite()
Return the website.
getBootstrap()
Bootstrap object.
Definition: ElggPlugin.php:685
isValid()
Returns if the plugin is complete, meaning has all required files and Elgg can read them and they mak...
Definition: ElggPlugin.php:418
elgg_get_site_entity()
Get the current site entity.
Definition: entities.php:98
const PRIORITY_SETTING_NAME
Definition: ElggPlugin.php:18
$extensions
registerHooks()
Registers the plugin&#39;s hooks provided in the plugin config file.
getVersion()
Returns the plugin version.
$action
Definition: subscribe.php:11
if($container instanceof ElggGroup &&$container->guid!=elgg_get_page_owner_guid()) $key
Definition: summary.php:44
registerViewExtensions()
Registers the plugin&#39;s view extensions provided in the plugin config file.
activate()
Activates the plugin for the current site.
Definition: ElggPlugin.php:507
registerWidgets()
Registers the plugin&#39;s widgets provided in the plugin config file.
Definition: ElggPlugin.php:986
boot()
Boot the plugin.
Definition: ElggPlugin.php:755
deactivate()
Deactivates the plugin.
Definition: ElggPlugin.php:648
__get($name)
Get an attribute or setting value.
getComposer()
Returns the composer parser.
const ELGG_VALUE_STRING
Definition: constants.php:112
canReadFile(string $filename)
Checks whether a plugin file with the given name exists.
Definition: ElggPlugin.php:828
registerNotifications()
Registers the plugin&#39;s notification events.
$subtype
Definition: delete.php:23
registerEvents()
Registers the plugin&#39;s events provided in the plugin config file.
$subtypes
getPriority()
Gets the plugin&#39;s load priority.
Definition: ElggPlugin.php:225
__set($name, $value)
Set a value as attribute or setting.
getAllMetadata()
Get all entity metadata.
Definition: ElggEntity.php:343
_elgg_services()
Get the global service provider.
Definition: elgglib.php:346
loadLanguages()
Loads the plugin&#39;s translations.
const ACCESS_PUBLIC
Definition: constants.php:12
elgg_get_plugins_path()
Get the plugin path for this installation, ending with slash.
$handler
Definition: add.php:7
static fromId(string $plugin_id, string $path=null)
Load a plugin object from its ID Create a new plugin entity if doesn&#39;t exist.
Definition: ElggPlugin.php:81
getSetting(string $name, $default=null)
Returns a plugin setting.
Definition: ElggPlugin.php:292
registerGroupTools()
Registers the plugin&#39;s group tools provided in the plugin config file.
canActivate()
Checks if this plugin can be activated on the current Elgg installation.
Definition: ElggPlugin.php:473
includeFile(string $filename)
Includes one of the plugins files.
Definition: ElggPlugin.php:788
getPath()
Returns the plugin&#39;s full path with trailing slash.
Definition: ElggPlugin.php:174
getLicense()
Returns the license.
assertPluginDependencies()
Assert required plugins or plugin position.
var elgg
Definition: elgglib.js:4
autoload()
Register plugin classes and require composer autoloader.
Definition: ElggPlugin.php:710
unsetAllEntityAndPluginSettings()
Remove all entity and plugin settings for this plugin.
Definition: ElggPlugin.php:386
$priority
$views
Definition: item.php:17
getMetadata(string $name)
Return the value of a piece of metadata.
Definition: ElggEntity.php:333
registerViewOptions()
Registers the plugin&#39;s view options provided in the plugin config file.
getStaticConfig(string $key, $default=null)
Get a value from the plugins&#39;s static config file.
Definition: ElggPlugin.php:204
$guid
Reset an ElggUpgrade.
Definition: reset.php:6
getAuthors()
Returns an array of authors.