Completed
Push — master ( fab0a4...8e249d )
by Jeroen
41:43 queued 16:43
created

Plugins::get()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2.003

Importance

Changes 0
Metric Value
cc 2
eloc 13
nc 1
nop 1
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
ccs 10
cts 11
cp 0.9091
crap 2.003
1
<?php
2
namespace Elgg\Database;
3
4
use Elgg\Cache\Pool;
5
use Elgg\Config;
6
use Elgg\Profilable;
7
use Exception;
8
use Elgg\Cache\PluginSettingsCache;
9
10
/**
11
 * Persistent, installation-wide key-value storage.
12
 *
13
 * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
14
 *
15
 * @access private
16
 *
17
 * @since 1.10.0
18
 */
19
class Plugins {
20
	use Profilable;
21
22
	/**
23
	 * @var \ElggPlugin[]
24
	 */
25
	private $boot_plugins = [];
26
27
	/**
28
	 * @var array|null
29
	 */
30
	private $provides_cache;
31
32
	/**
33
	 * @var string[] Active plugins, with plugin ID => GUID. Missing keys imply inactive plugins.
34
	 */
35
	private $active_guids = [];
36
37
	/**
38
	 * @var bool Has $active_guids been populated?
39
	 */
40
	private $active_guids_known = false;
41
42
	/**
43
	 * @var Pool
44
	 */
45
	private $plugins_by_id;
46
47
	/**
48
	 * @var PluginSettingsCache
49
	 */
50
	private $settings_cache;
51
52
	/**
53
	 * Constructor
54
	 *
55
	 * @param Pool                $pool  Cache for referencing plugins by ID
56
	 * @param PluginSettingsCache $cache Plugin settings cache
57
	 */
58 1352
	public function __construct(Pool $pool, PluginSettingsCache $cache) {
59 1352
		$this->plugins_by_id = $pool;
60 1352
		$this->settings_cache = $cache;
61 1352
	}
62
63
	/**
64
	 * Set the list of active plugins according to the boot data cache
65
	 *
66
	 * @param \ElggPlugin[] $plugins Set of active plugins
67
	 * @return void
68
	 */
69 311
	public function setBootPlugins(array $plugins) {
70 311
		$this->boot_plugins = $plugins;
71 311
		foreach ($plugins as $plugin) {
72 311
			$this->plugins_by_id->put($plugin->getID(), $plugin);
73
		}
74 311
	}
75
76
	/**
77
	 * Returns a list of plugin directory names from a base directory.
78
	 *
79
	 * @param string $dir A dir to scan for plugins. Defaults to config's plugins_path.
80
	 *                    Must have a trailing slash.
81
	 *
82
	 * @return array Array of directory names (not full paths)
83
	 * @access private
84
	 */
85
	function getDirsInDir($dir = null) {
0 ignored issues
show
Best Practice introduced by
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...
86
		if (!$dir) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $dir of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
87
			$dir = elgg_get_plugins_path();
88
		}
89
	
90
		$plugin_dirs = [];
91
		$handle = opendir($dir);
92
	
93
		if ($handle) {
94
			while ($plugin_dir = readdir($handle)) {
95
				// must be directory and not begin with a .
96
				if (substr($plugin_dir, 0, 1) !== '.' && is_dir($dir . $plugin_dir)) {
97
					$plugin_dirs[] = $plugin_dir;
98
				}
99
			}
100
		}
101
	
102
		sort($plugin_dirs);
103
	
104
		return $plugin_dirs;
105
	}
106
	
107
	/**
108
	 * Discovers plugins in the plugins_path setting and creates \ElggPlugin
109
	 * entities for them if they don't exist.  If there are plugins with entities
110
	 * but not actual files, will disable the \ElggPlugin entities and mark as inactive.
111
	 * The \ElggPlugin object holds config data, so don't delete.
112
	 *
113
	 * @return bool
114
	 * @access private
115
	 */
116
	function generateEntities() {
0 ignored issues
show
Best Practice introduced by
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...
117
	
118
		$mod_dir = elgg_get_plugins_path();
119
		$db_prefix = _elgg_config()->dbprefix;
0 ignored issues
show
Unused Code introduced by
$db_prefix is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
120
	
121
		// ignore access in case this is called with no admin logged in - needed for creating plugins perhaps?
122
		$old_ia = elgg_set_ignore_access(true);
123
	
124
		// show hidden entities so that we can enable them if appropriate
125
		$old_access = access_show_hidden_entities(true);
126
	
127
		$known_plugins = elgg_get_entities_from_relationship([
128
			'type' => 'object',
129
			'subtype' => 'plugin',
130
			'limit' => ELGG_ENTITIES_NO_VALUE,
131
		]);
132
		/* @var \ElggPlugin[] $known_plugins */
133
	
134
		if (!$known_plugins) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $known_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...
135
			$known_plugins = [];
136
		}
