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 (strpos($name, 'elgg:internal:') === 0) {
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  $actions->register($action, $handler, $access);
955  }
956  }
957 
963  protected function registerRoutes(): void {
964  $routes = _elgg_services()->routes;
965 
966  $spec = (array) $this->getStaticConfig('routes', []);
967  foreach ($spec as $name => $route_spec) {
968  if (!is_array($route_spec)) {
969  continue;
970  }
971 
972  $routes->register($name, $route_spec);
973  }
974  }
975 
981  protected function registerWidgets(): void {
982  $widgets = _elgg_services()->widgets;
983 
984  $spec = (array) $this->getStaticConfig('widgets', []);
985  foreach ($spec as $widget_id => $widget_definition) {
986  if (!is_array($widget_definition)) {
987  continue;
988  }
989  if (!isset($widget_definition['id'])) {
990  $widget_definition['id'] = $widget_id;
991  }
992 
993  $definition = \Elgg\WidgetDefinition::factory($widget_definition);
994 
995  $widgets->registerType($definition);
996  }
997  }
998 
1006  public function registerLanguages(): void {
1007  _elgg_services()->translator->registerLanguagePath($this->getLanguagesPath());
1008  }
1009 
1019  protected function loadLanguages(): void {
1020  $languages_path = $this->getLanguagesPath();
1021  if (!is_dir($languages_path)) {
1022  return;
1023  }
1024 
1025  _elgg_services()->translator->registerTranslations($languages_path);
1026  }
1027 
1033  protected function registerClasses(): void {
1034  _elgg_services()->autoloadManager->addClasses("{$this->getPath()}classes");
1035  }
1036 
1042  protected function activateEntities(): void {
1043  $spec = (array) $this->getStaticConfig('entities', []);
1044 
1045  foreach ($spec as $entity) {
1046  if (isset($entity['type'], $entity['subtype'], $entity['class'])) {
1047  _elgg_services()->entityTable->setEntityClass($entity['type'], $entity['subtype'], $entity['class']);
1048  }
1049  }
1050  }
1051 
1057  protected function deactivateEntities(): void {
1058  $spec = (array) $this->getStaticConfig('entities', []);
1059 
1060  foreach ($spec as $entity) {
1061  if (isset($entity['type'], $entity['subtype'], $entity['class'])) {
1062  _elgg_services()->entityTable->setEntityClass($entity['type'], $entity['subtype']);
1063  }
1064  }
1065  }
1066 
1074  protected function registerHooks(): void {
1075  $events = _elgg_services()->events;
1076 
1077  $spec = (array) $this->getStaticConfig('hooks', []);
1078 
1079  if (!empty($spec)) {
1080  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');
1081  }
1082 
1083  foreach ($spec as $name => $types) {
1084  foreach ($types as $type => $callbacks) {
1085  foreach ($callbacks as $callback => $hook_spec) {
1086  if (!is_array($hook_spec)) {
1087  continue;
1088  }
1089 
1090  $unregister = (bool) elgg_extract('unregister', $hook_spec, false);
1091 
1092  if ($unregister) {
1093  $events->unregisterHandler($name, $type, $callback);
1094  } else {
1095  $priority = (int) elgg_extract('priority', $hook_spec, 500);
1096 
1097  $events->registerHandler($name, $type, $callback, $priority);
1098  }
1099  }
1100  }
1101  }
1102  }
1103 
1109  protected function registerEvents(): void {
1110  $events = _elgg_services()->events;
1111 
1112  $spec = (array) $this->getStaticConfig('events', []);
1113 
1114  foreach ($spec as $name => $types) {
1115  foreach ($types as $type => $callbacks) {
1116  foreach ($callbacks as $callback => $event_spec) {
1117  if (!is_array($event_spec)) {
1118  continue;
1119  }
1120 
1121  $unregister = (bool) elgg_extract('unregister', $event_spec, false);
1122 
1123  if ($unregister) {
1124  $events->unregisterHandler($name, $type, $callback);
1125  } else {
1126  $priority = (int) elgg_extract('priority', $event_spec, 500);
1127 
1128  $events->registerHandler($name, $type, $callback, $priority);
1129  }
1130  }
1131  }
1132  }
1133  }
1134 
1140  protected function registerViewExtensions(): void {
1141  $views = _elgg_services()->views;
1142 
1143  $spec = (array) $this->getStaticConfig('view_extensions', []);
1144 
1145  foreach ($spec as $src_view => $extensions) {
1146  foreach ($extensions as $extention => $extention_spec) {
1147  if (!is_array($extention_spec)) {
1148  continue;
1149  }
1150 
1151  $unextend = (bool) elgg_extract('unextend', $extention_spec, false);
1152 
1153  if ($unextend) {
1154  $views->unextendView($src_view, $extention);
1155  } else {
1156  $priority = (int) elgg_extract('priority', $extention_spec, 501);
1157 
1158  $views->extendView($src_view, $extention, $priority);
1159  }
1160  }
1161  }
1162  }
1163 
1169  protected function registerGroupTools(): void {
1170  $tools = _elgg_services()->group_tools;
1171 
1172  $spec = (array) $this->getStaticConfig('group_tools', []);
1173 
1174  foreach ($spec as $tool_name => $tool_options) {
1175  if (!is_array($tool_options)) {
1176  continue;
1177  }
1178 
1179  $unregister = (bool) elgg_extract('unregister', $tool_options, false);
1180 
1181  if ($unregister) {
1182  $tools->unregister($tool_name);
1183  } else {
1184  $tools->register($tool_name, $tool_options);
1185  }
1186  }
1187  }
1188 
1194  protected function registerViewOptions(): void {
1195  $spec = (array) $this->getStaticConfig('view_options', []);
1196 
1197  foreach ($spec as $view_name => $options) {
1198  if (!is_array($options)) {
1199  continue;
1200  }
1201 
1202  if (isset($options['ajax'])) {
1203  if ($options['ajax'] === true) {
1204  _elgg_services()->ajax->registerView($view_name);
1205  } else {
1206  _elgg_services()->ajax->unregisterView($view_name);
1207  }
1208  }
1209 
1210  if (isset($options['simplecache']) && $options['simplecache'] === true) {
1211  _elgg_services()->views->registerCacheableView($view_name);
1212  }
1213  }
1214  }
1215 
1221  protected function registerNotifications(): void {
1222  $spec = (array) $this->getStaticConfig('notifications', []);
1223 
1224  foreach ($spec as $type => $subtypes) {
1225  foreach ($subtypes as $subtype => $actions) {
1226  foreach ($actions as $action => $callback) {
1227  if ($callback === false) {
1228  _elgg_services()->notifications->unregisterEvent($type, $subtype, [$action]);
1229  } elseif ($callback === true) {
1230  _elgg_services()->notifications->registerEvent($type, $subtype, [$action]);
1231  } else {
1232  _elgg_services()->notifications->registerEvent($type, $subtype, [$action], $callback);
1233  }
1234  }
1235  }
1236  }
1237  }
1238 
1246  public function __get($name) {
1247  // See if its in our base attribute
1248  if (array_key_exists($name, $this->attributes)) {
1249  return $this->attributes[$name];
1250  }
1251 
1252  // object title and description are stored as metadata
1253  if (in_array($name, ['title', 'description'])) {
1254  return parent::__get($name);
1255  }
1256 
1257  $result = $this->getSetting($name);
1258  if ($result !== null) {
1259  return $result;
1260  }
1261 
1262  $defaults = $this->getStaticConfig('settings', []);
1263 
1264  return elgg_extract($name, $defaults, $result);
1265  }
1266 
1275  public function __set($name, $value) {
1276  if (array_key_exists($name, $this->attributes)) {
1277  // Check that we're not trying to change the guid!
1278  if ((array_key_exists('guid', $this->attributes)) && ($name == 'guid')) {
1279  return;
1280  }
1281 
1282  $this->attributes[$name] = $value;
1283 
1284  return;
1285  }
1286 
1287  // object title and description are stored as metadata
1288  if (in_array($name, ['title', 'description'])) {
1289  parent::__set($name, $value);
1290 
1291  return;
1292  }
1293 
1294  // to make sure we trigger the correct events
1295  $this->setSetting($name, $value);
1296  }
1297 
1305  protected function setStatus(bool $active): bool {
1306  if (!$this->guid) {
1307  return false;
1308  }
1309 
1311  if ($active) {
1312  $result = _elgg_services()->relationshipsTable->add($this->guid, 'active_plugin', $site->guid);
1313  } else {
1314  $result = _elgg_services()->relationshipsTable->remove($this->guid, 'active_plugin', $site->guid);
1315  }
1316 
1317  if ($result) {
1318  $this->activated = $active;
1319  }
1320 
1321  $this->invalidateCache();
1322 
1323  return $result;
1324  }
1325 
1329  public function isCacheable(): bool {
1330  return true;
1331  }
1332 
1336  public function cache(bool $persist = true): void {
1337  _elgg_services()->plugins->cache($this);
1338 
1339  parent::cache($persist);
1340  }
1341 
1345  public function invalidateCache(): void {
1346 
1347  _elgg_services()->boot->clearCache();
1348  _elgg_services()->plugins->invalidateCache($this->getID());
1349 
1350  parent::invalidateCache();
1351  }
1352 
1360  protected function getComposer(): \Elgg\Plugin\Composer {
1361  if (isset($this->composer)) {
1362  return $this->composer;
1363  }
1364 
1365  $this->composer = new \Elgg\Plugin\Composer($this);
1366  return $this->composer;
1367  }
1368 
1376  public function meetsDependencies(): bool {
1377  try {
1378  $this->assertDependencies();
1379 
1380  return true;
1381  } catch (PluginException $e) {
1382  return false;
1383  }
1384  }
1385 
1394  public function assertDependencies(): void {
1395  $this->getComposer()->assertConflicts();
1396  $this->getComposer()->assertActivePluginConflicts();
1397  $this->getComposer()->assertRequiredPhpVersion();
1398  $this->getComposer()->assertRequiredPhpExtensions();
1399  $this->assertPluginDependencies();
1400  }
1401 
1410  protected function assertPluginDependencies(): void {
1411  foreach ($this->getDependencies() as $plugin_id => $plugin_dep) {
1412  $must_be_active = elgg_extract('must_be_active', $plugin_dep, true);
1413  $position = elgg_extract('position', $plugin_dep);
1414 
1415  $dependent_plugin = elgg_get_plugin_from_id($plugin_id);
1416 
1417  if ($must_be_active && (!$dependent_plugin instanceof \ElggPlugin || !$dependent_plugin->isActive())) {
1418  throw PluginException::factory([
1419  'message' => elgg_echo('PluginException:PluginMustBeActive', [$plugin_id]),
1420  'plugin' => $this,
1421  ]);
1422  }
1423 
1424  if ($dependent_plugin instanceof \ElggPlugin && $position && $dependent_plugin->isActive()) {
1425  if ($position == 'after' && ($this->getPriority() < $dependent_plugin->getPriority())) {
1426  throw PluginException::factory([
1427  'message' => elgg_echo('PluginException:PluginMustBeAfter', [$plugin_id]),
1428  'plugin' => $this,
1429  ]);
1430  } elseif ($position == 'before' && ($this->getPriority() > $dependent_plugin->getPriority())) {
1431  throw PluginException::factory([
1432  'message' => elgg_echo('PluginException:PluginMustBeBefore', [$plugin_id]),
1433  'plugin' => $this,
1434  ]);
1435  }
1436  }
1437  }
1438  }
1439 
1445  public function getVersion(): string {
1446  // composer version
1447  $version = $this->getComposer()->getConfiguration()->version();
1448  if (!elgg_is_empty($version)) {
1449  return $version;
1450  }
1451 
1452  // elgg-plugin version
1453  $plugin_config = $this->getStaticConfig('plugin', []);
1454  $version = elgg_extract('version', $plugin_config);
1455  if (!elgg_is_empty($version)) {
1456  return $version;
1457  }
1458 
1459  // bundled plugins use elgg version
1460  if (in_array($this->getID(), Plugins::BUNDLED_PLUGINS)) {
1461  return elgg_get_release();
1462  }
1463 
1464  return '0.1';
1465  }
1466 
1474  public function getCategories(): array {
1475  return $this->getComposer()->getCategories();
1476  }
1477 
1485  public function getLicense(): string {
1486  return $this->getComposer()->getLicense();
1487  }
1488 
1496  public function getDescription(): string {
1497  return (string) $this->getComposer()->getConfiguration()->description();
1498  }
1499 
1507  public function getRepositoryURL(): string {
1508  return (string) $this->getComposer()->getConfiguration()->support()->source();
1509  }
1510 
1518  public function getBugTrackerURL(): string {
1519  return (string) $this->getComposer()->getConfiguration()->support()->issues();
1520  }
1521 
1529  public function getWebsite(): string {
1530  return (string) $this->getComposer()->getConfiguration()->homepage();
1531  }
1532 
1540  public function getAuthors(): array {
1541  return (array) $this->getComposer()->getConfiguration()->authors();
1542  }
1543 
1551  public function getConflicts(): array {
1552  return $this->getComposer()->getConflicts();
1553  }
1554 }
$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:301
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
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
$defaults
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()
{}
if($owner->guid!=$page_owner->guid) $widgets
Definition: widgets.php:40
registerPublicServices()
Registers the plugin public services.
Definition: ElggPlugin.php:867
meetsDependencies()
Checks if dependencies are met.
$delete
$type
Definition: delete.php:21
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:963
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
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:256
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:135
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:484
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:97
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:981
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:117
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:22
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.
$filename
getAllMetadata()
Get all entity metadata.
Definition: ElggEntity.php:343
_elgg_services()
Get the global service provider.
Definition: elgglib.php:347
loadLanguages()
Loads the plugin&#39;s translations.
const ACCESS_PUBLIC
Definition: constants.php:14
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.