Elgg  Version 3.0
Plugins.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
5 use Closure;
9 use Elgg\Config;
10 use Elgg\Context;
11 use Elgg\Database;
13 use Elgg\Includer;
15 use Elgg\Loggable;
16 use Elgg\Profilable;
20 use ElggCache;
21 use ElggPlugin;
22 use ElggSession;
23 use ElggUser;
24 use Exception;
27 
36 class Plugins {
37 
38  use Profilable;
39  use Cacheable;
40  use Loggable;
41 
45  protected $boot_plugins;
46 
50  protected $provides_cache;
51 
55  protected $db;
56 
60  protected $session;
61 
65  protected $events;
66 
70  protected $translator;
71 
75  protected $views;
76 
81 
85  protected $config;
86 
90  protected $system_messages;
91 
95  protected $context;
96 
97 
112  public function __construct(
113  ElggCache $cache,
114  Database $db,
120  Config $config,
123  ) {
124  $this->cache = $cache;
125  $this->db = $db;
126  $this->session = $session;
127  $this->events = $events;
128  $this->translator = $translator;
129  $this->views = $views;
130  $this->private_settings_cache = $private_settings_cache;
131  $this->config = $config;
132  $this->system_messages = $system_messages;
133  $this->context = $context;
134  }
135 
141  public function getPath() {
142  $path = $this->config->plugins_path;
143  if (!$path) {
144  $path = Paths::project() . 'mod/';
145  }
146  return $path;
147  }
148 
157  public function setBootPlugins($plugins, $order_plugins = true) {
158  if (!is_array($plugins)) {
159  unset($this->boot_plugins);
160  return;
161  }
162 
163  // Always (re)set the boot_plugins. This makes sure that even if you have no plugins active this is known to the system.
164  $this->boot_plugins = [];
165 
166  if ($order_plugins) {
168  }
169 
170  foreach ($plugins as $plugin) {
171  if (!$plugin instanceof ElggPlugin) {
172  continue;
173  }
174 
175  $plugin_id = $plugin->getID();
176  if (!$plugin_id) {
177  continue;
178  }
179 
180  $plugin->registerLanguages();
181 
182  $this->boot_plugins[$plugin_id] = $plugin;
183  $this->cache->save($plugin_id, $plugin);
184  }
185  }
186 
191  public function clear() {
192  $this->cache->clear();
193  $this->invalidateProvidesCache();
194  }
195 
204  public function getDirsInDir($dir = null) {
205  if (!$dir) {
206  $dir = $this->getPath();
207  }
208 
209  if (!is_dir($dir)) {
210  return [];
211  }
212 
213  $handle = opendir($dir);
214  if ($handle === false) {
215  return [];
216  }
217 
218  $plugin_dirs = [];
219  while (($plugin_dir = readdir($handle)) !== false) {
220  // must be directory and not begin with a .
221  if (substr($plugin_dir, 0, 1) !== '.' && is_dir($dir . $plugin_dir)) {
222  $plugin_dirs[] = $plugin_dir;
223  }
224  }
225 
226  sort($plugin_dirs);
227 
228  return $plugin_dirs;
229  }
230 
241  public function generateEntities() {
242 
243  $mod_dir = $this->getPath();
244 
245  // ignore access in case this is called with no admin logged in - needed for creating plugins perhaps?
246  $old_ia = $this->session->setIgnoreAccess(true);
247 
248  // show hidden entities so that we can enable them if appropriate
249  $old_access = $this->session->setDisabledEntityVisibility(true);
250 
251  $known_plugins = $this->find('all');
252  if (empty($known_plugins)) {
253  $known_plugins = [];
254  }
255 
256  // map paths to indexes
257  $id_map = [];
258  foreach ($known_plugins as $i => $plugin) {
259  // if the ID is wrong, delete the plugin because we can never load it.
260  $id = $plugin->getID();
261  if (!$id) {
262  $plugin->delete();
263  unset($known_plugins[$i]);
264  continue;
265  }
266  $id_map[$plugin->getID()] = $i;
267  $plugin->cache();
268  }
269 
270  $physical_plugins = $this->getDirsInDir($mod_dir);
271  if (empty($physical_plugins)) {
272  $this->session->setIgnoreAccess($old_ia);
273  $this->session->setDisabledEntityVisibility($old_access);
274 
275  return false;
276  }
277 
278  // check real plugins against known ones
279  foreach ($physical_plugins as $plugin_id) {
280  // is this already in the db?
281  if (array_key_exists($plugin_id, $id_map)) {
282  $index = $id_map[$plugin_id];
283  $plugin = $known_plugins[$index];
284  // was this plugin deleted and its entity disabled?
285  if (!$plugin->isEnabled()) {
286  $plugin->enable();
287  $plugin->deactivate();
288  $plugin->setPriority('new');
289  }
290 
291  // remove from the list of plugins to disable
292  unset($known_plugins[$index]);
293  } else {
294  // create new plugin
295  // priority is forced to last in save() if not set.
296  $plugin = ElggPlugin::fromId($plugin_id);
297  $plugin->cache();
298  }
299  }
300 
301  // everything remaining in $known_plugins needs to be disabled
302  // because they are entities, but their dirs were removed.
303  // don't delete the entities because they hold settings.
304  $reindex = false;
305  foreach ($known_plugins as $plugin) {
306  if (!$plugin->isEnabled()) {
307  continue;
308  }
309 
310  $reindex = true;
311 
312  if ($plugin->isActive()) {
313  $plugin->deactivate();
314  }
315  // remove the priority.
316  $name = $this->namespacePrivateSetting('internal', 'priority');
317  $plugin->removePrivateSetting($name);
318 
319  $plugin->disable();
320  }
321 
322  if ($reindex) {
323  $this->reindexPriorities();
324  }
325 
326  $this->session->setIgnoreAccess($old_ia);
327  $this->session->setDisabledEntityVisibility($old_access);
328 
329  return true;
330  }
331 
339  public function cache(ElggPlugin $plugin) {
340  if (!$plugin->getID()) {
341  return;
342  }
343  $this->cache->save($plugin->getID(), $plugin);
344  }
345 
353  public function invalidateCache($plugin_id) {
354  try {
355  $this->cache->delete($plugin_id);
356  $this->invalidateProvidesCache();
357  } catch (\InvalidArgumentException $ex) {
358  // A plugin must have been deactivated due to missing folder
359  // without proper cleanup
361  }
362  }
363 
371  public function get($plugin_id) {
372  if (!$plugin_id) {
373  return null;
374  }
375 
376  $fallback = function () use ($plugin_id) {
378  'type' => 'object',
379  'subtype' => 'plugin',
380  'metadata_name_value_pairs' => [
381  'name' => 'title',
382  'value' => $plugin_id,
383  ],
384  'limit' => 1,
385  'distinct' => false,
386  ]);
387 
388  if ($plugins) {
389  return $plugins[0];
390  }
391 
392  return null;
393  };
394 
395  $plugin = $this->cache->load($plugin_id);
396  if (!isset($plugin)) {
397  $plugin = $fallback();
398  if ($plugin instanceof ElggPlugin) {
399  $plugin->cache();
400  }
401  }
402 
403  return $plugin;
404  }
405 
417  public function exists($id) {
418  return $this->get($id) instanceof ElggPlugin;
419  }
420 
427  public function getMaxPriority() {
428  $priority = $this->namespacePrivateSetting('internal', 'priority');
429 
430  $qb = Select::fromTable('entities', 'e');
431  $qb->select('MAX(CAST(ps.value AS unsigned)) as max')
432  ->join('e', 'private_settings', 'ps', 'e.guid = ps.entity_guid')
433  ->where($qb->compare('ps.name', '=', $priority, ELGG_VALUE_STRING))
434  ->andWhere($qb->compare('e.type', '=', 'object', ELGG_VALUE_STRING))
435  ->andWhere($qb->compare('e.subtype', '=', 'plugin', ELGG_VALUE_STRING));
436 
437  $data = $this->db->getDataRow($qb);
438  if (empty($data)) {
439  return 1;
440  }
441 
442  return max(1, (int) $data->max);
443  }
444 
452  public function isActive($plugin_id) {
453  if (isset($this->boot_plugins) && is_array($this->boot_plugins)) {
454  return array_key_exists($plugin_id, $this->boot_plugins);
455  }
456 
457  $plugin = $this->get($plugin_id);
458  if (!$plugin) {
459  return false;
460  }
461 
462  return check_entity_relationship($plugin->guid, 'active_plugin', 1) instanceof \ElggRelationship;
463  }
464 
474  public function build() {
475 
476  $plugins_path = $this->getPath();
477 
478  // temporary disable all plugins if there is a file called 'disabled' in the plugin dir
479  if (file_exists("$plugins_path/disabled")) {
480  if ($this->session->isAdminLoggedIn() && $this->context->contains('admin')) {
481  $this->system_messages->addSuccessMessage($this->translator->translate('plugins:disabled'));
482  }
483 
484  return false;
485  }
486 
487  $this->events->registerHandler('plugins_load', 'system', [$this, 'register']);
488  $this->events->registerHandler('plugins_boot:before', 'system', [$this, 'boot']);
489  $this->events->registerHandler('init', 'system', [$this, 'init']);
490  $this->events->registerHandler('ready', 'system', [$this, 'ready']);
491  $this->events->registerHandler('upgrade', 'system', [$this, 'upgrade']);
492  $this->events->registerHandler('shutdown', 'system', [$this, 'shutdown']);
493 
494  return true;
495  }
496 
504  public function register() {
505  $plugins = $this->find('active');
506  if (empty($plugins)) {
507  return;
508  }
509 
510  if ($this->timer) {
511  $this->timer->begin([__METHOD__]);
512  }
513 
514  foreach ($plugins as $plugin) {
515  try {
516  $plugin->register();
517  } catch (Exception $ex) {
518  $this->disable($plugin, $ex);
519  }
520  }
521 
522  $this->registerRoot();
523 
524  if ($this->timer) {
525  $this->timer->end([__METHOD__]);
526  }
527  }
528 
535  public function boot() {
536  $plugins = $this->find('active');
537  if (empty($plugins)) {
538  return;
539  }
540 
541  if ($this->timer) {
542  $this->timer->begin([__METHOD__]);
543  }
544 
545  foreach ($plugins as $plugin) {
546  try {
547  $setup = $plugin->boot();
548  if ($setup instanceof Closure) {
549  $setup();
550  }
551  } catch (Exception $ex) {
552  $this->disable($plugin, $ex);
553  }
554  }
555 
556  $this->bootRoot();
557 
558  if ($this->timer) {
559  $this->timer->end([__METHOD__]);
560  }
561  }
562 
567  protected function registerRoot() {
568  if (Paths::project() === Paths::elgg()) {
569  return;
570  }
571 
572  // Elgg is installed as a composer dep, so try to treat the root directory
573  // as a custom plugin that is always loaded last and can't be disabled...
574  if (!$this->config->system_cache_loaded) {
575  // configure view locations for the custom plugin (not Elgg core)
576  $viewsFile = Paths::project() . 'views.php';
577  if (is_file($viewsFile)) {
578  $viewsSpec = Includer::includeFile($viewsFile);
579  if (is_array($viewsSpec)) {
580  $this->views->mergeViewsSpec($viewsSpec);
581  }
582  }
583 
584  // find views for the custom plugin (not Elgg core)
585  $this->views->registerPluginViews(Paths::project());
586  }
587 
588  if (!$this->config->i18n_loaded_from_cache) {
589  $this->translator->registerTranslations(Paths::project() . 'languages');
590  }
591  }
596  protected function bootRoot() {
597  if (Paths::project() === Paths::elgg()) {
598  return;
599  }
600 
601  // This is root directory start.php
602  $root_start = Paths::project() . "start.php";
603  if (is_file($root_start)) {
604  $setup = Application::requireSetupFileOnce($root_start);
605  if ($setup instanceof \Closure) {
606  $setup();
607  }
608  }
609  }
610 
617  public function init() {
618  $plugins = $this->find('active');
619  if (empty($plugins)) {
620  return;
621  }
622 
623  if ($this->timer) {
624  $this->timer->begin([__METHOD__]);
625  }
626 
627  foreach ($plugins as $plugin) {
628  try {
629  $plugin->init();
630  } catch (Exception $ex) {
631  $this->disable($plugin, $ex);
632  }
633  }
634 
635  if ($this->timer) {
636  $this->timer->end([__METHOD__]);
637  }
638  }
639 
646  public function ready() {
647  $plugins = $this->find('active');
648  if (empty($plugins)) {
649  return;
650  }
651 
652  if ($this->timer) {
653  $this->timer->begin([__METHOD__]);
654  }
655 
656  foreach ($plugins as $plugin) {
657  try {
658  $plugin->getBootstrap()->ready();
659  } catch (Exception $ex) {
660  $this->disable($plugin, $ex);
661  }
662  }
663 
664  if ($this->timer) {
665  $this->timer->end([__METHOD__]);
666  }
667  }
668 
675  public function upgrade() {
676  $plugins = $this->find('active');
677  if (empty($plugins)) {
678  return;
679  }
680 
681  if ($this->timer) {
682  $this->timer->begin([__METHOD__]);
683  }
684 
685  foreach ($plugins as $plugin) {
686  try {
687  $plugin->getBootstrap()->upgrade();
688  } catch (Exception $ex) {
689  $this->disable($plugin, $ex);
690  }
691  }
692 
693  if ($this->timer) {
694  $this->timer->end([__METHOD__]);
695  }
696  }
697 
704  public function shutdown() {
705  $plugins = $this->find('active');
706  if (empty($plugins)) {
707  return;
708  }
709 
710  if ($this->timer) {
711  $this->timer->begin([__METHOD__]);
712  }
713 
714  foreach ($plugins as $plugin) {
715  try {
716  $plugin->getBootstrap()->shutdown();
717  } catch (Exception $ex) {
718  $this->disable($plugin, $ex);
719  }
720  }
721 
722  if ($this->timer) {
723  $this->timer->end([__METHOD__]);
724  }
725  }
726 
735  protected function disable(ElggPlugin $plugin, Exception $previous) {
736  $this->getLogger()->log(LogLevel::ERROR, $previous, [
737  'context' => [
738  'plugin' => $plugin,
739  ],
740  ]);
741 
742  $disable_plugins = $this->config->auto_disable_plugins;
743  if ($disable_plugins === null) {
744  $disable_plugins = true;
745  }
746 
747  if (!$disable_plugins) {
748  return;
749  }
750 
751  try {
752  $id = $plugin->getID();
753  $plugin->deactivate();
754 
755  $msg = $this->translator->translate(
756  'PluginException:CannotStart',
757  [$id, $plugin->guid, $previous->getMessage()]
758  );
759 
760  elgg_add_admin_notice("cannot_start $id", $msg);
761  } catch (\PluginException $ex) {
762  $this->getLogger()->log(LogLevel::ERROR, $ex, [
763  'context' => [
764  'plugin' => $plugin,
765  ],
766  ]);
767  }
768  }
769 
777  public function find($status = 'active') {
778  if (!$this->db || !$this->config->installed) {
779  return [];
780  }
781 
782  if ($status === 'active' && isset($this->boot_plugins)) {
783  // boot_plugins is an already ordered list of plugins
784  return array_values($this->boot_plugins);
785  }
786 
787  $volatile_data_name = null;
788  $site_guid = 1;
789 
790  // grab plugins
791  $options = [
792  'type' => 'object',
793  'subtype' => 'plugin',
794  'limit' => false,
795  // ORDER BY CAST(ps.value) is super slow. We custom sorting below.
796  'order_by' => false,
797  // preload private settings because private settings will probably be used, at least priority
798  'preload_private_settings' => true,
799  ];
800 
801  switch ($status) {
802  case 'active':
803  $options['relationship'] = 'active_plugin';
804  $options['relationship_guid'] = $site_guid;
805  $options['inverse_relationship'] = true;
806 
807  // shorten callstack
808  $volatile_data_name = 'select:value';
809  $options['select'] = ['ps.value'];
810  $options['private_setting_names'] = [
811  $this->namespacePrivateSetting('internal', 'priority'),
812  ];
813  break;
814 
815  case 'inactive':
816  $options['wheres'][] = function (QueryBuilder $qb, $main_alias) use ($site_guid) {
817  $subquery = $qb->subquery('entity_relationships', 'active_er');
818  $subquery->select('active_er.guid_one')
819  ->where($qb->compare('active_er.relationship', '=', 'active_plugin', ELGG_VALUE_STRING))
820  ->andWhere($qb->compare('active_er.guid_two', '=', $site_guid, ELGG_VALUE_GUID));
821 
822  return $qb->compare("{$main_alias}.guid", 'NOT IN', $subquery->getSQL());
823  };
824  break;
825 
826  case 'all':
827  default:
828  break;
829  }
830 
831  $old_ia = $this->session->setIgnoreAccess(true);
833  $this->session->setIgnoreAccess($old_ia);
834 
835  $result = $this->orderPluginsByPriority($plugins, $volatile_data_name);
836 
837  if ($status === 'active' && !isset($this->boot_plugins)) {
838  // populate local cache if for some reason this is not set yet
839  $this->setBootPlugins($result, false);
840  }
841 
842  return $result;
843  }
844 
853  protected function orderPluginsByPriority($plugins = [], $volatile_data_name = null) {
854  $priorities = [];
855  $sorted_plugins = [];
856 
857  foreach ($plugins as $plugin) {
858  $priority = null;
859  if (!empty($volatile_data_name)) {
860  $priority = $plugin->getVolatileData($volatile_data_name);
861  }
862 
863  if (!isset($priority)) {
864  $priority = $plugin->getPriority();
865  }
866 
867  $priorities[$plugin->guid] = (int) $priority;
868  $sorted_plugins[$plugin->guid] = $plugin;
869  }
870 
871  asort($priorities);
872 
873  return array_values(array_replace($priorities, $sorted_plugins));
874  }
875 
889  public function setPriorities(array $order) {
890  $name = $this->namespacePrivateSetting('internal', 'priority');
891 
892  $plugins = $this->find('any');
893  if (empty($plugins)) {
894  return false;
895  }
896 
897  // reindex to get standard counting. no need to increment by 10.
898  // though we do start with 1
899  $order = array_values($order);
900 
901  $missing_plugins = [];
902  /* @var ElggPlugin[] $missing_plugins */
903 
904  $priority = 0;
905  foreach ($plugins as $plugin) {
906  $plugin_id = $plugin->getID();
907 
908  if (!in_array($plugin_id, $order)) {
909  $missing_plugins[] = $plugin;
910  continue;
911  }
912 
913  $priority = array_search($plugin_id, $order) + 1;
914 
915  if (!$plugin->setPrivateSetting($name, $priority)) {
916  return false;
917  }
918  }
919 
920  // set the missing plugins' priorities
921  if (empty($missing_plugins)) {
922  return true;
923  }
924 
925  foreach ($missing_plugins as $plugin) {
926  $priority++;
927  if (!$plugin->setPrivateSetting($name, $priority)) {
928  return false;
929  }
930  }
931 
932  return true;
933  }
934 
940  public function reindexPriorities() {
941  return $this->setPriorities([]);
942  }
943 
958  public function namespacePrivateSetting($type, $name, $id = null) {
959  switch ($type) {
960  case 'user_setting':
961  if (!$id) {
962  throw new \InvalidArgumentException("You must pass the plugin id for user settings");
963  }
964  $name = ELGG_PLUGIN_USER_SETTING_PREFIX . "$id:$name";
965  break;
966 
967  case 'internal':
969  break;
970  }
971 
972  return $name;
973  }
974 
975 
993  public function getProvides($type = null, $name = null) {
994  if ($this->provides_cache === null) {
995  $active_plugins = $this->find('active');
996 
997  $provides = [];
998 
999  foreach ($active_plugins as $plugin) {
1000  $plugin_provides = [];
1001  $manifest = $plugin->getManifest();
1002  if ($manifest instanceof \ElggPluginManifest) {
1003  $plugin_provides = $plugin->getManifest()->getProvides();
1004  }
1005  if ($plugin_provides) {
1006  foreach ($plugin_provides as $provided) {
1007  $provides[$provided['type']][$provided['name']] = [
1008  'version' => $provided['version'],
1009  'provided_by' => $plugin->getID()
1010  ];
1011  }
1012  }
1013  }
1014 
1015  $this->provides_cache = $provides;
1016  }
1017 
1018  if ($type && $name) {
1019  if (isset($this->provides_cache[$type][$name])) {
1020  return $this->provides_cache[$type][$name];
1021  } else {
1022  return false;
1023  }
1024  } else if ($type) {
1025  if (isset($this->provides_cache[$type])) {
1026  return $this->provides_cache[$type];
1027  } else {
1028  return false;
1029  }
1030  }
1031 
1032  return $this->provides_cache;
1033  }
1034 
1040  public function invalidateProvidesCache() {
1041  $this->provides_cache = null;
1042 
1043  return true;
1044  }
1045 
1060  public function checkProvides($type, $name, $version = null, $comparison = 'ge') {
1061  $provided = $this->getProvides($type, $name);
1062  if (!$provided) {
1063  return [
1064  'status' => false,
1065  'value' => ''
1066  ];
1067  }
1068 
1069  if ($version) {
1070  $status = version_compare($provided['version'], $version, $comparison);
1071  } else {
1072  $status = true;
1073  }
1074 
1075  return [
1076  'status' => $status,
1077  'value' => $provided['version']
1078  ];
1079  }
1080 
1095  public function getDependencyStrings($dep) {
1097  $dep_system = elgg_extract('type', $dep);
1098  $info = elgg_extract('dep', $dep);
1099  $type = elgg_extract('type', $info);
1100 
1101  if (!$dep_system || !$info || !$type) {
1102  return false;
1103  }
1104 
1105  // rewrite some of these to be more readable
1106  $comparison = elgg_extract('comparison', $info);
1107  switch ($comparison) {
1108  case 'lt':
1109  $comparison = '<';
1110  break;
1111  case 'gt':
1112  $comparison = '>';
1113  break;
1114  case 'ge':
1115  $comparison = '>=';
1116  break;
1117  case 'le':
1118  $comparison = '<=';
1119  break;
1120  default:
1121  //keep $comparison value intact
1122  break;
1123  }
1124 
1125  /*
1126  'requires' 'plugin oauth_lib' <1.3 1.3 'downgrade'
1127  'requires' 'php setting bob' >3 3 'change it'
1128  'conflicts' 'php setting' >3 4 'change it'
1129  'conflicted''plugin profile' any 1.8 'disable profile'
1130  'provides' 'plugin oauth_lib' 1.3 -- --
1131  'priority' 'before blog' -- after 'move it'
1132  */
1133  $strings = [];
1134  $strings['type'] = $translator->translate('ElggPlugin:Dependencies:' . ucwords($dep_system));
1135 
1136  switch ($type) {
1137  case 'elgg_release':
1138  // 'Elgg Version'
1139  $strings['name'] = $translator->translate('ElggPlugin:Dependencies:Elgg');
1140  $strings['expected_value'] = "$comparison {$info['version']}";
1141  $strings['local_value'] = $dep['value'];
1142  $strings['comment'] = '';
1143  break;
1144 
1145  case 'php_version':
1146  // 'PHP version'
1147  $strings['name'] = $translator->translate('ElggPlugin:Dependencies:PhpVersion');
1148  $strings['expected_value'] = "$comparison {$info['version']}";
1149  $strings['local_value'] = $dep['value'];
1150  $strings['comment'] = '';
1151  break;
1152 
1153  case 'php_extension':
1154  // PHP Extension %s [version]
1155  $strings['name'] = $translator->translate('ElggPlugin:Dependencies:PhpExtension', [$info['name']]);
1156  if ($info['version']) {
1157  $strings['expected_value'] = "$comparison {$info['version']}";
1158  $strings['local_value'] = $dep['value'];
1159  } else {
1160  $strings['expected_value'] = '';
1161  $strings['local_value'] = '';
1162  }
1163  $strings['comment'] = '';
1164  break;
1165 
1166  case 'php_ini':
1167  $strings['name'] = $translator->translate('ElggPlugin:Dependencies:PhpIni', [$info['name']]);
1168  $strings['expected_value'] = "$comparison {$info['value']}";
1169  $strings['local_value'] = $dep['value'];
1170  $strings['comment'] = '';
1171  break;
1172 
1173  case 'plugin':
1174  $strings['name'] = $translator->translate('ElggPlugin:Dependencies:Plugin', [$info['name']]);
1175  $expected = $info['version'] ? "$comparison {$info['version']}" : $translator->translate('any');
1176  $strings['expected_value'] = $expected;
1177  $strings['local_value'] = $dep['value'] ? $dep['value'] : '--';
1178  $strings['comment'] = '';
1179  break;
1180 
1181  case 'priority':
1182  $expected_priority = ucwords($info['priority']);
1183  $real_priority = ucwords($dep['value']);
1184  $strings['name'] = $translator->translate('ElggPlugin:Dependencies:Priority');
1185  $strings['expected_value'] = $translator->translate("ElggPlugin:Dependencies:Priority:$expected_priority", [$info['plugin']]);
1186  $strings['local_value'] = $translator->translate("ElggPlugin:Dependencies:Priority:$real_priority", [$info['plugin']]);
1187  $strings['comment'] = '';
1188  break;
1189  }
1190 
1191  if ($dep['type'] == 'suggests') {
1192  if ($dep['status']) {
1193  $strings['comment'] = $translator->translate('ok');
1194  } else {
1195  $strings['comment'] = $translator->translate('ElggPlugin:Dependencies:Suggests:Unsatisfied');
1196  }
1197  } else {
1198  if ($dep['status']) {
1199  $strings['comment'] = $translator->translate('ok');
1200  } else {
1201  $strings['comment'] = $translator->translate('error');
1202  }
1203  }
1204 
1205  return $strings;
1206  }
1207 
1216  public function getAllSettings(ElggPlugin $plugin) {
1217  if (!$plugin->guid) {
1218  return [];
1219  }
1220 
1221  $values = $this->private_settings_cache->load($plugin->guid);
1222  if (isset($values)) {
1223  return $values;
1224  }
1225 
1226  $us_prefix = $this->namespacePrivateSetting('user_setting', '', $plugin->getID());
1227 
1228  // Get private settings for user
1229  $qb = Select::fromTable('private_settings');
1230  $qb->select('name')
1231  ->addSelect('value')
1232  ->where($qb->compare('name', 'not like', "$us_prefix%", ELGG_VALUE_STRING))
1233  ->andWhere($qb->compare('entity_guid', '=', $plugin->guid, ELGG_VALUE_GUID));
1234 
1235  $rows = $this->db->getData($qb);
1236 
1237  $settings = [];
1238 
1239  if (!empty($rows)) {
1240  foreach ($rows as $row) {
1241  $settings[$row->name] = $row->value;
1242  }
1243  }
1244 
1245  $this->private_settings_cache->save($plugin->guid, $settings);
1246 
1247  return $settings;
1248  }
1249 
1261 
1262  // send an empty name so we just get the first part of the namespace
1263  $prefix = $this->namespacePrivateSetting('user_setting', '', $plugin->getID());
1264 
1265  $qb = Select::fromTable('private_settings');
1266  $qb->select('name')
1267  ->addSelect('value')
1268  ->where($qb->compare('name', 'like', "{$prefix}%"));
1269 
1270  if ($user) {
1271  $qb->andWhere($qb->compare('entity_guid', '=', $user->guid, ELGG_VALUE_INTEGER));
1272  }
1273 
1274  $rows = $this->db->getData($qb);
1275 
1276  $settings = [];
1277 
1278  if (!empty($rows)) {
1279  foreach ($rows as $rows) {
1280  $name = substr($rows->name, strlen($prefix));
1281  $value = $rows->value;
1282 
1283  $settings[$name] = $value;
1284  }
1285  }
1286 
1287  return $settings;
1288  }
1289 
1301  public function setUserSetting($name, $value, $user_guid = 0, $plugin_id = null) {
1302  $plugin = $this->get($plugin_id);
1303  if (!$plugin) {
1304  return false;
1305  }
1306 
1307  return $plugin->setUserSetting($name, $value, (int) $user_guid);
1308  }
1309 
1320  public function unsetUserSetting($name, $user_guid = 0, $plugin_id = null) {
1321  $plugin = $this->get($plugin_id);
1322  if (!$plugin) {
1323  return false;
1324  }
1325 
1326  return $plugin->unsetUserSetting($name, (int) $user_guid);
1327  }
1328 
1340  public function getUserSetting($name, $user_guid = 0, $plugin_id = null, $default = null) {
1341  $plugin = $this->get($plugin_id);
1342  if (!$plugin) {
1343  return false;
1344  }
1345 
1346  return $plugin->getUserSetting($name, (int) $user_guid, $default);
1347  }
1348 
1359  public function setSetting($name, $value, $plugin_id) {
1360  $plugin = $this->get($plugin_id);
1361  if (!$plugin) {
1362  return false;
1363  }
1364 
1365  return $plugin->setSetting($name, $value);
1366  }
1367 
1378  public function getSetting($name, $plugin_id, $default = null) {
1379  $plugin = $this->get($plugin_id);
1380  if (!$plugin) {
1381  return false;
1382  }
1383 
1384  return $plugin->getSetting($name, $default);
1385  }
1386 
1396  public function unsetSetting($name, $plugin_id) {
1397  $plugin = $this->get($plugin_id);
1398  if (!$plugin) {
1399  return false;
1400  }
1401 
1402  return $plugin->unsetSetting($name);
1403  }
1404 
1413  public function unsetAllSettings($plugin_id) {
1414  $plugin = $this->get($plugin_id);
1415  if (!$plugin) {
1416  return false;
1417  }
1418 
1419  return $plugin->unsetAllSettings();
1420  }
1421 
1449  public function getEntitiesFromUserSettings(array $options = []) {
1450  $singulars = [
1451  'plugin_user_setting_name',
1452  'plugin_user_setting_value',
1453  'plugin_user_setting_name_value_pair'
1454  ];
1455 
1456  $options = LegacyQueryOptionsAdapter::normalizePluralOptions($options, $singulars);
1457 
1458  // rewrite plugin_user_setting_name_* to the right PS ones.
1459  $map = [
1460  'plugin_user_setting_names' => 'private_setting_names',
1461  'plugin_user_setting_values' => 'private_setting_values',
1462  'plugin_user_setting_name_value_pairs' => 'private_setting_name_value_pairs',
1463  'plugin_user_setting_name_value_pairs_operator' => 'private_setting_name_value_pairs_operator',
1464  ];
1465 
1466  foreach ($map as $plugin => $private) {
1467  if (!isset($options[$plugin])) {
1468  continue;
1469  }
1470 
1471  if (isset($options[$private])) {
1472  if (!is_array($options[$private])) {
1473  $options[$private] = [$options[$private]];
1474  }
1475 
1476  $options[$private] = array_merge($options[$private], $options[$plugin]);
1477  } else {
1478  $options[$private] = $options[$plugin];
1479  }
1480  }
1481 
1482  $prefix = $this->namespacePrivateSetting('user_setting', '', $options['plugin_id']);
1483  $options['private_setting_name_prefix'] = $prefix;
1484 
1485  return elgg_get_entities($options);
1486  }
1487 
1498 
1499  $old_priority = $plugin->getPriority() ? : 1;
1500 
1501  $name = $this->namespacePrivateSetting('internal', 'priority');
1502 
1503  if (!$plugin->setPrivateSetting($name, $priority)) {
1504  return false;
1505  }
1506 
1507  if (!$plugin->guid) {
1508  return false;
1509  }
1510 
1511  $qb = Update::table('private_settings');
1512  $qb->where($qb->compare('name', '=', $name, ELGG_VALUE_STRING))
1513  ->andWhere($qb->compare('entity_guid', '!=', $plugin->guid, ELGG_VALUE_INTEGER));
1514 
1515  if ($priority > $old_priority) {
1516  $qb->set('value', "CAST(value AS UNSIGNED) - 1");
1517  $qb->andWhere($qb->between('CAST(value AS UNSIGNED)', $old_priority, $priority, ELGG_VALUE_INTEGER));
1518  } else {
1519  $qb->set('value', "CAST(value AS UNSIGNED) + 1");
1520  $qb->andWhere($qb->between('CAST(value AS UNSIGNED)', $priority, $old_priority, ELGG_VALUE_INTEGER));
1521  }
1522 
1523  if (!$this->db->updateData($qb)) {
1524  return false;
1525  }
1526 
1527  return $priority;
1528  }
1529 }
static project()
Get the project root (where composer is installed) path with "/".
Definition: Paths.php:25
static includeFile($file)
Include a file with as little context as possible.
Definition: Includer.php:18
getID()
Returns the ID (dir name) of this plugin.
Definition: ElggPlugin.php:123
getSetting($name, $plugin_id, $default=null)
Get setting for a plugin.
Definition: Plugins.php:1378
$plugin
reindexPriorities()
Reindexes all plugin priorities starting at 1.
Definition: Plugins.php:940
if(!$user||!$user->canDelete()) $name
Definition: delete.php:22
static elgg()
Get the Elgg codebase path with "/".
Definition: Paths.php:44
getMaxPriority()
Returns the highest priority of the plugins.
Definition: Plugins.php:427
$rows
Definition: redis.php:20
getEntitiesFromUserSettings(array $options=[])
Returns entities based upon plugin user settings.
Definition: Plugins.php:1449
static table($table, $alias=null)
{}
Definition: Update.php:13
setPrivateSetting($name, $value)
Adds a private setting to this entity.
Definition: ElggEntity.php:589
build()
Registers lifecycle hooks for all active plugins sorted by their priority.
Definition: Plugins.php:474
elgg_add_admin_notice($id, $message)
Write a persistent message to the admin view.
Definition: admin.php:59
const ELGG_VALUE_INTEGER
Value types.
Definition: constants.php:138
trait Loggable
Enables adding a logger.
Definition: Loggable.php:12
setPriority(ElggPlugin $plugin, $priority)
Set plugin priority and adjust the priorities of other plugins.
Definition: Plugins.php:1497
$path
Definition: details.php:89
const ELGG_VALUE_GUID
Definition: constants.php:140
Events service.
if(elgg_trigger_plugin_hook('usersettings:save', 'user', $hooks_params, true)) foreach($request->validation() ->all() as $item) $data
Definition: save.php:57
Database abstraction query builder.
cache(ElggPlugin $plugin)
Cache a reference to this plugin by its ID.
Definition: Plugins.php:339
getAllUserSettings(ElggPlugin $plugin, ElggUser $user=null)
Returns an array of all plugin user settings for a user.
Definition: Plugins.php:1260
getDependencyStrings($dep)
Returns an array of parsed strings for a dependency in the format: array( &#39;type&#39; => requires...
Definition: Plugins.php:1095
$plugins
Definition: categories.php:3
$type
Definition: delete.php:21
unsetSetting($name, $plugin_id)
Unsets a plugin setting.
Definition: Plugins.php:1396
boot()
Boot the plugins.
Definition: Plugins.php:535
isActive($plugin_id)
Returns if a plugin is active for a current site.
Definition: Plugins.php:452
trait Profilable
Make an object accept a timer.
Definition: Profilable.php:10
find($status= 'active')
Returns an ordered list of plugins.
Definition: Plugins.php:777
getDirsInDir($dir=null)
Returns a list of plugin directory names from a base directory.
Definition: Plugins.php:204
$options
Elgg admin footer.
Definition: footer.php:6
PluginException.
setSetting($name, $value, $plugin_id)
Set a setting for a plugin.
Definition: Plugins.php:1359
getLogger()
Returns logger.
Definition: Loggable.php:34
$user_guid
Validate a user.
Definition: validate.php:6
registerRoot()
Register root level plugin views and translations.
Definition: Plugins.php:567
disable(ElggPlugin $plugin, Exception $previous)
Disable a plugin upon exception.
Definition: Plugins.php:735
const ELGG_PLUGIN_USER_SETTING_PREFIX
Prefix for plugin user setting names.
Definition: constants.php:126
$id
River item delete action.
Definition: delete.php:6
orderPluginsByPriority($plugins=[], $volatile_data_name=null)
Sorts plugins by priority.
Definition: Plugins.php:853
elgg_get_entities(array $options=[])
Fetches/counts entities or performs a calculation on their properties.
Definition: entities.php:545
bootRoot()
Boot root level custom plugin for starter-project installation.
Definition: Plugins.php:596
ready()
Run plugin ready handlers.
Definition: Plugins.php:646
init()
Initialize plugins.
Definition: Plugins.php:617
In memory cache of known private settings values stored by entity.
$plugin_id
Definition: save.php:15
generateEntities()
Discovers plugins in the plugins_path setting and creates entities for them if they don&#39;t exist...
Definition: Plugins.php:241
setBootPlugins($plugins, $order_plugins=true)
Set the list of active plugins according to the boot data cache.
Definition: Plugins.php:157
$user
Definition: ban.php:7
compare($x, $comparison, $y=null, $type=null, $case_sensitive=null)
Build value comparison clause.
elgg ElggUser
Definition: ElggUser.js:12
WARNING: API IN FLUX.
checkProvides($type, $name, $version=null, $comparison= 'ge')
Checks if a plugin is currently providing $type and $name, and optionally checking a version...
Definition: Plugins.php:1060
upgrade()
Run plugin upgrade handlers.
Definition: Plugins.php:675
if(!empty($screenshots)) $info
Definition: details.php:59
check_entity_relationship($guid_one, $relationship, $guid_two)
Check if a relationship exists between two entities.
__construct(ElggCache $cache, Database $db, ElggSession $session, EventsService $events, Translator $translator, ViewsService $views, PrivateSettingsCache $private_settings_cache, Config $config, SystemMessagesService $system_messages, Context $context)
Constructor.
Definition: Plugins.php:112
trait Cacheable
Utility trait for injecting cache.
Definition: Cacheable.php:11
clear()
Clear plugin caches.
Definition: Plugins.php:191
invalidateCache($plugin_id)
Remove plugin from cache.
Definition: Plugins.php:353
$default
Definition: checkbox.php:35
elgg_extract($key, $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:1131
unsetAllSettings($plugin_id)
Unsets all plugin settings for a plugin.
Definition: Plugins.php:1413
subquery($table, $alias=null)
Creates a new SelectQueryBuilder for join/where subqueries using the DB connection of the primary Que...
getAllSettings(ElggPlugin $plugin)
Get all settings (excluding user settings) for a plugin.
Definition: Plugins.php:1216
static fromTable($table, $alias=null)
{}
Definition: Select.php:13
deactivate()
Deactivates the plugin.
Definition: ElggPlugin.php:727
$value
Definition: debugging.php:7
const ELGG_VALUE_STRING
Definition: constants.php:139
System messages service.
getPath()
Get the plugin path for this installation, ending with slash.
Definition: Plugins.php:141
namespacePrivateSetting($type, $name, $id=null)
Namespaces a string to be used as a private setting name for a plugin.
Definition: Plugins.php:958
getPriority()
Gets the plugin&#39;s load priority.
Definition: ElggPlugin.php:239
setPriorities(array $order)
Reorder plugins to an order specified by the array.
Definition: Plugins.php:889
setUserSetting($name, $value, $user_guid=0, $plugin_id=null)
Set a user specific setting for a plugin.
Definition: Plugins.php:1301
getProvides($type=null, $name=null)
Returns an array of all provides from all active plugins.
Definition: Plugins.php:993
shutdown()
Run plugin shutdown handlers.
Definition: Plugins.php:704
elgg ajax ERROR
Definition: ajax.js:33
$index
Definition: gallery.php:47
Persistent, installation-wide key-value storage.
Definition: Plugins.php:36
static requireSetupFileOnce($file)
Require a library/plugin file once and capture returned anonymous functions.
const ELGG_PLUGIN_INTERNAL_PREFIX
Internal settings prefix.
Definition: constants.php:133
$settings
Definition: settings.php:3
getUserSetting($name, $user_guid=0, $plugin_id=null, $default=null)
Get a user specific setting for a plugin.
Definition: Plugins.php:1340
$version
Definition: version.php:14
static fromId($plugin_id, $path=null)
Load a plugin object from its ID Create a new plugin entity if doesn&#39;t exist.
Definition: ElggPlugin.php:65
exists($id)
Returns if a plugin exists in the system.
Definition: Plugins.php:417
elgg_flush_caches()
Flush all the registered caches.
Definition: cache.php:234
$priority
Manages a global stack of strings for sharing information about the current execution context...
Definition: Context.php:27
invalidateProvidesCache()
Deletes all cached data on plugins being provided.
Definition: Plugins.php:1040
unsetUserSetting($name, $user_guid=0, $plugin_id=null)
Unsets a user-specific plugin setting.
Definition: Plugins.php:1320