137
	
138
		// map paths to indexes
139
		$id_map = [];
140
		foreach ($known_plugins as $i => $plugin) {
141
			// if the ID is wrong, delete the plugin because we can never load it.
142
			$id = $plugin->getID();
143
			if (!$id) {
144
				$plugin->delete();
145
				unset($known_plugins[$i]);
146
				continue;
147
			}
148
			$id_map[$plugin->getID()] = $i;
149
		}
150
	
151
		$physical_plugins = $this->getDirsInDir($mod_dir);
152
		if (!$physical_plugins) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $physical_plugins of type array 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...
153
			elgg_set_ignore_access($old_ia);
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 311
	function cache(\ElggPlugin $plugin) {
0 ignored issues
show
Best Practice introduced by
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...
209 311
		$this->plugins_by_id->put($plugin->getID(), $plugin);
210 311
	}
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 311
	function get($plugin_id) {
0 ignored issues
show
Best Practice introduced by
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...
219
		return $this->plugins_by_id->get($plugin_id, function () use ($plugin_id) {
220 1
			$plugins = elgg_get_entities_from_metadata([
221 1
				'type' => 'object',
222 1
				'subtype' => 'plugin',
223
				'metadata_name_value_pairs' => [
224 1
					'name' => 'title',
225 1
					'value' => $plugin_id,
226
				],
227 1
				'limit' => 1,
228
				'distinct' => false,
229
			]);
230
231 1
			if ($plugins) {
232 1
				return $plugins[0];
233
			}
234
235
			return null;
236 311
		});
237
	}
238
	
239
	/**
240
	 * Returns if a plugin exists in the system.
241
	 *
242
	 * @warning This checks only plugins that are registered in the system!
243
	 * If the plugin cache is outdated, be sure to regenerate it with
244
	 * {@link _elgg_generate_plugin_objects()} first.
245
	 *
246
	 * @param string $id The plugin ID.
247
	 * @return bool
248
	 */
249
	function exists($id) {
0 ignored issues
show
Best Practice introduced by
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...
250
		return (bool) $this->get($id);
251
	}
252
	
253
	/**
254
	 * Returns the highest priority of the plugins
255
	 *
256
	 * @return int
257
	 * @access private
258
	 */
259 1
	function getMaxPriority() {
0 ignored issues
show
Best Practice introduced by
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...
260 1
		$db_prefix = _elgg_config()->dbprefix;
261 1
		$priority = $this->namespacePrivateSetting('internal', 'priority');
262 1
		$plugin_subtype = get_subtype_id('object', 'plugin');
263
	
264
		$q = "SELECT MAX(CAST(ps.value AS unsigned)) as max
265 1
			FROM {$db_prefix}entities e, {$db_prefix}private_settings ps
266 1
			WHERE ps.name = '$priority'
267
			AND ps.entity_guid = e.guid
268 1
			AND e.type = 'object' and e.subtype = $plugin_subtype";
269
	
270 1
		$data = get_data($q);
271 1
		if ($data) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type array 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...
272 1
			$max = $data[0]->max;
273
		} else {
274
			$max = 1;
275
		}
276
	
277
		// can't have a priority of 0.
278 1
		return ($max) ? $max : 1;
279
	}
280
	
281
	/**
282
	 * Returns if a plugin is active for a current site.
283
	 *
284
	 * @param string $plugin_id The plugin ID
285
	 * @return bool
286
	 */
287 2
	function isActive($plugin_id) {
0 ignored issues
show
Best Practice introduced by
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...
288 2
		if ($this->active_guids_known) {
289 1
			return isset($this->active_guids[$plugin_id]);
290
		}
291
292 1
		$site = elgg_get_site_entity();
293
	
294 1
		if (!($site instanceof \ElggSite)) {
295
			return false;
296
		}
297
	
298 1
		$plugin = $this->get($plugin_id);
299
	
300 1
		if (!$plugin) {
301
			return false;
302
		}
303
	
304 1
		return $plugin->isActive($site->guid);
0 ignored issues
show
Unused Code introduced by
The call to ElggPlugin::isActive() has too many arguments starting with $site->guid.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
305
	}
306
	
