These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Elgg\Database; |
||
3 | |||
4 | use Elgg\Cache\Pool; |
||
5 | use Elgg\Profilable; |
||
6 | use Exception; |
||
7 | use Elgg\Cache\PluginSettingsCache; |
||
8 | |||
9 | /** |
||
10 | * Persistent, installation-wide key-value storage. |
||
11 | * |
||
12 | * WARNING: API IN FLUX. DO NOT USE DIRECTLY. |
||
13 | * |
||
14 | * @access private |
||
15 | * |
||
16 | * @since 1.10.0 |
||
17 | */ |
||
18 | class Plugins { |
||
19 | use Profilable; |
||
20 | |||
21 | /** |
||
22 | * @var \ElggPlugin[] |
||
23 | */ |
||
24 | private $boot_plugins = []; |
||
25 | |||
26 | /** |
||
27 | * @var array|null |
||
28 | */ |
||
29 | private $provides_cache; |
||
30 | |||
31 | /** |
||
32 | * @var string[] Active plugins, with plugin ID => GUID. Missing keys imply inactive plugins. |
||
33 | */ |
||
34 | private $active_guids = []; |
||
35 | |||
36 | /** |
||
37 | * @var bool Has $active_guids been populated? |
||
38 | */ |
||
39 | private $active_guids_known = false; |
||
40 | |||
41 | /** |
||
42 | * @var Pool |
||
43 | */ |
||
44 | private $plugins_by_id; |
||
45 | |||
46 | /** |
||
47 | * @var PluginSettingsCache |
||
48 | */ |
||
49 | private $settings_cache; |
||
50 | |||
51 | /** |
||
52 | * Constructor |
||
53 | * |
||
54 | * @param Pool $pool Cache for referencing plugins by ID |
||
55 | * @param PluginSettingsCache $cache Plugin settings cache |
||
56 | */ |
||
57 | 8 | public function __construct(Pool $pool, PluginSettingsCache $cache) { |
|
58 | 8 | $this->plugins_by_id = $pool; |
|
59 | 8 | $this->settings_cache = $cache; |
|
60 | 8 | } |
|
61 | |||
62 | /** |
||
63 | * Set the list of active plugins according to the boot data cache |
||
64 | * |
||
65 | * @param \ElggPlugin[] $plugins Set of active plugins |
||
66 | * @return void |
||
67 | */ |
||
68 | public function setBootPlugins(array $plugins) { |
||
69 | $this->boot_plugins = $plugins; |
||
70 | foreach ($plugins as $plugin) { |
||
71 | $this->plugins_by_id->put($plugin->getID(), $plugin); |
||
72 | } |
||
73 | } |
||
74 | |||
75 | /** |
||
76 | * Returns a list of plugin directory names from a base directory. |
||
77 | * |
||
78 | * @param string $dir A dir to scan for plugins. Defaults to config's plugins_path. |
||
79 | * Must have a trailing slash. |
||
80 | * |
||
81 | * @return array Array of directory names (not full paths) |
||
82 | * @access private |
||
83 | */ |
||
84 | function getDirsInDir($dir = null) { |
||
85 | if (!$dir) { |
||
86 | $dir = elgg_get_plugins_path(); |
||
87 | } |
||
88 | |||
89 | $plugin_dirs = []; |
||
90 | $handle = opendir($dir); |
||
91 | |||
92 | if ($handle) { |
||
93 | while ($plugin_dir = readdir($handle)) { |
||
94 | // must be directory and not begin with a . |
||
95 | if (substr($plugin_dir, 0, 1) !== '.' && is_dir($dir . $plugin_dir)) { |
||
96 | $plugin_dirs[] = $plugin_dir; |
||
97 | } |
||
98 | } |
||
99 | } |
||
100 | |||
101 | sort($plugin_dirs); |
||
102 | |||
103 | return $plugin_dirs; |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Discovers plugins in the plugins_path setting and creates \ElggPlugin |
||
108 | * entities for them if they don't exist. If there are plugins with entities |
||
109 | * but not actual files, will disable the \ElggPlugin entities and mark as inactive. |
||
110 | * The \ElggPlugin object holds config data, so don't delete. |
||
111 | * |
||
112 | * @return bool |
||
113 | * @access private |
||
114 | */ |
||
115 | function generateEntities() { |
||
116 | |||
117 | $mod_dir = elgg_get_plugins_path(); |
||
118 | $db_prefix = _elgg_config()->dbprefix; |
||
119 | |||
120 | // ignore access in case this is called with no admin logged in - needed for creating plugins perhaps? |
||
121 | $old_ia = elgg_set_ignore_access(true); |
||
122 | |||
123 | // show hidden entities so that we can enable them if appropriate |
||
124 | $old_access = access_show_hidden_entities(true); |
||
125 | |||
126 | $known_plugins = elgg_get_entities_from_relationship([ |
||
127 | 'type' => 'object', |
||
128 | 'subtype' => 'plugin', |
||
129 | 'selects' => ['plugin_oe.*'], |
||
130 | 'joins' => ["JOIN {$db_prefix}objects_entity plugin_oe on plugin_oe.guid = e.guid"], |
||
131 | 'limit' => ELGG_ENTITIES_NO_VALUE, |
||
132 | ]); |
||
133 | /* @var \ElggPlugin[] $known_plugins */ |
||
134 | |||
135 | if (!$known_plugins) { |
||
136 | $known_plugins = []; |
||
137 | } |
||
138 | |||
139 | // map paths to indexes |
||
140 | $id_map = []; |
||
141 | foreach ($known_plugins as $i => $plugin) { |
||
142 | // if the ID is wrong, delete the plugin because we can never load it. |
||
143 | $id = $plugin->getID(); |
||
144 | if (!$id) { |
||
145 | $plugin->delete(); |
||
146 | unset($known_plugins[$i]); |
||
147 | continue; |
||
148 | } |
||
149 | $id_map[$plugin->getID()] = $i; |
||
150 | } |
||
151 | |||
152 | $physical_plugins = $this->getDirsInDir($mod_dir); |
||
153 | if (!$physical_plugins) { |
||
154 | return false; |
||
155 | } |
||
156 | |||
157 | // check real plugins against known ones |
||
158 | foreach ($physical_plugins as $plugin_id) { |
||
159 | // is this already in the db? |
||
160 | if (array_key_exists($plugin_id, $id_map)) { |
||
161 | $index = $id_map[$plugin_id]; |
||
162 | $plugin = $known_plugins[$index]; |
||
163 | // was this plugin deleted and its entity disabled? |
||
164 | if (!$plugin->isEnabled()) { |
||
165 | $plugin->enable(); |
||
166 | $plugin->deactivate(); |
||
167 | $plugin->setPriority('last'); |
||
168 | } |
||
169 | |||
170 | // remove from the list of plugins to disable |
||
171 | unset($known_plugins[$index]); |
||
172 | } else { |
||
173 | // create new plugin |
||
174 | // priority is forced to last in save() if not set. |
||
175 | $plugin = new \ElggPlugin($mod_dir . $plugin_id); |
||
176 | $plugin->save(); |
||
177 | } |
||
178 | } |
||
179 | |||
180 | // everything remaining in $known_plugins needs to be disabled |
||
181 | // because they are entities, but their dirs were removed. |
||
182 | // don't delete the entities because they hold settings. |
||
183 | foreach ($known_plugins as $plugin) { |
||
184 | if ($plugin->isActive()) { |
||
185 | $plugin->deactivate(); |
||
186 | } |
||
187 | // remove the priority. |
||
188 | $name = $this->namespacePrivateSetting('internal', 'priority'); |
||
189 | remove_private_setting($plugin->guid, $name); |
||
190 | if ($plugin->isEnabled()) { |
||
191 | $plugin->disable(); |
||
192 | } |
||
193 | } |
||
194 | |||
195 | access_show_hidden_entities($old_access); |
||
196 | elgg_set_ignore_access($old_ia); |
||
197 | |||
198 | return true; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Cache a reference to this plugin by its ID |
||
203 | * |
||
204 | * @param \ElggPlugin $plugin |
||
205 | * |
||
206 | * @access private |
||
207 | */ |
||
208 | function cache(\ElggPlugin $plugin) { |
||
209 | $this->plugins_by_id->put($plugin->getID(), $plugin); |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * Returns an \ElggPlugin object with the path $path. |
||
214 | * |
||
215 | * @param string $plugin_id The id (dir name) of the plugin. NOT the guid. |
||
216 | * @return \ElggPlugin|null |
||
217 | */ |
||
218 | function get($plugin_id) { |
||
219 | return $this->plugins_by_id->get($plugin_id, function () use ($plugin_id) { |
||
220 | $plugin_id = sanitize_string($plugin_id); |
||
221 | $db_prefix = _elgg_config()->dbprefix; |
||
222 | |||
223 | $options = [ |
||
224 | 'type' => 'object', |
||
225 | 'subtype' => 'plugin', |
||
226 | 'joins' => ["JOIN {$db_prefix}objects_entity oe on oe.guid = e.guid"], |
||
227 | 'selects' => ["oe.title", "oe.description"], |
||
228 | 'wheres' => ["oe.title = '$plugin_id'"], |
||
229 | 'limit' => 1, |
||
230 | 'distinct' => false, |
||
231 | ]; |
||
232 | |||
233 | $plugins = elgg_get_entities($options); |
||
234 | |||
235 | if ($plugins) { |
||
236 | return $plugins[0]; |
||
237 | } |
||
238 | |||
239 | return null; |
||
240 | }); |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * Returns if a plugin exists in the system. |
||
245 | * |
||
246 | * @warning This checks only plugins that are registered in the system! |
||
247 | * If the plugin cache is outdated, be sure to regenerate it with |
||
248 | * {@link _elgg_generate_plugin_objects()} first. |
||
249 | * |
||
250 | * @param string $id The plugin ID. |
||
251 | * @return bool |
||
252 | */ |
||
253 | function exists($id) { |
||
254 | return (bool) $this->get($id); |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * Returns the highest priority of the plugins |
||
259 | * |
||
260 | * @return int |
||
261 | * @access private |
||
262 | */ |
||
263 | function getMaxPriority() { |
||
264 | $db_prefix = _elgg_config()->dbprefix; |
||
265 | $priority = $this->namespacePrivateSetting('internal', 'priority'); |
||
266 | $plugin_subtype = get_subtype_id('object', 'plugin'); |
||
267 | |||
268 | $q = "SELECT MAX(CAST(ps.value AS unsigned)) as max |
||
269 | FROM {$db_prefix}entities e, {$db_prefix}private_settings ps |
||
270 | WHERE ps.name = '$priority' |
||
271 | AND ps.entity_guid = e.guid |
||
272 | AND e.type = 'object' and e.subtype = $plugin_subtype"; |
||
273 | |||
274 | $data = get_data($q); |
||
275 | if ($data) { |
||
276 | $max = $data[0]->max; |
||
277 | } else { |
||
278 | $max = 1; |
||
279 | } |
||
280 | |||
281 | // can't have a priority of 0. |
||
282 | return ($max) ? $max : 1; |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * Returns if a plugin is active for a current site. |
||
287 | * |
||
288 | * @param string $plugin_id The plugin ID |
||
289 | * @return bool |
||
290 | */ |
||
291 | function isActive($plugin_id) { |
||
292 | if ($this->active_guids_known) { |
||
293 | return isset($this->active_guids[$plugin_id]); |
||
294 | } |
||
295 | |||
296 | $site = elgg_get_site_entity(); |
||
297 | |||
298 | if (!($site instanceof \ElggSite)) { |
||
299 | return false; |
||
300 | } |
||
301 | |||
302 | $plugin = $this->get($plugin_id); |
||
303 | |||
304 | if (!$plugin) { |
||
305 | return false; |
||
306 | } |
||
307 | |||
308 | return $plugin->isActive($site->guid); |
||
309 | } |
||
310 | |||
311 | /** |
||
312 | * Loads all active plugins in the order specified in the tool admin panel. |
||
313 | * |
||
314 | * @note This is called on every page load. If a plugin is active and problematic, it |
||
315 | * will be disabled and a visible error emitted. This does not check the deps system because |
||
316 | * that was too slow. |
||
317 | * |
||
318 | * @return bool |
||
319 | * @access private |
||
320 | */ |
||
321 | function load() { |
||
322 | if ($this->timer) { |
||
323 | $this->timer->begin([__METHOD__]); |
||
324 | } |
||
325 | |||
326 | $plugins_path = elgg_get_plugins_path(); |
||
327 | $start_flags = ELGG_PLUGIN_INCLUDE_START | |
||
328 | ELGG_PLUGIN_REGISTER_VIEWS | |
||
329 | ELGG_PLUGIN_REGISTER_ACTIONS | |
||
330 | ELGG_PLUGIN_REGISTER_LANGUAGES | |
||
331 | ELGG_PLUGIN_REGISTER_WIDGETS | |
||
332 | ELGG_PLUGIN_REGISTER_CLASSES; |
||
333 | |||
334 | if (!$plugins_path) { |
||
335 | return false; |
||
336 | } |
||
337 | |||
338 | // temporary disable all plugins if there is a file called 'disabled' in the plugin dir |
||
339 | if (file_exists("$plugins_path/disabled")) { |
||
340 | if (elgg_is_admin_logged_in() && elgg_in_context('admin')) { |
||
341 | system_message(_elgg_services()->translator->translate('plugins:disabled')); |
||
342 | } |
||
343 | return false; |
||
344 | } |
||
345 | |||
346 | $config = _elgg_config(); |
||
347 | |||
348 | if ($config->system_cache_loaded) { |
||
349 | $start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_VIEWS; |
||
350 | } |
||
351 | |||
352 | if (!empty($GLOBALS['_ELGG']->i18n_loaded_from_cache)) { |
||
353 | $start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_LANGUAGES; |
||
354 | } |
||
355 | |||
356 | $plugins = $this->boot_plugins; |
||
357 | if (!$plugins) { |
||
0 ignored issues
–
show
|
|||
358 | $this->active_guids_known = true; |
||
359 | return true; |
||
360 | } |
||
361 | |||
362 | $return = true; |
||
363 | foreach ($plugins as $plugin) { |
||
364 | $id = $plugin->getID(); |
||
365 | try { |
||
366 | $plugin->start($start_flags); |
||
367 | $this->active_guids[$id] = $plugin->guid; |
||
368 | } catch (Exception $e) { |
||
369 | $disable_plugins = _elgg_config()->auto_disable_plugins; |
||
370 | if ($disable_plugins === null) { |
||
371 | $disable_plugins = true; |
||
372 | } |
||
373 | if ($disable_plugins) { |
||
374 | $plugin->deactivate(); |
||
375 | |||
376 | $msg = _elgg_services()->translator->translate('PluginException:CannotStart', |
||
377 | [$id, $plugin->guid, $e->getMessage()]); |
||
378 | elgg_add_admin_notice("cannot_start $id", $msg); |
||
379 | $return = false; |
||
380 | } |
||
381 | } |
||
382 | } |
||
383 | |||
384 | $this->active_guids_known = true; |
||
385 | |||
386 | if ($this->timer) { |
||
387 | $this->timer->end([__METHOD__]); |
||
388 | } |
||
389 | return $return; |
||
390 | } |
||
391 | |||
392 | /** |
||
393 | * Returns an ordered list of plugins |
||
394 | * |
||
395 | * @param string $status The status of the plugins. active, inactive, or all. |
||
396 | * @return \ElggPlugin[] |
||
397 | */ |
||
398 | function find($status = 'active') { |
||
0 ignored issues
–
show
|
|||
399 | $db_prefix = elgg_get_config('dbprefix'); |
||
400 | $priority = $this->namespacePrivateSetting('internal', 'priority'); |
||
401 | $site_guid = 1; |
||
402 | |||
403 | // grab plugins |
||
404 | $options = [ |
||
405 | 'type' => 'object', |
||
406 | 'subtype' => 'plugin', |
||
407 | 'limit' => ELGG_ENTITIES_NO_VALUE, |
||
408 | 'selects' => ['plugin_oe.*', 'ps.value'], |
||
409 | 'joins' => [ |
||
410 | "JOIN {$db_prefix}private_settings ps on ps.entity_guid = e.guid", |
||
411 | "JOIN {$db_prefix}objects_entity plugin_oe on plugin_oe.guid = e.guid" |
||
412 | ], |
||
413 | 'wheres' => ["ps.name = '$priority'"], |
||
414 | // ORDER BY CAST(ps.value) is super slow. We usort() below. |
||
415 | 'order_by' => false, |
||
416 | 'distinct' => false, |
||
417 | ]; |
||
418 | |||
419 | switch ($status) { |
||
420 | case 'active': |
||
421 | $options['relationship'] = 'active_plugin'; |
||
422 | $options['relationship_guid'] = $site_guid; |
||
423 | $options['inverse_relationship'] = true; |
||
424 | break; |
||
425 | |||
426 | case 'inactive': |
||
427 | $options['wheres'][] = "NOT EXISTS ( |
||
428 | SELECT 1 FROM {$db_prefix}entity_relationships active_er |
||
429 | WHERE active_er.guid_one = e.guid |
||
430 | AND active_er.relationship = 'active_plugin' |
||
431 | AND active_er.guid_two = $site_guid)"; |
||
432 | break; |
||
433 | |||
434 | case 'all': |
||
435 | default: |
||
436 | break; |
||
437 | } |
||
438 | |||
439 | $old_ia = elgg_set_ignore_access(true); |
||
440 | $plugins = elgg_get_entities_from_relationship($options); |
||
441 | elgg_set_ignore_access($old_ia); |
||
442 | |||
443 | usort($plugins, function (\ElggPlugin $a, \ElggPlugin $b) { |
||
444 | $a_value = $a->getVolatileData('select:value'); |
||
445 | $b_value = $b->getVolatileData('select:value'); |
||
446 | |||
447 | if ($b_value !== $a_value) { |
||
448 | return $a_value - $b_value; |
||
449 | } else { |
||
450 | return $a->guid - $b->guid; |
||
451 | } |
||
452 | }); |
||
453 | |||
454 | return $plugins; |
||
455 | } |
||
456 | |||
457 | /** |
||
458 | * Reorder plugins to an order specified by the array. |
||
459 | * Plugins not included in this array will be appended to the end. |
||
460 | * |
||
461 | * @note This doesn't use the \ElggPlugin->setPriority() method because |
||
462 | * all plugins are being changed and we don't want it to automatically |
||
463 | * reorder plugins. |
||
464 | * @todo Can this be done in a single sql command? |
||
465 | * |
||
466 | * @param array $order An array of plugin ids in the order to set them |
||
467 | * @return bool |
||
468 | * @access private |
||
469 | */ |
||
470 | function setPriorities(array $order) { |
||
471 | $name = $this->namespacePrivateSetting('internal', 'priority'); |
||
472 | |||
473 | $plugins = $this->find('any'); |
||
474 | if (!$plugins) { |
||
475 | return false; |
||
476 | } |
||
477 | |||
478 | // reindex to get standard counting. no need to increment by 10. |
||
479 | // though we do start with 1 |
||
480 | $order = array_values($order); |
||
481 | |||
482 | $missing_plugins = []; |
||
483 | /* @var \ElggPlugin[] $missing_plugins */ |
||
484 | |||
485 | $priority = 0; |
||
486 | foreach ($plugins as $plugin) { |
||
487 | $plugin_id = $plugin->getID(); |
||
488 | |||
489 | if (!in_array($plugin_id, $order)) { |
||
490 | $missing_plugins[] = $plugin; |
||
491 | continue; |
||
492 | } |
||
493 | |||
494 | $priority = array_search($plugin_id, $order) + 1; |
||
495 | |||
496 | if (!$plugin->setPrivateSetting($name, $priority)) { |
||
497 | return false; |
||
498 | } |
||
499 | } |
||
500 | |||
501 | // set the missing plugins' priorities |
||
502 | if (empty($missing_plugins)) { |
||
503 | return true; |
||
504 | } |
||
505 | |||
506 | foreach ($missing_plugins as $plugin) { |
||
507 | $priority++; |
||
508 | if (!$plugin->setPrivateSetting($name, $priority)) { |
||
509 | return false; |
||
510 | } |
||
511 | } |
||
512 | |||
513 | return true; |
||
514 | } |
||
515 | |||
516 | /** |
||
517 | * Reindexes all plugin priorities starting at 1. |
||
518 | * |
||
519 | * @return bool |
||
520 | * @access private |
||
521 | */ |
||
522 | function reindexPriorities() { |
||
523 | return $this->setPriorities([]); |
||
524 | } |
||
525 | |||
526 | /** |
||
527 | * Namespaces a string to be used as a private setting name for a plugin. |
||
528 | * |
||
529 | * For user_settings, two namespaces are added: a user setting namespace and the |
||
530 | * plugin id. |
||
531 | * |
||
532 | * For internal (plugin priority), there is a single internal namespace added. |
||
533 | * |
||
534 | * @param string $type The type of setting: user_setting or internal. |
||
535 | * @param string $name The name to namespace. |
||
536 | * @param string $id The plugin's ID to namespace with. Required for user_setting. |
||
537 | * @return string |
||
538 | * @access private |
||
539 | */ |
||
540 | function namespacePrivateSetting($type, $name, $id = null) { |
||
541 | switch ($type) { |
||
542 | // commented out because it breaks $plugin->$name access to variables |
||
543 | //case 'setting': |
||
544 | // $name = ELGG_PLUGIN_SETTING_PREFIX . $name; |
||
545 | // break; |
||
546 | |||
547 | case 'user_setting': |
||
548 | if (!$id) { |
||
549 | throw new \InvalidArgumentException("You must pass the plugin id for user settings"); |
||
550 | } |
||
551 | $name = ELGG_PLUGIN_USER_SETTING_PREFIX . "$id:$name"; |
||
552 | break; |
||
553 | |||
554 | case 'internal': |
||
555 | $name = ELGG_PLUGIN_INTERNAL_PREFIX . $name; |
||
556 | break; |
||
557 | } |
||
558 | |||
559 | return $name; |
||
560 | } |
||
561 | |||
562 | |||
563 | /** |
||
564 | * Returns an array of all provides from all active plugins. |
||
565 | * |
||
566 | * Array in the form array( |
||
567 | * 'provide_type' => array( |
||
568 | * 'provided_name' => array( |
||
569 | * 'version' => '1.8', |
||
570 | * 'provided_by' => 'provider_plugin_id' |
||
571 | * ) |
||
572 | * ) |
||
573 | * ) |
||
574 | * |
||
575 | * @param string $type The type of provides to return |
||
576 | * @param string $name A specific provided name to return. Requires $provide_type. |
||
577 | * |
||
578 | * @return array |
||
579 | * @access private |
||
580 | */ |
||
581 | function getProvides($type = null, $name = null) { |
||
582 | if ($this->provides_cache === null) { |
||
583 | $active_plugins = $this->find('active'); |
||
584 | |||
585 | $provides = []; |
||
586 | |||
587 | foreach ($active_plugins as $plugin) { |
||
588 | $plugin_provides = []; |
||
589 | $manifest = $plugin->getManifest(); |
||
590 | if ($manifest instanceof \ElggPluginManifest) { |
||
591 | $plugin_provides = $plugin->getManifest()->getProvides(); |
||
592 | } |
||
593 | if ($plugin_provides) { |
||
594 | foreach ($plugin_provides as $provided) { |
||
595 | $provides[$provided['type']][$provided['name']] = [ |
||
596 | 'version' => $provided['version'], |
||
597 | 'provided_by' => $plugin->getID() |
||
598 | ]; |
||
599 | } |
||
600 | } |
||
601 | } |
||
602 | |||
603 | $this->provides_cache = $provides; |
||
604 | } |
||
605 | |||
606 | if ($type && $name) { |
||
607 | View Code Duplication | if (isset($this->provides_cache[$type][$name])) { |
|
608 | return $this->provides_cache[$type][$name]; |
||
609 | } else { |
||
610 | return false; |
||
611 | } |
||
612 | View Code Duplication | } elseif ($type) { |
|
613 | if (isset($this->provides_cache[$type])) { |
||
614 | return $this->provides_cache[$type]; |
||
615 | } else { |
||
616 | return false; |
||
617 | } |
||
618 | } |
||
619 | |||
620 | return $this->provides_cache; |
||
621 | } |
||
622 | |||
623 | /** |
||
624 | * Deletes all cached data on plugins being provided. |
||
625 | * |
||
626 | * @return boolean |
||
627 | * @access private |
||
628 | */ |
||
629 | function invalidateProvidesCache() { |
||
630 | $this->provides_cache = null; |
||
631 | return true; |
||
632 | } |
||
633 | |||
634 | /** |
||
635 | * Delete the cache holding whether plugins are active or not |
||
636 | * |
||
637 | * @return void |
||
638 | * @access private |
||
639 | */ |
||
640 | public function invalidateIsActiveCache() { |
||
641 | $this->active_guids = []; |
||
642 | $this->active_guids_known = false; |
||
643 | } |
||
644 | |||
645 | /** |
||
646 | * Checks if a plugin is currently providing $type and $name, and optionally |
||
647 | * checking a version. |
||
648 | * |
||
649 | * @param string $type The type of the provide |
||
650 | * @param string $name The name of the provide |
||
651 | * @param string $version A version to check against |
||
652 | * @param string $comparison The comparison operator to use in version_compare() |
||
653 | * |
||
654 | * @return array An array in the form array( |
||
655 | * 'status' => bool Does the provide exist?, |
||
656 | * 'value' => string The version provided |
||
657 | * ) |
||
658 | * @access private |
||
659 | */ |
||
660 | function checkProvides($type, $name, $version = null, $comparison = 'ge') { |
||
661 | $provided = $this->getProvides($type, $name); |
||
662 | if (!$provided) { |
||
663 | return [ |
||
664 | 'status' => false, |
||
665 | 'value' => '' |
||
666 | ]; |
||
667 | } |
||
668 | |||
669 | if ($version) { |
||
670 | $status = version_compare($provided['version'], $version, $comparison); |
||
671 | } else { |
||
672 | $status = true; |
||
673 | } |
||
674 | |||
675 | return [ |
||
676 | 'status' => $status, |
||
677 | 'value' => $provided['version'] |
||
678 | ]; |
||
679 | } |
||
680 | |||
681 | /** |
||
682 | * Returns an array of parsed strings for a dependency in the |
||
683 | * format: array( |
||
684 | * 'type' => requires, conflicts, or provides. |
||
685 | * 'name' => The name of the requirement / conflict |
||
686 | * 'value' => A string representing the expected value: <1, >=3, !=enabled |
||
687 | * 'local_value' => The current value, ("Not installed") |
||
688 | * 'comment' => Free form text to help resovle the problem ("Enable / Search for plugin <link>") |
||
689 | * ) |
||
690 | * |
||
691 | * @param array $dep An \ElggPluginPackage dependency array |
||
692 | * @return array |
||
693 | * @access private |
||
694 | */ |
||
695 | function getDependencyStrings($dep) { |
||
696 | $translator = _elgg_services()->translator; |
||
697 | $dep_system = elgg_extract('type', $dep); |
||
698 | $info = elgg_extract('dep', $dep); |
||
699 | $type = elgg_extract('type', $info); |
||
700 | |||
701 | if (!$dep_system || !$info || !$type) { |
||
702 | return false; |
||
703 | } |
||
704 | |||
705 | // rewrite some of these to be more readable |
||
706 | $comparison = elgg_extract('comparison', $info); |
||
707 | switch ($comparison) { |
||
708 | case 'lt': |
||
709 | $comparison = '<'; |
||
710 | break; |
||
711 | case 'gt': |
||
712 | $comparison = '>'; |
||
713 | break; |
||
714 | case 'ge': |
||
715 | $comparison = '>='; |
||
716 | break; |
||
717 | case 'le': |
||
718 | $comparison = '<='; |
||
719 | break; |
||
720 | default: |
||
721 | //keep $comparison value intact |
||
722 | break; |
||
723 | } |
||
724 | |||
725 | /* |
||
726 | 'requires' 'plugin oauth_lib' <1.3 1.3 'downgrade' |
||
727 | 'requires' 'php setting bob' >3 3 'change it' |
||
728 | 'conflicts' 'php setting' >3 4 'change it' |
||
729 | 'conflicted''plugin profile' any 1.8 'disable profile' |
||
730 | 'provides' 'plugin oauth_lib' 1.3 -- -- |
||
731 | 'priority' 'before blog' -- after 'move it' |
||
732 | */ |
||
733 | $strings = []; |
||
734 | $strings['type'] = $translator->translate('ElggPlugin:Dependencies:' . ucwords($dep_system)); |
||
735 | |||
736 | switch ($type) { |
||
737 | View Code Duplication | case 'elgg_release': |
|
738 | // 'Elgg Version' |
||
739 | $strings['name'] = $translator->translate('ElggPlugin:Dependencies:Elgg'); |
||
740 | $strings['expected_value'] = "$comparison {$info['version']}"; |
||
741 | $strings['local_value'] = $dep['value']; |
||
742 | $strings['comment'] = ''; |
||
743 | break; |
||
744 | |||
745 | View Code Duplication | case 'php_version': |
|
746 | // 'PHP version' |
||
747 | $strings['name'] = $translator->translate('ElggPlugin:Dependencies:PhpVersion'); |
||
748 | $strings['expected_value'] = "$comparison {$info['version']}"; |
||
749 | $strings['local_value'] = $dep['value']; |
||
750 | $strings['comment'] = ''; |
||
751 | break; |
||
752 | |||
753 | case 'php_extension': |
||
754 | // PHP Extension %s [version] |
||
755 | $strings['name'] = $translator->translate('ElggPlugin:Dependencies:PhpExtension', [$info['name']]); |
||
756 | if ($info['version']) { |
||
757 | $strings['expected_value'] = "$comparison {$info['version']}"; |
||
758 | $strings['local_value'] = $dep['value']; |
||
759 | } else { |
||
760 | $strings['expected_value'] = ''; |
||
761 | $strings['local_value'] = ''; |
||
762 | } |
||
763 | $strings['comment'] = ''; |
||
764 | break; |
||
765 | |||
766 | View Code Duplication | case 'php_ini': |
|
767 | $strings['name'] = $translator->translate('ElggPlugin:Dependencies:PhpIni', [$info['name']]); |
||
768 | $strings['expected_value'] = "$comparison {$info['value']}"; |
||
769 | $strings['local_value'] = $dep['value']; |
||
770 | $strings['comment'] = ''; |
||
771 | break; |
||
772 | |||
773 | case 'plugin': |
||
774 | $strings['name'] = $translator->translate('ElggPlugin:Dependencies:Plugin', [$info['name']]); |
||
775 | $expected = $info['version'] ? "$comparison {$info['version']}" : $translator->translate('any'); |
||
776 | $strings['expected_value'] = $expected; |
||
777 | $strings['local_value'] = $dep['value'] ? $dep['value'] : '--'; |
||
778 | $strings['comment'] = ''; |
||
779 | break; |
||
780 | |||
781 | case 'priority': |
||
782 | $expected_priority = ucwords($info['priority']); |
||
783 | $real_priority = ucwords($dep['value']); |
||
784 | $strings['name'] = $translator->translate('ElggPlugin:Dependencies:Priority'); |
||
785 | $strings['expected_value'] = $translator->translate("ElggPlugin:Dependencies:Priority:$expected_priority", [$info['plugin']]); |
||
786 | $strings['local_value'] = $translator->translate("ElggPlugin:Dependencies:Priority:$real_priority", [$info['plugin']]); |
||
787 | $strings['comment'] = ''; |
||
788 | break; |
||
789 | } |
||
790 | |||
791 | if ($dep['type'] == 'suggests') { |
||
792 | View Code Duplication | if ($dep['status']) { |
|
793 | $strings['comment'] = $translator->translate('ok'); |
||
794 | } else { |
||
795 | $strings['comment'] = $translator->translate('ElggPlugin:Dependencies:Suggests:Unsatisfied'); |
||
796 | } |
||
797 | View Code Duplication | } else { |
|
798 | if ($dep['status']) { |
||
799 | $strings['comment'] = $translator->translate('ok'); |
||
800 | } else { |
||
801 | $strings['comment'] = $translator->translate('error'); |
||
802 | } |
||
803 | } |
||
804 | |||
805 | return $strings; |
||
806 | } |
||
807 | |||
808 | /** |
||
809 | * Returns an array of all plugin user settings for a user. |
||
810 | * |
||
811 | * @param int $user_guid The user GUID or 0 for the currently logged in user. |
||
812 | * @param string $plugin_id The plugin ID (Required) |
||
813 | * @param bool $return_obj Return settings as an object? This can be used to in reusable |
||
814 | * views where the settings are passed as $vars['entity']. |
||
815 | * @return array |
||
816 | * @see \ElggPlugin::getAllUserSettings() |
||
817 | */ |
||
818 | function getAllUserSettings($user_guid = 0, $plugin_id = null, $return_obj = false) { |
||
819 | $plugin = $this->get($plugin_id); |
||
820 | if (!$plugin) { |
||
821 | return false; |
||
822 | } |
||
823 | |||
824 | $settings = $plugin->getAllUserSettings((int) $user_guid); |
||
825 | |||
826 | if ($settings && $return_obj) { |
||
827 | $return = new \stdClass; |
||
828 | |||
829 | foreach ($settings as $k => $v) { |
||
830 | $return->$k = $v; |
||
831 | } |
||
832 | |||
833 | return $return; |
||
834 | } else { |
||
835 | return $settings; |
||
836 | } |
||
837 | } |
||
838 | |||
839 | /** |
||
840 | * Set a user specific setting for a plugin. |
||
841 | * |
||
842 | * @param string $name The name. Note: cannot be "title". |
||
843 | * @param mixed $value The value. |
||
844 | * @param int $user_guid The user GUID or 0 for the currently logged in user. |
||
845 | * @param string $plugin_id The plugin ID (Required) |
||
846 | * |
||
847 | * @return bool |
||
848 | * @see \ElggPlugin::setUserSetting() |
||
849 | */ |
||
850 | View Code Duplication | function setUserSetting($name, $value, $user_guid = 0, $plugin_id = null) { |
|
851 | $plugin = $this->get($plugin_id); |
||
852 | if (!$plugin) { |
||
853 | return false; |
||
854 | } |
||
855 | |||
856 | return $plugin->setUserSetting($name, $value, (int) $user_guid); |
||
857 | } |
||
858 | |||
859 | /** |
||
860 | * Unsets a user-specific plugin setting |
||
861 | * |
||
862 | * @param string $name Name of the setting |
||
863 | * @param int $user_guid The user GUID or 0 for the currently logged in user. |
||
864 | * @param string $plugin_id The plugin ID (Required) |
||
865 | * |
||
866 | * @return bool |
||
867 | * @see \ElggPlugin::unsetUserSetting() |
||
868 | */ |
||
869 | function unsetUserSetting($name, $user_guid = 0, $plugin_id = null) { |
||
870 | $plugin = $this->get($plugin_id); |
||
871 | if (!$plugin) { |
||
872 | return false; |
||
873 | } |
||
874 | |||
875 | return $plugin->unsetUserSetting($name, (int) $user_guid); |
||
876 | } |
||
877 | |||
878 | /** |
||
879 | * Get a user specific setting for a plugin. |
||
880 | * |
||
881 | * @param string $name The name of the setting. |
||
882 | * @param int $user_guid The user GUID or 0 for the currently logged in user. |
||
883 | * @param string $plugin_id The plugin ID (Required) |
||
884 | * @param mixed $default The default value to return if none is set |
||
885 | * |
||
886 | * @return mixed |
||
887 | * @see \ElggPlugin::getUserSetting() |
||
888 | */ |
||
889 | View Code Duplication | function getUserSetting($name, $user_guid = 0, $plugin_id = null, $default = null) { |
|
890 | $plugin = $this->get($plugin_id); |
||
891 | if (!$plugin) { |
||
892 | return false; |
||
893 | } |
||
894 | |||
895 | return $plugin->getUserSetting($name, (int) $user_guid, $default); |
||
896 | } |
||
897 | |||
898 | /** |
||
899 | * Set a setting for a plugin. |
||
900 | * |
||
901 | * @param string $name The name of the setting - note, can't be "title". |
||
902 | * @param mixed $value The value. |
||
903 | * @param string $plugin_id The plugin ID (Required) |
||
904 | * |
||
905 | * @return bool |
||
906 | * @see \ElggPlugin::setSetting() |
||
907 | */ |
||
908 | function setSetting($name, $value, $plugin_id) { |
||
909 | $plugin = $this->get($plugin_id); |
||
910 | if (!$plugin) { |
||
911 | return false; |
||
912 | } |
||
913 | |||
914 | return $plugin->setSetting($name, $value); |
||
915 | } |
||
916 | |||
917 | /** |
||
918 | * Get setting for a plugin. |
||
919 | * |
||
920 | * @param string $name The name of the setting. |
||
921 | * @param string $plugin_id The plugin ID (Required) |
||
922 | * @param mixed $default The default value to return if none is set |
||
923 | * |
||
924 | * @return mixed |
||
925 | * @see \ElggPlugin::getSetting() |
||
926 | */ |
||
927 | function getSetting($name, $plugin_id, $default = null) { |
||
928 | $plugin = $this->get($plugin_id); |
||
929 | if (!$plugin) { |
||
930 | return false; |
||
931 | } |
||
932 | |||
933 | return $plugin->getSetting($name, $default); |
||
934 | } |
||
935 | |||
936 | /** |
||
937 | * Unsets a plugin setting. |
||
938 | * |
||
939 | * @param string $name The name of the setting. |
||
940 | * @param string $plugin_id The plugin ID (Required) |
||
941 | * |
||
942 | * @return bool |
||
943 | * @see \ElggPlugin::unsetSetting() |
||
944 | */ |
||
945 | function unsetSetting($name, $plugin_id) { |
||
946 | $plugin = $this->get($plugin_id); |
||
947 | if (!$plugin) { |
||
948 | return false; |
||
949 | } |
||
950 | |||
951 | return $plugin->unsetSetting($name); |
||
952 | } |
||
953 | |||
954 | /** |
||
955 | * Unsets all plugin settings for a plugin. |
||
956 | * |
||
957 | * @param string $plugin_id The plugin ID (Required) |
||
958 | * |
||
959 | * @return bool |
||
960 | * @see \ElggPlugin::unsetAllSettings() |
||
961 | */ |
||
962 | function unsetAllSettings($plugin_id) { |
||
963 | $plugin = $this->get($plugin_id); |
||
964 | if (!$plugin) { |
||
965 | return false; |
||
966 | } |
||
967 | |||
968 | return $plugin->unsetAllSettings(); |
||
969 | } |
||
970 | |||
971 | /** |
||
972 | * Returns entities based upon plugin user settings. |
||
973 | * Takes all the options for {@link elgg_get_entities_from_private_settings()} |
||
974 | * in addition to the ones below. |
||
975 | * |
||
976 | * @param array $options Array in the format: |
||
977 | * |
||
978 | * plugin_id => STR The plugin id. Required. |
||
979 | * |
||
980 | * plugin_user_setting_names => null|ARR private setting names |
||
981 | * |
||
982 | * plugin_user_setting_values => null|ARR metadata values |
||
983 | * |
||
984 | * plugin_user_setting_name_value_pairs => null|ARR ( |
||
985 | * name => 'name', |
||
986 | * value => 'value', |
||
987 | * 'operand' => '=', |
||
988 | * ) |
||
989 | * Currently if multiple values are sent via |
||
990 | * an array (value => array('value1', 'value2') |
||
991 | * the pair's operand will be forced to "IN". |
||
992 | * |
||
993 | * plugin_user_setting_name_value_pairs_operator => null|STR The operator to use for combining |
||
994 | * (name = value) OPERATOR (name = value); default AND |
||
995 | * |
||
996 | * @return mixed int If count, int. If not count, array. false on errors. |
||
997 | */ |
||
998 | function getEntitiesFromUserSettings(array $options = []) { |
||
999 | $singulars = ['plugin_user_setting_name', 'plugin_user_setting_value', |
||
1000 | 'plugin_user_setting_name_value_pair']; |
||
1001 | |||
1002 | $options = _elgg_normalize_plural_options_array($options, $singulars); |
||
1003 | |||
1004 | // rewrite plugin_user_setting_name_* to the right PS ones. |
||
1005 | $map = [ |
||
1006 | 'plugin_user_setting_names' => 'private_setting_names', |
||
1007 | 'plugin_user_setting_values' => 'private_setting_values', |
||
1008 | 'plugin_user_setting_name_value_pairs' => 'private_setting_name_value_pairs', |
||
1009 | 'plugin_user_setting_name_value_pairs_operator' => 'private_setting_name_value_pairs_operator', |
||
1010 | ]; |
||
1011 | |||
1012 | foreach ($map as $plugin => $private) { |
||
1013 | if (!isset($options[$plugin])) { |
||
1014 | continue; |
||
1015 | } |
||
1016 | |||
1017 | if (isset($options[$private])) { |
||
1018 | if (!is_array($options[$private])) { |
||
1019 | $options[$private] = [$options[$private]]; |
||
1020 | } |
||
1021 | |||
1022 | $options[$private] = array_merge($options[$private], $options[$plugin]); |
||
1023 | } else { |
||
1024 | $options[$private] = $options[$plugin]; |
||
1025 | } |
||
1026 | } |
||
1027 | |||
1028 | $prefix = $this->namespacePrivateSetting('user_setting', '', $options['plugin_id']); |
||
1029 | $options['private_setting_name_prefix'] = $prefix; |
||
1030 | |||
1031 | return elgg_get_entities_from_private_settings($options); |
||
1032 | } |
||
1033 | } |
||
1034 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.