Test Failed
Push — master ( 8c47c2...3acf9f )
by Steve
12:37
created

engine/classes/Elgg/Database/Plugins.php (3 issues)

Upgrade to new PHP Analysis Engine

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() {
0 ignored issues
show
load uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
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
Bug Best Practice introduced by
The expression $plugins of type ElggPlugin[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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