307
	/**
308
	 * Loads all active plugins in the order specified in the tool admin panel.
309
	 *
310
	 * @note This is called on every page load. If a plugin is active and problematic, it
311
	 * will be disabled and a visible error emitted. This does not check the deps system because
312
	 * that was too slow.
313
	 *
314
	 * @return bool
315
	 * @access private
316
	 */
317 311
	function load() {
0 ignored issues
show
Best Practice introduced by
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...
318 311
		if ($this->timer) {
319
			$this->timer->begin([__METHOD__]);
320
		}
321
322 311
		$plugins_path = elgg_get_plugins_path();
323 311
		$start_flags = ELGG_PLUGIN_INCLUDE_START |
324 311
						ELGG_PLUGIN_REGISTER_VIEWS |
325 311
						ELGG_PLUGIN_REGISTER_ACTIONS |
326 311
						ELGG_PLUGIN_REGISTER_LANGUAGES |
327 311
						ELGG_PLUGIN_REGISTER_WIDGETS |
328 311
						ELGG_PLUGIN_REGISTER_CLASSES;
329
	
330 311
		if (!$plugins_path) {
331
			return false;
332
		}
333
	
334
		// temporary disable all plugins if there is a file called 'disabled' in the plugin dir
335 311
		if (file_exists("$plugins_path/disabled")) {
336
			if (elgg_is_admin_logged_in() && elgg_in_context('admin')) {
337
				system_message(_elgg_services()->translator->translate('plugins:disabled'));
338
			}
339
			return false;
340
		}
341
342 311
		$config = _elgg_config();
343
	
344 311
		if ($config->system_cache_loaded) {
345 311
			$start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_VIEWS;
346
		}
347
	
348 311
		if (_elgg_services()->translator->wasLoadedFromCache()) {
349 311
			$start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_LANGUAGES;
350
		}
351
	
352 311
		$plugins = $this->boot_plugins;
353 311
		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...
354
			$this->active_guids_known = true;
355
			return true;
356
		}
357
358 311
		$return = true;
359 311
		foreach ($plugins as $plugin) {
360 311
			$id = $plugin->getID();
361
			try {
362 311
				$plugin->start($start_flags);
363 311
				$this->active_guids[$id] = $plugin->guid;
364
			} catch (Exception $e) {
365
				$disable_plugins = _elgg_config()->auto_disable_plugins;
366
				if ($disable_plugins === null) {
367
					$disable_plugins = true;
368
				}
369
				if ($disable_plugins) {
370
					$plugin->deactivate();
371
				
372
					$msg = _elgg_services()->translator->translate('PluginException:CannotStart',
373
									[$id, $plugin->guid, $e->getMessage()]);
374
					elgg_add_admin_notice("cannot_start $id", $msg);
375 311
					$return = false;
376
				}
377
			}
378
		}
379
380 311
		$this->active_guids_known = true;
381
382 311
		if ($this->timer) {
383
			$this->timer->end([__METHOD__]);
384
		}
385 311
		return $return;
386
	}
387
	
388
	/**
389
	 * Returns an ordered list of plugins
390
	 *
391
	 * @param string $status The status of the plugins. active, inactive, or all.
392
	 * @return \ElggPlugin[]
393
	 */
394 311
	function find($status = 'active') {
0 ignored issues
show
Best Practice introduced by
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...
395 311
		if (!_elgg_services()->db) {
396
			return [];
397
		}
398
399 311
		$db_prefix = _elgg_config()->dbprefix;
400 311
		$priority = $this->namespacePrivateSetting('internal', 'priority');
401 311
		$site_guid = 1;
402
		
403
		// grab plugins
404
		$options = [
405 311
			'type' => 'object',
406 311
			'subtype' => 'plugin',
407 311
			'limit' => ELGG_ENTITIES_NO_VALUE,
408
			'selects' => ['ps.value'],
409
			'joins' => [
410 311
				"JOIN {$db_prefix}private_settings ps on ps.entity_guid = e.guid",
411
			],
412 311
			'wheres' => ["ps.name = '$priority'"],
413
			// ORDER BY CAST(ps.value) is super slow. We usort() below.
414
			'order_by' => false,
415
		];
416
	
417
		switch ($status) {
418 311
			case 'active':
419 311
				$options['relationship'] = 'active_plugin';
420 311
				$options['relationship_guid'] = $site_guid;
421 311
				$options['inverse_relationship'] = true;
422 311
				break;
423
	
424
			case 'inactive':
425
				$options['wheres'][] = "NOT EXISTS (
426
						SELECT 1 FROM {$db_prefix}entity_relationships active_er
427
						WHERE active_er.guid_one = e.guid
428
							AND active_er.relationship = 'active_plugin'
429
							AND active_er.guid_two = $site_guid)";
430
				break;
431
	
432
			case 'all':
433
			default:
434
				break;
435
		}
