Elgg  Version master
ElggPlugin.php
Go to the documentation of this file.
1 <?php
2 
9 use Elgg\Includer;
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 
87  $plugin = elgg_get_plugin_from_id($plugin_id);
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 {
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 
293  public function getSetting(string $name, $default = null) {
294  $values = $this->getAllSettings();
295  return elgg_extract($name, $values, $default);
296  }
297 
305  public function getAllSettings(): array {
306 
307  try {
308  $defaults = [];
309  if ($this->isActive()) {
310  // only load settings from static config for active plugins to prevent issues
311  // with internal plugin references ie. classes and language keys
312  $defaults = $this->getStaticConfig('settings', []);
313  }
314 
315  $settings = $this->getAllMetadata();
316 
317  // title and description are not considered settings
318  unset($settings['title'], $settings['description']);
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->getAllSettings();
373 
374  foreach ($settings as $name => $value) {
375  if (str_starts_with($name, 'elgg:internal:')) {
376  continue;
377  }
378 
379  $this->unsetSetting($name);
380  }
381 
382  return true;
383  }
384 
391  public function unsetAllEntityAndPluginSettings(): bool {
392  // remove all plugin settings
393  $result = $this->unsetAllSettings();
394 
395  // entity plugin settings are stored with the entity
396  $delete = Delete::fromTable(MetadataTable::TABLE_NAME);
397  $delete->andWhere($delete->compare('name', 'like', "plugin:%_setting:{$this->getID()}:%", ELGG_VALUE_STRING));
398 
399  try {
400  _elgg_services()->db->deleteData($delete);
401  _elgg_services()->metadataCache->clear();
402 
403  $result &= true;
404  } catch (DatabaseException $e) {
405  elgg_log($e, \Psr\Log\LogLevel::ERROR);
406 
407  $result &= false;
408  }
409 
410  // trigger an event, so plugin devs can also remove settings
411  $params = [
412  'entity' => $this,
413  ];
414  return (bool) _elgg_services()->events->triggerResults('remove:settings', 'plugin', $params, $result);
415  }
416 
423  public function isValid(): bool {
424  try {
425  $this->assertValid();
426  return true;
427  } catch (PluginException $e) {
428  return false;
429  }
430  }
431 
440  public function assertValid(): void {
441  if (!$this->getID()) {
442  throw PluginException::factory([
443  'message' => elgg_echo('ElggPlugin:MissingID', [$this->guid]),
444  'plugin' => $this,
445  ]);
446  }
447 
448  $this->getComposer()->assertPluginId();
449 
450  if (file_exists($this->getPath() . 'start.php')) {
451  throw PluginException::factory([
452  'message' => elgg_echo('ElggPlugin:StartFound', [$this->getID()]),
453  'plugin' => $this,
454  ]);
455  }
456  }
457 
463  public function isActive(): bool {
464  if (isset($this->activated)) {
465  return $this->activated;
466  }
467 
468  $this->activated = elgg_is_active_plugin($this->getID());
469  return $this->activated;
470  }
471 
478  public function canActivate(): bool {
479  if ($this->isActive()) {
480  return false;
481  }
482 
483  try {
484  $this->assertCanActivate();
485  return true;
486  } catch (PluginException $e) {
487  return false;
488  }
489  }
490 
499  public function assertCanActivate(): void {
500  $this->assertValid();
501  $this->assertDependencies();
502  }
503 
504  // activating and deactivating
505 
512  public function activate(): bool {
513  if ($this->isActive()) {
514  return false;
515  }
516 
517  $this->assertCanActivate();
518 
519  // Check this before setting status because the file could potentially throw
520  $this->assertStaticConfigValid();
521 
522  if (!$this->setStatus(true)) {
523  return false;
524  }
525 
526  // perform tasks and emit events
527  // emit an event. returning false will make this not be activated.
528  // we need to do this after it's been fully activated
529  // or the deactivate will be confused.
530  $params = [
531  'plugin_id' => $this->getID(),
532  'plugin_entity' => $this,
533  ];
534 
535  $return = _elgg_services()->events->trigger('activate', 'plugin', $params);
536 
537  // if there are any on_enable functions, start the plugin now and run them
538  // Note: this will not run re-run the init events!
539  if ($return) {
540  try {
542 
543  $this->register();
544 
545  // directly load languages to have them available during runtime
546  $this->loadLanguages();
547 
548  $this->boot();
549 
550  $this->getBootstrap()->activate();
551 
552  $this->init();
553  } catch (PluginException $ex) {
554  elgg_log($ex, \Psr\Log\LogLevel::ERROR);
555 
556  $return = false;
557  }
558  }
559 
560  if ($return === false) {
561  $this->deactivate();
562  } else {
563  elgg_delete_admin_notice("cannot_start {$this->getID()}");
564 
566  _elgg_services()->logger->notice("Plugin {$this->getID()} has been activated");
567  }
568 
569  return $return;
570  }
571 
577  public function getDependencies(): array {
578  $plugin_config = $this->getStaticConfig('plugin', []);
579  return (array) elgg_extract('dependencies', $plugin_config, []);
580  }
581 
589  public function canDeactivate(): bool {
590  if (!$this->isActive()) {
591  return false;
592  }
593 
594  try {
595  $this->assertcanDeactivate();
596  return true;
597  } catch (PluginException $e) {
598  return false;
599  }
600  }
601 
610  public function assertCanDeactivate(): void {
611  $dependents = [];
612 
613  $active_plugins = elgg_get_plugins();
614 
615  foreach ($active_plugins as $plugin) {
616  $dependencies = $plugin->getDependencies();
617  if (!array_key_exists($this->getID(), $dependencies)) {
618  continue;
619  }
620 
621  if (elgg_extract('must_be_active', $dependencies[$this->getID()], true)) {
622  $dependents[$plugin->getID()] = $plugin;
623  }
624  }
625 
626  if (empty($dependents)) {
627  return;
628  }
629 
630  $list = array_map(function (\ElggPlugin $plugin) {
631  $css_id = preg_replace('/[^a-z0-9-]/i', '-', $plugin->getID());
632 
633  return elgg_view('output/url', [
634  'text' => $plugin->getDisplayName(),
635  'href' => "#{$css_id}",
636  ]);
637  }, $dependents);
638 
639  $list = implode(', ', $list);
640  throw PluginException::factory([
641  'message' => elgg_echo('ElggPlugin:Dependencies:ActiveDependent', [$this->getDisplayName(), $list]),
642  'plugin' => $this,
643  ]);
644  }
645 
653  public function deactivate(): bool {
654  if (!$this->isActive()) {
655  return false;
656  }
657 
658  $this->assertCanDeactivate();
659 
660  // emit an event. returning false will cause this to not be deactivated.
661  $params = [
662  'plugin_id' => $this->getID(),
663  'plugin_entity' => $this,
664  ];
665 
666  $return = _elgg_services()->events->trigger('deactivate', 'plugin', $params);
667  if ($return === false) {
668  return false;
669  }
670 
671  $this->getBootstrap()->deactivate();
672 
673  $this->deactivateEntities();
674 
676 
677  _elgg_services()->logger->notice("Plugin {$this->getID()} has been deactivated");
678 
679  return $this->setStatus(false);
680  }
681 
690  public function getBootstrap(): \Elgg\PluginBootstrapInterface {
691  $bootstrap = $this->getStaticConfig('bootstrap');
692  if ($bootstrap) {
693  if (!is_subclass_of($bootstrap, \Elgg\PluginBootstrapInterface::class)) {
694  throw PluginException::factory([
695  'message' => elgg_echo('LogicException:InterfaceNotImplemented', [
696  $bootstrap,
697  \Elgg\PluginBootstrapInterface::class
698  ]),
699  'plugin' => $this,
700  ]);
701  }
702 
703  return new $bootstrap($this, elgg());
704  }
705 
706  return new \Elgg\DefaultPluginBootstrap($this, elgg());
707  }
708 
715  public function autoload(): void {
716  $this->registerClasses();
717 
718  $autoload_file = 'vendor/autoload.php';
719  if (!$this->canReadFile($autoload_file)) {
720  return;
721  }
722 
723  $autoloader = Includer::requireFileOnce("{$this->getPath()}{$autoload_file}");
724 
725  if (!$autoloader instanceof \Composer\Autoload\ClassLoader) {
726  return;
727  }
728 
729  $autoloader->unregister();
730 
731  // plugins should be appended, composer defaults to prepend
732  $autoloader->register(false);
733  }
734 
743  public function register(): void {
744  $this->autoload();
745 
746  $this->registerPublicServices();
747  $this->activateEntities();
748  $this->registerLanguages();
749  $this->registerViews();
750 
751  $this->getBootstrap()->load();
752  }
753 
760  public function boot(): void {
761  $this->getBootstrap()->boot();
762  }
763 
770  public function init(): void {
771  $this->registerRoutes();
772  $this->registerActions();
773  $this->registerEntities();
774  $this->registerWidgets();
775  $this->registerEvents();
776  $this->registerViewExtensions();
777  $this->registerGroupTools();
778  $this->registerViewOptions();
779  $this->registerNotifications();
780 
781  $this->getBootstrap()->init();
782  }
783 
792  protected function includeFile(string $filename) {
793  $filepath = "{$this->getPath()}{$filename}";
794 
795  if (!$this->canReadFile($filename)) {
796  $msg = elgg_echo(
797  'ElggPlugin:Exception:CannotIncludeFile',
798  [$filename, $this->getID(), $this->guid, $this->getPath()]
799  );
800 
801  throw PluginException::factory([
802  'message' => $msg,
803  'plugin' => $this,
804  ]);
805  }
806 
807  try {
808  $ret = Includer::requireFile($filepath);
809  } catch (Exception $e) {
810  $msg = elgg_echo(
811  'ElggPlugin:Exception:IncludeFileThrew',
812  [$filename, $this->getID(), $this->guid, $this->getPath()]
813  );
814 
815  throw PluginException::factory([
816  'message' => $msg,
817  'previous' => $e,
818  'plugin' => $this,
819  ]);
820  }
821 
822  return $ret;
823  }
824 
832  protected function canReadFile(string $filename): bool {
833  return is_file("{$this->getPath()}{$filename}");
834  }
835 
842  protected function assertStaticConfigValid(): void {
843  if (!$this->canReadFile(self::STATIC_CONFIG_FILENAME)) {
844  return;
845  }
846 
847  ob_start();
848  $value = $this->includeFile(self::STATIC_CONFIG_FILENAME);
849  if (ob_get_clean() !== '') {
850  throw PluginException::factory([
851  'message' => elgg_echo('ElggPlugin:activate:ConfigSentOutput'),
852  'plugin' => $this,
853  ]);
854  }
855 
856  // make sure can serialize
857  $value = @unserialize(serialize($value));
858  if (!is_array($value)) {
859  throw PluginException::factory([
860  'message' => elgg_echo('ElggPlugin:activate:BadConfigFormat'),
861  'plugin' => $this,
862  ]);
863  }
864  }
865 
871  protected function registerPublicServices(): void {
872  $services_path = $this->getPath() . self::PUBLIC_SERVICES_FILENAME;
873  if (!is_file($services_path)) {
874  return;
875  }
876 
877  $services = Includer::includeFile($services_path);
878  foreach ($services as $name => $definition) {
879  elgg()->set($name, $definition);
880  }
881  }
882 
889  protected function registerViews(): void {
890  if (_elgg_services()->views->isViewLocationsLoadedFromCache()) {
891  return;
892  }
893 
894  $views = _elgg_services()->views;
895 
896  // Declared views first
897  $spec = $this->getStaticConfig('views');
898  if ($spec) {
899  $views->mergeViewsSpec($spec);
900  }
901 
902  // Allow /views directory files to override
903  if (!$views->registerViewsFromPath($this->getPath())) {
904  $msg = elgg_echo('ElggPlugin:Exception:CannotRegisterViews', [$this->getID(), $this->guid, $this->getPath()]);
905 
906  throw PluginException::factory([
907  'message' => $msg,
908  'plugin' => $this,
909  ]);
910  }
911  }
912 
918  protected function registerEntities(): void {
919  $spec = (array) $this->getStaticConfig('entities', []);
920 
921  foreach ($spec as $entity) {
922  if (!isset($entity['type']) || !isset($entity['subtype'])) {
923  continue;
924  }
925 
926  $capabilities = elgg_extract('capabilities', $entity, []);
927  foreach ($capabilities as $capability => $value) {
928  _elgg_services()->entity_capabilities->setCapability($entity['type'], $entity['subtype'], $capability, $value);
929  }
930  }
931  }
932 
938  protected function registerActions(): void {
939  $actions = _elgg_services()->actions;
940  $root_path = $this->getPath();
941 
942  $spec = (array) $this->getStaticConfig('actions', []);
943 
944  foreach ($spec as $action => $action_spec) {
945  if (!is_array($action_spec)) {
946  continue;
947  }
948 
949  $access = elgg_extract('access', $action_spec, 'logged_in');
950  $handler = elgg_extract('controller', $action_spec);
951  if (!$handler) {
952  $handler = elgg_extract('filename', $action_spec);
953  if (!$handler) {
954  $handler = "{$root_path}actions/{$action}.php";
955  }
956  }
957 
958  // unset handled action specs, pass the rest to the action service
959  unset($action_spec['access']);
960  unset($action_spec['controller']);
961  unset($action_spec['filename']);
962 
963  $actions->register($action, $handler, $access, $action_spec);
964  }
965  }
966 
972  protected function registerRoutes(): void {
973  $routes = _elgg_services()->routes;
974 
975  $spec = (array) $this->getStaticConfig('routes', []);
976  foreach ($spec as $name => $route_spec) {
977  if (!is_array($route_spec)) {
978  continue;
979  }
980 
981  $routes->register($name, $route_spec);
982  }
983  }
984 
990  protected function registerWidgets(): void {
991  $widgets = _elgg_services()->widgets;
992 
993  $spec = (array) $this->getStaticConfig('widgets', []);
994  foreach ($spec as $widget_id => $widget_definition) {
995  if (!is_array($widget_definition)) {
996  continue;
997  }
998 
999  if (!isset($widget_definition['id'])) {
1000  $widget_definition['id'] = $widget_id;
1001  }
1002 
1003  $definition = \Elgg\WidgetDefinition::factory($widget_definition);
1004 
1005  $widgets->registerType($definition);
1006  }
1007  }
1008 
1016  public function registerLanguages(): void {
1017  _elgg_services()->translator->registerLanguagePath($this->getLanguagesPath());
1018  }
1019 
1029  protected function loadLanguages(): void {
1030  $languages_path = $this->getLanguagesPath();
1031  if (!is_dir($languages_path)) {
1032  return;
1033  }
1034 
1035  _elgg_services()->translator->registerTranslations($languages_path);
1036  }
1037 
1043  protected function registerClasses(): void {
1044  _elgg_services()->autoloadManager->addClasses("{$this->getPath()}classes");
1045  }
1046 
1052  protected function activateEntities(): void {
1053  $spec = (array) $this->getStaticConfig('entities', []);
1054 
1055  foreach ($spec as $entity) {
1056  if (isset($entity['type'], $entity['subtype'], $entity['class'])) {
1057  _elgg_services()->entityTable->setEntityClass($entity['type'], $entity['subtype'], $entity['class']);
1058  }
1059  }
1060  }
1061 
1067  protected function deactivateEntities(): void {
1068  $spec = (array) $this->getStaticConfig('entities', []);
1069 
1070  foreach ($spec as $entity) {
1071  if (isset($entity['type'], $entity['subtype'], $entity['class'])) {
1072  _elgg_services()->entityTable->setEntityClass($entity['type'], $entity['subtype']);
1073  }
1074  }
1075  }
1076 
1082  protected function registerEvents(): void {
1083  $events = _elgg_services()->events;
1084 
1085  $spec = (array) $this->getStaticConfig('events', []);
1086 
1087  foreach ($spec as $name => $types) {
1088  foreach ($types as $type => $callbacks) {
1089  foreach ($callbacks as $callback => $event_spec) {
1090  if (!is_array($event_spec)) {
1091  continue;
1092  }
1093 
1094  $unregister = (bool) elgg_extract('unregister', $event_spec, false);
1095 
1096  if ($unregister) {
1097  $events->unregisterHandler($name, $type, $callback);
1098  } else {
1099  $priority = (int) elgg_extract('priority', $event_spec, 500);
1100 
1101  $events->registerHandler($name, $type, $callback, $priority);
1102  }
1103  }
1104  }
1105  }
1106  }
1107 
1113  protected function registerViewExtensions(): void {
1114  $views = _elgg_services()->views;
1115 
1116  $spec = (array) $this->getStaticConfig('view_extensions', []);
1117 
1118  foreach ($spec as $src_view => $extensions) {
1119  foreach ($extensions as $extention => $extention_spec) {
1120  if (!is_array($extention_spec)) {
1121  continue;
1122  }
1123 
1124  $unextend = (bool) elgg_extract('unextend', $extention_spec, false);
1125 
1126  if ($unextend) {
1127  $views->unextendView($src_view, $extention);
1128  } else {
1129  $priority = (int) elgg_extract('priority', $extention_spec, 501);
1130 
1131  $views->extendView($src_view, $extention, $priority);
1132  }
1133  }
1134  }
1135  }
1136 
1142  protected function registerGroupTools(): void {
1143  $tools = _elgg_services()->group_tools;
1144 
1145  $spec = (array) $this->getStaticConfig('group_tools', []);
1146 
1147  foreach ($spec as $tool_name => $tool_options) {
1148  if (!is_array($tool_options)) {
1149  continue;
1150  }
1151 
1152  $unregister = (bool) elgg_extract('unregister', $tool_options, false);
1153 
1154  if ($unregister) {
1155  $tools->unregister($tool_name);
1156  } else {
1157  $tools->register($tool_name, $tool_options);
1158  }
1159  }
1160  }
1161 
1167  protected function registerViewOptions(): void {
1168  $spec = (array) $this->getStaticConfig('view_options', []);
1169 
1170  foreach ($spec as $view_name => $options) {
1171  if (!is_array($options)) {
1172  continue;
1173  }
1174 
1175  if (isset($options['ajax'])) {
1176  if ($options['ajax'] === true) {
1177  _elgg_services()->ajax->registerView($view_name);
1178  } else {
1179  _elgg_services()->ajax->unregisterView($view_name);
1180  }
1181  }
1182 
1183  if (isset($options['simplecache']) && $options['simplecache'] === true) {
1184  _elgg_services()->simpleCache->registerCacheableView($view_name);
1185  }
1186  }
1187  }
1188 
1194  protected function registerNotifications(): void {
1195  $spec = (array) $this->getStaticConfig('notifications', []);
1196 
1197  foreach ($spec as $type => $subtypes) {
1198  foreach ($subtypes as $subtype => $actions) {
1199  foreach ($actions as $action => $callback) {
1200  if ($callback === false) {
1201  _elgg_services()->notifications->unregisterEvent($type, $subtype, [$action]);
1202  } elseif ($callback === true) {
1203  _elgg_services()->notifications->registerEvent($type, $subtype, [$action]);
1204  } else {
1205  _elgg_services()->notifications->registerEvent($type, $subtype, [$action], $callback);
1206  }
1207  }
1208  }
1209  }
1210  }
1211 
1219  public function __get($name) {
1220  // See if its in our base attribute
1221  if (array_key_exists($name, $this->attributes)) {
1222  return $this->attributes[$name];
1223  }
1224 
1225  // object title and description are stored as metadata
1226  if (in_array($name, ['title', 'description'])) {
1227  return parent::__get($name);
1228  }
1229 
1230  $result = $this->getSetting($name);
1231  if ($result !== null) {
1232  return $result;
1233  }
1234 
1235  $defaults = $this->getStaticConfig('settings', []);
1236 
1237  return elgg_extract($name, $defaults, $result);
1238  }
1239 
1248  public function __set($name, $value) {
1249  if (array_key_exists($name, $this->attributes)) {
1250  // Check that we're not trying to change the guid!
1251  if ((array_key_exists('guid', $this->attributes)) && ($name == 'guid')) {
1252  return;
1253  }
1254 
1255  $this->attributes[$name] = $value;
1256 
1257  return;
1258  }
1259 
1260  // object title and description are stored as metadata
1261  if (in_array($name, ['title', 'description'])) {
1262  parent::__set($name, $value);
1263 
1264  return;
1265  }
1266 
1267  // to make sure we trigger the correct events
1268  $this->setSetting($name, $value);
1269  }
1270 
1278  protected function setStatus(bool $active): bool {
1279  if (!$this->guid) {
1280  return false;
1281  }
1282 
1284  if ($active) {
1285  $result = $this->addRelationship($site->guid, 'active_plugin');
1286  } else {
1287  $result = $this->removeRelationship($site->guid, 'active_plugin');
1288  }
1289 
1290  if ($result) {
1291  $this->activated = $active;
1292  }
1293 
1294  $this->invalidateCache();
1295 
1296  return $result;
1297  }
1298 
1302  public function isCacheable(): bool {
1303  return true;
1304  }
1305 
1309  public function invalidateCache(): void {
1310  _elgg_services()->boot->clearCache();
1311  _elgg_services()->pluginsCache->delete($this->getID());
1312 
1313  parent::invalidateCache();
1314  }
1315 
1323  protected function getComposer(): \Elgg\Plugin\Composer {
1324  if (isset($this->composer)) {
1325  return $this->composer;
1326  }
1327 
1328  $this->composer = new \Elgg\Plugin\Composer($this);
1329  return $this->composer;
1330  }
1331 
1339  public function meetsDependencies(): bool {
1340  try {
1341  $this->assertDependencies();
1342 
1343  return true;
1344  } catch (PluginException $e) {
1345  return false;
1346  }
1347  }
1348 
1357  public function assertDependencies(): void {
1358  $this->getComposer()->assertConflicts();
1359  $this->getComposer()->assertActivePluginConflicts();
1360  $this->getComposer()->assertRequiredPhpVersion();
1361  $this->getComposer()->assertRequiredPhpExtensions();
1362  $this->assertPluginDependencies();
1363  }
1364 
1373  protected function assertPluginDependencies(): void {
1374  foreach ($this->getDependencies() as $plugin_id => $plugin_dep) {
1375  $must_be_active = elgg_extract('must_be_active', $plugin_dep, true);
1376  $position = elgg_extract('position', $plugin_dep);
1377 
1378  $dependent_plugin = elgg_get_plugin_from_id($plugin_id);
1379 
1380  if ($must_be_active && (!$dependent_plugin instanceof \ElggPlugin || !$dependent_plugin->isActive())) {
1381  throw PluginException::factory([
1382  'message' => elgg_echo('PluginException:PluginMustBeActive', [$plugin_id]),
1383  'plugin' => $this,
1384  ]);
1385  }
1386 
1387  if ($dependent_plugin instanceof \ElggPlugin && $position && $dependent_plugin->isActive()) {
1388  if ($position == 'after' && ($this->getPriority() < $dependent_plugin->getPriority())) {
1389  throw PluginException::factory([
1390  'message' => elgg_echo('PluginException:PluginMustBeAfter', [$plugin_id]),
1391  'plugin' => $this,
1392  ]);
1393  } elseif ($position == 'before' && ($this->getPriority() > $dependent_plugin->getPriority())) {
1394  throw PluginException::factory([
1395  'message' => elgg_echo('PluginException:PluginMustBeBefore', [$plugin_id]),
1396  'plugin' => $this,
1397  ]);
1398  }
1399  }
1400  }
1401  }
1402 
1408  public function getVersion(): string {
1409  // composer version
1410  $version = $this->getComposer()->getConfiguration()->version();
1411  if (!elgg_is_empty($version)) {
1412  return $version;
1413  }
1414 
1415  // elgg-plugin version
1416  $plugin_config = $this->getStaticConfig('plugin', []);
1417  $version = elgg_extract('version', $plugin_config);
1418  if (!elgg_is_empty($version)) {
1419  return $version;
1420  }
1421 
1422  // bundled plugins use elgg version
1423  if (in_array($this->getID(), Plugins::BUNDLED_PLUGINS)) {
1424  return elgg_get_release();
1425  }
1426 
1427  return '0.1';
1428  }
1429 
1437  public function getCategories(): array {
1438  return $this->getComposer()->getCategories();
1439  }
1440 
1448  public function getLicense(): string {
1449  return $this->getComposer()->getLicense();
1450  }
1451 
1459  public function getDescription(): string {
1460  return (string) $this->getComposer()->getConfiguration()->description();
1461  }
1462 
1470  public function getRepositoryURL(): string {
1471  return (string) $this->getComposer()->getConfiguration()->support()->source();
1472  }
1473 
1481  public function getBugTrackerURL(): string {
1482  return (string) $this->getComposer()->getConfiguration()->support()->issues();
1483  }
1484 
1492  public function getWebsite(): string {
1493  return (string) $this->getComposer()->getConfiguration()->homepage();
1494  }
1495 
1503  public function getAuthors(): array {
1504  return (array) $this->getComposer()->getConfiguration()->authors();
1505  }
1506 
1514  public function getConflicts(): array {
1515  return $this->getComposer()->getConflicts();
1516  }
1517 }
$default
Definition: checkbox.php:30
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:304
elgg
Definition: install.js:27
registerViews()
Registers the plugin&#39;s views.
Definition: ElggPlugin.php:889
const ELGG_DISABLE_SYSTEM_LOG
Definition: constants.php:125
registerEntities()
Registers the plugin&#39;s entities.
Definition: ElggPlugin.php:918
getID()
Returns the ID (dir name) of this plugin.
Definition: ElggPlugin.php:139
elgg_get_release()
Get the current Elgg release.
elgg_get_plugins(string $status= 'active')
Returns an ordered list of plugins.
Definition: plugins.php:39
$plugin
registerActions()
Registers the plugin&#39;s actions provided in the plugin config file.
Definition: ElggPlugin.php:938
$params
Saves global plugin settings.
Definition: save.php:13
$position
Definition: add.php:11
getLanguagesPath()
Returns the plugin&#39;s languages directory full path with trailing slash.
Definition: ElggPlugin.php:190
registerClasses()
Registers the plugin&#39;s classes.
assertCanDeactivate()
Asserts if a plugin can be deactivated.
Definition: ElggPlugin.php:610
Plugin class containing helper functions for plugin activation/deactivation, dependency checking capa...
Definition: ElggPlugin.php:17
const STATIC_CONFIG_FILENAME
Definition: ElggPlugin.php:20
$defaults
Generic entity header upload helper.
Definition: header.php:6
setSetting(string $name, $value)
Set a plugin setting for the plugin.
Definition: ElggPlugin.php:334
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
if(empty($page_owner)||$owner->guid!==$page_owner->guid) $widgets
Definition: widgets.php:40
getAllSettings()
Returns an array of all settings saved for this plugin.
Definition: ElggPlugin.php:305
$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:440
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:871
meetsDependencies()
Checks if dependencies are met.
$delete
$type
Definition: delete.php:21
deleteMetadata(string $name=null)
Deletes all metadata on this object (metadata.entity_guid = $this->guid).
Definition: Metadata.php:190
unsetSetting(string $name)
Removes a plugin setting name and value.
Definition: ElggPlugin.php:361
getDescription()
Return the description.
registerRoutes()
Registers the plugin&#39;s routes provided in the plugin config file.
Definition: ElggPlugin.php:972
setPriority($priority)
Sets the priority of the plugin Returns the new priority or false on error.
Definition: ElggPlugin.php:246
$site
Definition: icons.php:5
isCacheable()
{}
activateEntities()
Activates the plugin&#39;s entities.
if($type!= 'user') $settings
Definition: save.php:16
getAllMetadata()
Get all entity metadata.
Definition: Metadata.php:37
assertCanActivate()
Asserts if a plugin can activate.
Definition: ElggPlugin.php:499
const PUBLIC_SERVICES_FILENAME
Definition: ElggPlugin.php:21
if($item instanceof\ElggEntity) elseif($item instanceof\ElggRiverItem) elseif($item instanceof\ElggRelationship) elseif(is_callable([$item, 'getType']))
Definition: item.php:48
$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:589
elgg_invalidate_caches()
Invalidate all the registered caches.
Definition: cache.php:88
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:577
const ADDITIONAL_TEXT_FILES
Definition: ElggPlugin.php:30
if($who_can_change_language=== 'nobody') elseif($who_can_change_language=== 'admin_only'&&!elgg_is_admin_logged_in()) $options
Definition: language.php:20
getDisplayName()
Returns the name from elgg-plugin.php if available, otherwise a nicely formatted ID.
Definition: ElggPlugin.php:149
assertStaticConfigValid()
If a static config file is present, is it a serializable array?
Definition: ElggPlugin.php:842
setPath(string $path)
Set path.
Definition: ElggPlugin.php:166
const ELGG_IGNORE_ACCESS
elgg_call() flags
Definition: constants.php:121
elgg_view(string $view, array $vars=[], string $viewtype= '')
Return a parsed view.
Definition: views.php:156
assertDependencies()
Assert plugin dependencies.
isActive()
Is this plugin active?
Definition: ElggPlugin.php:463
$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:27
$active
Definition: full.php:20
setStatus(bool $active)
Sets the plugin to active or inactive.
init()
Init the plugin.
Definition: ElggPlugin.php:770
elgg_log($message, $level=\Psr\Log\LogLevel::NOTICE)
Log a message.
Definition: elgglib.php:86
initializeAttributes()
{}
Definition: ElggPlugin.php:66
A generic parent class for database exceptions.
normalizePriority($priority)
Normalize and validate new priority.
Definition: ElggPlugin.php:260
unsetAllSettings()
Removes all settings for this plugin.
Definition: ElggPlugin.php:371
getBugTrackerURL()
Returns the bug tracker page.
if(!$menu instanceof\Elgg\Menu\PreparedMenu) $actions
Definition: user_hover.php:16
setMetadata(string $name, mixed $value, string $value_type= '', bool $multiple=false)
Set metadata on this entity.
Definition: Metadata.php:78
getWebsite()
Return the website.
getBootstrap()
Bootstrap object.
Definition: ElggPlugin.php:690
isValid()
Returns if the plugin is complete, meaning has all required files and Elgg can read them and they mak...
Definition: ElggPlugin.php:423
elgg_get_site_entity()
Get the current site entity.
Definition: entities.php:101
const PRIORITY_SETTING_NAME
Definition: ElggPlugin.php:19
$extensions
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:512
registerWidgets()
Registers the plugin&#39;s widgets provided in the plugin config file.
Definition: ElggPlugin.php:990
boot()
Boot the plugin.
Definition: ElggPlugin.php:760
deactivate()
Deactivates the plugin.
Definition: ElggPlugin.php:653
__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:832
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:226
__set($name, $value)
Set a value as attribute or setting.
_elgg_services()
Get the global service provider.
Definition: elgglib.php:351
loadLanguages()
Loads the plugin&#39;s translations.
const ACCESS_PUBLIC
Definition: constants.php:12
getMetadata(string $name)
Return the value of a piece of metadata.
Definition: Metadata.php:27
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:82
getSetting(string $name, $default=null)
Returns a plugin setting.
Definition: ElggPlugin.php:293
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:478
includeFile(string $filename)
Includes one of the plugins files.
Definition: ElggPlugin.php:792
getPath()
Returns the plugin&#39;s full path with trailing slash.
Definition: ElggPlugin.php:175
getLicense()
Returns the license.
assertPluginDependencies()
Assert required plugins or plugin position.
autoload()
Register plugin classes and require composer autoloader.
Definition: ElggPlugin.php:715
unsetAllEntityAndPluginSettings()
Remove all entity and plugin settings for this plugin.
Definition: ElggPlugin.php:391
$priority
$views
Definition: item.php:17
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:205
$guid
Reset an ElggUpgrade.
Definition: reset.php:6
getAuthors()
Returns an array of authors.