436
	
437 311
		$old_ia = elgg_set_ignore_access(true);
438 311
		$plugins = elgg_get_entities_from_relationship($options);
439 311
		elgg_set_ignore_access($old_ia);
440
441 311
		usort($plugins, function (\ElggPlugin $a, \ElggPlugin $b) {
442 311
			$a_value = $a->getVolatileData('select:value');
443 311
			$b_value = $b->getVolatileData('select:value');
444
445 311
			if ($b_value !== $a_value) {
446 311
				return $a_value - $b_value;
447
			} else {
448 311
				return $a->guid - $b->guid;
449
			}
450 311
		});
451
	
452 311
		return $plugins;
453
	}
454
	
455
	/**
456
	 * Reorder plugins to an order specified by the array.
457
	 * Plugins not included in this array will be appended to the end.
458
	 *
459
	 * @note This doesn't use the \ElggPlugin->setPriority() method because
460
	 *       all plugins are being changed and we don't want it to automatically
461
	 *       reorder plugins.
462
	 * @todo Can this be done in a single sql command?
463
	 *
464
	 * @param array $order An array of plugin ids in the order to set them
465
	 * @return bool
466
	 * @access private
467
	 */
468
	function setPriorities(array $order) {
0 ignored issues
show
Best Practice introduced by
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...
469
		$name = $this->namespacePrivateSetting('internal', 'priority');
470
	
471
		$plugins = $this->find('any');
472
		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...
473
			return false;
474
		}
475
	
476
		// reindex to get standard counting. no need to increment by 10.
477
		// though we do start with 1
478
		$order = array_values($order);
479
	
480
		$missing_plugins = [];
481
		/* @var \ElggPlugin[] $missing_plugins */
482
	
483
		$priority = 0;
484
		foreach ($plugins as $plugin) {
485
			$plugin_id = $plugin->getID();
486
	
487
			if (!in_array($plugin_id, $order)) {
488
				$missing_plugins[] = $plugin;
489
				continue;
490
			}
491
	
492
			$priority = array_search($plugin_id, $order) + 1;
493
	
494
			if (!$plugin->setPrivateSetting($name, $priority)) {
495
				return false;
496
			}
497
		}
498
	
499
		// set the missing plugins' priorities
500
		if (empty($missing_plugins)) {
501
			return true;
502
		}
503
		
504
		foreach ($missing_plugins as $plugin) {
505
			$priority++;
506
			if (!$plugin->setPrivateSetting($name, $priority)) {
507
				return false;
508
			}
509
		}
510
	
511
		return true;
512
	}
513
	
514
	/**
515
	 * Reindexes all plugin priorities starting at 1.
516
	 *
517
	 * @return bool
518
	 * @access private
519
	 */
520
	function reindexPriorities() {
0 ignored issues
show
Best Practice introduced by
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...
521
		return $this->setPriorities([]);
522
	}
523
	
524
	/**
525
	 * Namespaces a string to be used as a private setting name for a plugin.
526
	 *
527
	 * For user_settings, two namespaces are added: a user setting namespace and the
528
	 * plugin id.
529
	 *
530
	 * For internal (plugin priority), there is a single internal namespace added.
531
	 *
532
	 * @param string $type The type of setting: user_setting or internal.
533
	 * @param string $name The name to namespace.
534
	 * @param string $id   The plugin's ID to namespace with.  Required for user_setting.
535
	 * @return string
536
	 * @access private
537
	 */
538 311
	function namespacePrivateSetting($type, $name, $id = null) {
0 ignored issues
show
Best Practice introduced by
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...
539
		switch ($type) {
540 311
			case 'user_setting':
541 2
				if (!$id) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $id of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
542
					throw new \InvalidArgumentException("You must pass the plugin id for user settings");
543
				}
544 2
				$name = ELGG_PLUGIN_USER_SETTING_PREFIX . "$id:$name";
545 2
				break;
546
	
547 311
			case 'internal':
548 311
				$name = ELGG_PLUGIN_INTERNAL_PREFIX . $name;
549 311
				break;
550
		}
551
	
552 311
		return $name;
553
	}
554
	
555
	
556
	/**
557
	 * Returns an array of all provides from all active plugins.
558
	 *
559
	 * Array in the form array(
560
	 * 	'provide_type' => array(
561
	 * 		'provided_name' => array(
562
	 * 			'version' => '1.8',
563
	 * 			'provided_by' => 'provider_plugin_id'
564
	 *  	)
565
	 *  )
566
	 * )
567
	 *
568
	 * @param string $type The type of provides to return
569
	 * @param string $name A specific provided name to return. Requires $provide_type.
570
	 *
571
	 * @return array
572
	 * @access private
573
	 */
574
	function getProvides($type = null, $name = null) {
0 ignored issues
show
Best Practice introduced by
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...
575
		if ($this->provides_cache === null) {
576
			$active_plugins = $this->find('active');
577
		
578
			$provides = [];
579
	
580
			foreach ($active_plugins as $plugin) {
581
				$plugin_provides = [];
582
				$manifest = $plugin->getManifest();
583
				if ($manifest instanceof \ElggPluginManifest) {
584
					$plugin_provides = $plugin->getManifest()->getProvides();
585
				}
586
				if ($plugin_provides) {
587
					foreach ($plugin_provides as $provided) {
588
						$provides[$provided['type']][$provided['name']] = [
589
							'version' => $provided['version'],
590
							'provided_by' => $plugin->getID()
591
						];
592
					}
593
				}
594
			}
595
			
596
			$this->provides_cache = $provides;
597
		}
598
		
599
		if ($type && $name) {
2 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $name of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
600 View Code Duplication
			if (isset($this->provides_cache[$type][$name])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
601
				return $this->provides_cache[$type][$name];
602
			} else {
603
				return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Elgg\Database\Plugins::getProvides of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
604
			}
605 View Code Duplication
		} elseif ($type) {
2 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
606
			if (isset($this->provides_cache[$type])) {
607
				return $this->provides_cache[$type];
608
			} else {
609
				return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Elgg\Database\Plugins::getProvides of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
610
			}
611
		}
612
	
613
		return $this->provides_cache;
614
	}
615
	
616
	/**
617
	 * Deletes all cached data on plugins being provided.
618
	 *
619
	 * @return boolean
620
	 * @access private
621
	 */
622 1
	function invalidateProvidesCache() {
0 ignored issues
show
Best Practice introduced by
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...
623 1
		$this->provides_cache = null;
624 1
		return true;
625
	}
626
627
	/**
628
	 * Delete the cache holding whether plugins are active or not
629
	 *
630
	 * @return void
631
	 * @access private
632
	 */
633 1
	public function invalidateIsActiveCache() {
634 1
		$this->active_guids = [];
635 1
		$this->active_guids_known = false;
636 1
	}
637
	
638
	/**
639
	 * Checks if a plugin is currently providing $type and $name, and optionally
640
	 * checking a version.
641
	 *
642
	 * @param string $type       The type of the provide
643
	 * @param string $name       The name of the provide
644
	 * @param string $version    A version to check against
645
	 * @param string $comparison The comparison operator to use in version_compare()
646
	 *
647
	 * @return array An array in the form array(
648
	 * 	'status' => bool Does the provide exist?,
649
	 * 	'value' => string The version provided
650
	 * )
651
	 * @access private
652
	 */
653
	function checkProvides($type, $name, $version = null, $comparison = 'ge') {
0 ignored issues
show
Best Practice introduced by
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...
654
		$provided = $this->getProvides($type, $name);
655
		if (!$provided) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $provided of type array 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...
656
			return [
657
				'status' => false,
658
				'value' => ''
659
			];
660
		}
661
	
662
		if ($version) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $version of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
663
			$status = version_compare($provided['version'], $version, $comparison);
664
		} else {
665
			$status = true;
666
		}
667
	
668
		return [
669
			'status' => $status,
670
			'value' => $provided['version']
671
		];
672
	}
673
	
674
	/**
675
	 * Returns an array of parsed strings for a dependency in the
676
	 * format: array(
677
	 * 	'type'			=>	requires, conflicts, or provides.
678
	 * 	'name'			=>	The name of the requirement / conflict
679
	 * 	'value'			=>	A string representing the expected value: <1, >=3, !=enabled
680
	 * 	'local_value'	=>	The current value, ("Not installed")
681
	 * 	'comment'		=>	Free form text to help resovle the problem ("Enable / Search for plugin <link>")
682
	 * )
683
	 *
684
	 * @param array $dep An \ElggPluginPackage dependency array
685
	 * @return array
686
	 * @access private
687
	 */
688
	function getDependencyStrings($dep) {
0 ignored issues
show
Best Practice introduced by
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...
689
		$translator = _elgg_services()->translator;
690
		$dep_system = elgg_extract('type', $dep);
691
		$info = elgg_extract('dep', $dep);
692
		$type = elgg_extract('type', $info);
693
	
694
		if (!$dep_system || !$info || !$type) {
695
			return false;
696
		}
697
	
698
		// rewrite some of these to be more readable
699
		$comparison = elgg_extract('comparison', $info);
700
		switch ($comparison) {
701
			case 'lt':
702
				$comparison = '<';
703
				break;
704
			case 'gt':
705
				$comparison = '>';
706
				break;
707
			case 'ge':
708
				$comparison = '>=';
709
				break;
710
			case 'le':
711
				$comparison = '<=';
712
				break;
713
			default:
714
				//keep $comparison value intact
715
				break;
716
		}
717
	
718
		/*
719
		'requires'	'plugin oauth_lib'	<1.3	1.3		'downgrade'
720
		'requires'	'php setting bob'	>3		3		'change it'
721
		'conflicts'	'php setting'		>3		4		'change it'
722
		'conflicted''plugin profile'	any		1.8		'disable profile'
723
		'provides'	'plugin oauth_lib'	1.3		--		--
724
		'priority'	'before blog'		--		after	'move it'
725
		*/
726
		$strings = [];
727
		$strings['type'] = $translator->translate('ElggPlugin:Dependencies:' . ucwords($dep_system));
728
	
729
		switch ($type) {
730 View Code Duplication
			case 'elgg_release':
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
731
				// 'Elgg Version'
732
				$strings['name'] = $translator->translate('ElggPlugin:Dependencies:Elgg');
733
				$strings['expected_value'] = "$comparison {$info['version']}";
734
				$strings['local_value'] = $dep['value'];
735
				$strings['comment'] = '';
736
				break;
737
	
738 View Code Duplication
			case 'php_version':
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
739
				// 'PHP version'
740
				$strings['name'] = $translator->translate('ElggPlugin:Dependencies:PhpVersion');
741
				$strings['expected_value'] = "$comparison {$info['version']}";
742
				$strings['local_value'] = $dep['value'];
743
				$strings['comment'] = '';
744
				break;
745
			
746
			case 'php_extension':
747
				// PHP Extension %s [version]
748
				$strings['name'] = $translator->translate('ElggPlugin:Dependencies:PhpExtension', [$info['name']]);
749
				if ($info['version']) {
750
					$strings['expected_value'] = "$comparison {$info['version']}";
751
					$strings['local_value'] = $dep['value'];
752
				} else {
753
					$strings['expected_value'] = '';
754
					$strings['local_value'] = '';
755
				}
756
				$strings['comment'] = '';
757
				break;
758
	
759 View Code Duplication
			case 'php_ini':
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
760
				$strings['name'] = $translator->translate('ElggPlugin:Dependencies:PhpIni', [$info['name']]);
761
				$strings['expected_value'] = "$comparison {$info['value']}";
762
				$strings['local_value'] = $dep['value'];
763
				$strings['comment'] = '';
764
				break;
765
	
766
			case 'plugin':
767
				$strings['name'] = $translator->translate('ElggPlugin:Dependencies:Plugin', [$info['name']]);
768
				$expected = $info['version'] ? "$comparison {$info['version']}" : $translator->translate('any');
769
				$strings['expected_value'] = $expected;
770
				$strings['local_value'] = $dep['value'] ? $dep['value'] : '--';
771
				$strings['comment'] = '';
772
				break;
773
	
774
			case 'priority':
775
				$expected_priority = ucwords($info['priority']);
776
				$real_priority = ucwords($dep['value']);
777
				$strings['name'] = $translator->translate('ElggPlugin:Dependencies:Priority');
778
				$strings['expected_value'] = $translator->translate("ElggPlugin:Dependencies:Priority:$expected_priority", [$info['plugin']]);
779
				$strings['local_value'] = $translator->translate("ElggPlugin:Dependencies:Priority:$real_priority", [$info['plugin']]);
780
				$strings['comment'] = '';
781
				break;
782
		}
783
	
784
		if ($dep['type'] == 'suggests') {
785 View Code Duplication
			if ($dep['status']) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
786
				$strings['comment'] = $translator->translate('ok');
787
			} else {
788
				$strings['comment'] = $translator->translate('ElggPlugin:Dependencies:Suggests:Unsatisfied');
789
			}
790 View Code Duplication
		} else {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
791
			if ($dep['status']) {
792
				$strings['comment'] = $translator->translate('ok');
793
			} else {
794
				$strings['comment'] = $translator->translate('error');
795
			}
796
		}
797
	
798
		return $strings;
799
	}
800
	
801
	/**
802
	 * Returns an array of all plugin user settings for a user.
803
	 *
804
	 * @param int    $user_guid  The user GUID or 0 for the currently logged in user.
805
	 * @param string $plugin_id  The plugin ID (Required)
806
	 * @param bool   $return_obj Return settings as an object? This can be used to in reusable
807
	 *                           views where the settings are passed as $vars['entity'].
808
	 * @return array
809
	 * @see \ElggPlugin::getAllUserSettings()
810
	 */
811
	function getAllUserSettings($user_guid = 0, $plugin_id = null, $return_obj = false) {
0 ignored issues
show
Best Practice introduced by
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...
812
		$plugin = $this->get($plugin_id);
813
		if (!$plugin) {
814
			return false;
815
		}
816
	
817
		$settings = $plugin->getAllUserSettings((int) $user_guid);
818
	
819
		if ($settings && $return_obj) {
820
			$return = new \stdClass;
821
	
822
			foreach ($settings as $k => $v) {
823
				$return->$k = $v;
824
			}
825
	
826
			return $return;
827
		} else {
828
			return $settings;
829
		}
830
	}
831
	
832
	/**
833
	 * Set a user specific setting for a plugin.
834
	 *
835
	 * @param string $name      The name. Note: cannot be "title".
836
	 * @param mixed  $value     The value.
837
	 * @param int    $user_guid The user GUID or 0 for the currently logged in user.
838
	 * @param string $plugin_id The plugin ID (Required)
839
	 *
840
	 * @return bool
841
	 * @see \ElggPlugin::setUserSetting()
842
	 */
843 View Code Duplication
	function setUserSetting($name, $value, $user_guid = 0, $plugin_id = null) {
0 ignored issues
show
Best Practice introduced by
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...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
844
		$plugin = $this->get($plugin_id);
845
		if (!$plugin) {
846
			return false;
847
		}
848
849
		return $plugin->setUserSetting($name, $value, (int) $user_guid);
850
	}
851
	
852
	/**
853
	 * Unsets a user-specific plugin setting
854
	 *
855
	 * @param string $name      Name of the setting
856
	 * @param int    $user_guid The user GUID or 0 for the currently logged in user.
857
	 * @param string $plugin_id The plugin ID (Required)
858
	 *
859
	 * @return bool
860
	 * @see \ElggPlugin::unsetUserSetting()
861
	 */
862
	function unsetUserSetting($name, $user_guid = 0, $plugin_id = null) {
0 ignored issues
show
Best Practice introduced by
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...
863
		$plugin = $this->get($plugin_id);
864
		if (!$plugin) {
865
			return false;
866
		}
867
868
		return $plugin->unsetUserSetting($name, (int) $user_guid);
869
	}
870
	
871
	/**
872
	 * Get a user specific setting for a plugin.
873
	 *
874
	 * @param string $name      The name of the setting.
875
	 * @param int    $user_guid The user GUID or 0 for the currently logged in user.
876
	 * @param string $plugin_id The plugin ID (Required)
877
	 * @param mixed  $default   The default value to return if none is set
878
	 *
879
	 * @return mixed
880
	 * @see \ElggPlugin::getUserSetting()
881
	 */
882 View Code Duplication
	function getUserSetting($name, $user_guid = 0, $plugin_id = null, $default = null) {
0 ignored issues
show
Best Practice introduced by
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...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
883
		$plugin = $this->get($plugin_id);
884
		if (!$plugin) {
885
			return false;
886
		}
887
888
		return $plugin->getUserSetting($name, (int) $user_guid, $default);
889
	}
890
	
891
	/**
892
	 * Set a setting for a plugin.
893
	 *
894
	 * @param string $name      The name of the setting - note, can't be "title".
895
	 * @param mixed  $value     The value.
896
	 * @param string $plugin_id The plugin ID (Required)
897
	 *
898
	 * @return bool
899
	 * @see \ElggPlugin::setSetting()
900
	 */
901
	function setSetting($name, $value, $plugin_id) {
0 ignored issues
show
Best Practice introduced by
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...
902
		$plugin = $this->get($plugin_id);
903
		if (!$plugin) {
904
			return false;
905
		}
906
907
		return $plugin->setSetting($name, $value);
908
	}
909
	
910
	/**
911
	 * Get setting for a plugin.
912
	 *
913
	 * @param string $name      The name of the setting.
914
	 * @param string $plugin_id The plugin ID (Required)
915
	 * @param mixed  $default   The default value to return if none is set
916
	 *
917
	 * @return mixed
918
	 * @see \ElggPlugin::getSetting()
919
	 */
920 311
	function getSetting($name, $plugin_id, $default = null) {
0 ignored issues
show
Best Practice introduced by
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...
921 311
		$plugin = $this->get($plugin_id);
922 311
		if (!$plugin) {
923
			return false;
924
		}
925
926 311
		return $plugin->getSetting($name, $default);
927
	}
928
	
929
	/**
930
	 * Unsets a plugin setting.
931
	 *
932
	 * @param string $name      The name of the setting.
933
	 * @param string $plugin_id The plugin ID (Required)
934
	 *
935
	 * @return bool
936
	 * @see \ElggPlugin::unsetSetting()
937
	 */
938
	function unsetSetting($name, $plugin_id) {
0 ignored issues
show
Best Practice introduced by
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...
939
		$plugin = $this->get($plugin_id);
940
		if (!$plugin) {
941
			return false;
942
		}
943
944
		return $plugin->unsetSetting($name);
945
	}
946
	
947
	/**
948
	 * Unsets all plugin settings for a plugin.
949
	 *
950
	 * @param string $plugin_id The plugin ID (Required)
951
	 *
952
	 * @return bool
953
	 * @see \ElggPlugin::unsetAllSettings()
954
	 */
955
	function unsetAllSettings($plugin_id) {
0 ignored issues
show
Best Practice introduced by
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...
956
		$plugin = $this->get($plugin_id);
957
		if (!$plugin) {
958
			return false;
959
		}
960
	
961
		return $plugin->unsetAllSettings();
962
	}
963
	
964
	/**
965
	 * Returns entities based upon plugin user settings.
966
	 * Takes all the options for {@link elgg_get_entities_from_private_settings()}
967
	 * in addition to the ones below.
968
	 *
969
	 * @param array $options Array in the format:
970
	 *
971
	 * 	plugin_id => STR The plugin id. Required.
972
	 *
973
	 * 	plugin_user_setting_names => null|ARR private setting names
974
	 *
975
	 * 	plugin_user_setting_values => null|ARR metadata values
976
	 *
977
	 * 	plugin_user_setting_name_value_pairs => null|ARR (
978
	 *                                         name => 'name',
979
	 *                                         value => 'value',
980
	 *                                         'operand' => '=',
981
	 *                                        )
982
	 * 	                             Currently if multiple values are sent via
983
	 *                               an array (value => array('value1', 'value2')
984
	 *                               the pair's operand will be forced to "IN".
985
	 *
986
	 * 	plugin_user_setting_name_value_pairs_operator => null|STR The operator to use for combining
987
	 *                                        (name = value) OPERATOR (name = value); default AND
988
	 *
989
	 * @return mixed int If count, int. If not count, array. false on errors.
990
	 */
991
	function getEntitiesFromUserSettings(array $options = []) {
0 ignored issues
show
Best Practice introduced by
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...
992
		$singulars = ['plugin_user_setting_name', 'plugin_user_setting_value',
993
			'plugin_user_setting_name_value_pair'];
994
	
995
		$options = _elgg_normalize_plural_options_array($options, $singulars);
996
	
997
		// rewrite plugin_user_setting_name_* to the right PS ones.
998
		$map = [
999
			'plugin_user_setting_names' => 'private_setting_names',
1000
			'plugin_user_setting_values' => 'private_setting_values',
1001
			'plugin_user_setting_name_value_pairs' => 'private_setting_name_value_pairs',
1002
			'plugin_user_setting_name_value_pairs_operator' => 'private_setting_name_value_pairs_operator',
1003
		];
1004
	
1005
		foreach ($map as $plugin => $private) {
1006
			if (!isset($options[$plugin])) {
1007
				continue;
1008
			}
1009
	
1010
			if (isset($options[$private])) {
1011
				if (!is_array($options[$private])) {
1012
					$options[$private] = [$options[$private]];
1013
				}
1014
	
1015
				$options[$private] = array_merge($options[$private], $options[$plugin]);
1016
			} else {
1017
				$options[$private] = $options[$plugin];
1018
			}
1019
		}
1020
	
1021
		$prefix = $this->namespacePrivateSetting('user_setting', '', $options['plugin_id']);
1022
		$options['private_setting_name_prefix'] = $prefix;
1023
	
1024
		return elgg_get_entities_from_private_settings($options);
1025
	}
1026
}
1027