ElggPlugin   F
last analyzed

Complexity

Total Complexity 125

Size/Duplication

Total Lines 1020
Duplicated Lines 4.61 %

Coupling/Cohesion

Components 1
Dependencies 18

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 47
loc 1020
ccs 0
cts 502
cp 0
rs 1.52
c 0
b 0
f 0
wmc 125
lcom 1
cbo 18

41 Methods

Rating   Name   Duplication   Size   Complexity  
A initializeAttributes() 0 8 1
B __construct() 0 43 6
A save() 0 17 4
A getID() 0 3 1
A getFriendlyName() 0 8 2
A getPath() 0 3 1
A setID() 0 3 1
A getAvailableTextFiles() 0 12 3
A getPriority() 0 4 1
D setPriority() 0 69 14
A getSetting() 0 4 2
A getAllSettings() 0 28 4
A setSetting() 0 15 2
A unsetSetting() 0 3 1
A unsetAllSettings() 0 12 1
A getUserSetting() 0 18 4
A getAllUserSettings() 0 38 5
A setUserSetting() 0 28 3
A unsetUserSetting() 0 18 3
A unsetAllUserSettings() 10 10 1
A unsetAllUsersSettings() 9 9 1
A isValid() 0 18 4
A isActive() 0 17 4
A canActivate() 0 12 4
B activate() 0 43 7
A deactivate() 0 26 5
A start() 0 27 5
A getConfigWrapper() 0 9 2
A includeFile() 5 17 3
A canReadFile() 0 3 1
A registerViews() 5 7 2
A registerLanguages() 5 17 3
A registerClasses() 0 9 2
A __get() 0 26 4
A get() 0 4 1
A __set() 13 13 4
A set() 0 6 1
A setStatus() 0 25 5
A getError() 0 3 1
A getManifest() 0 14 3
A getPackage() 0 14 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ElggPlugin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ElggPlugin, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Stores site-side plugin settings as private data.
4
 *
5
 * This class is currently a stub, allowing a plugin to
6
 * save settings in an object's private settings for each site.
7
 *
8
 * @package    Elgg.Core
9
 * @subpackage Plugins.Settings
10
 */
11
class ElggPlugin extends \ElggObject {
12
	private $package;
13
	private $manifest;
14
15
	private $path;
16
	private $errorMsg = '';
17
18
	/**
19
	 * Set subtype to 'plugin'
20
	 *
21
	 * @return void
22
	 */
23
	protected function initializeAttributes() {
24
		parent::initializeAttributes();
25
26
		$this->attributes['subtype'] = "plugin";
27
28
		// plugins must be public.
29
		$this->access_id = ACCESS_PUBLIC;
30
	}
31
32
	/**
33
	 * Creates a new plugin from path
34
	 *
35
	 * @note Internal: also supports database objects
36
	 *
37
	 * @warning Unlike other \ElggEntity objects, you cannot null instantiate
38
	 *          \ElggPlugin. You must provide the path to the plugin directory.
39
	 *
40
	 * @param string $path The absolute path of the plugin
41
	 *
42
	 * @throws PluginException
43
	 */
44
	public function __construct($path) {
45
		if (!$path) {
46
			throw new \PluginException("ElggPlugin cannot be null instantiated. You must pass a full path.");
47
		}
48
49
		if (is_object($path)) {
50
			// database object
51
			parent::__construct($path);
52
			$this->path = _elgg_services()->config->getPluginsPath() . $this->getID();
53
		} else if (is_numeric($path)) {
54
			// guid
55
			// @todo plugins with directory names of '12345'
56
			elgg_deprecated_notice("Use elgg_get_plugin_from_id() to load a plugin.", 1.9);
57
			parent::__construct($path);
0 ignored issues
show
Documentation introduced by
$path is of type integer|double|string, but the function expects a object<stdClass>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
58
			$this->path = _elgg_services()->config->getPluginsPath() . $this->getID();
59
		} else {
60
			$this->initializeAttributes();
61
			
62
			$mod_dir = _elgg_services()->config->getPluginsPath();
63
64
			// not a full path, so assume a directory name and use the default path
65
			if (strpos($path, $mod_dir) !== 0) {
66
				elgg_deprecated_notice("You should pass a full path to ElggPlugin.", 1.9);
67
				$path = $mod_dir . $path;
68
			}
69
70
			// path checking is done in the package
71
			$path = sanitise_filepath($path);
72
			$this->path = $path;
73
			$path_parts = explode('/', rtrim($path, '/'));
74
			$plugin_id = array_pop($path_parts);
75
			$this->title = $plugin_id;
76
77
			// check if we're loading an existing plugin
78
			$existing_plugin = elgg_get_plugin_from_id($plugin_id);
79
			
80
			if ($existing_plugin) {
81
				$this->load($existing_plugin->guid);
82
			}
83
		}
84
85
		_elgg_cache_plugin_by_id($this);
86
	}
87
88
	/**
89
	 * Save the plugin object.  Make sure required values exist.
90
	 *
91
	 * @see \ElggObject::save()
92
	 * @return bool
93
	 */
94
	public function save() {
95
		// own by the current site so users can be deleted without affecting plugins
96
		$site = _elgg_services()->configTable->get('site');
97
		$this->attributes['site_guid'] = $site->guid;
98
		$this->attributes['owner_guid'] = $site->guid;
99
		$this->attributes['container_guid'] = $site->guid;
100
		
101
		if (parent::save()) {
102
			// make sure we have a priority
103
			$priority = $this->getPriority();
104
			if ($priority === false || $priority === null) {
105
				return $this->setPriority('last');
106
			}
107
		} else {
108
			return false;
109
		}
110
	}
111
112
113
	// Plugin ID and path
114
115
	/**
116
	 * Returns the ID (dir name) of this plugin
117
	 *
118
	 * @return string
119
	 */
120
	public function getID() {
121
		return $this->title;
122
	}
123
124
	/**
125
	 * Returns the manifest's name if available, otherwise the ID.
126
	 *
127
	 * @return string
128
	 * @since 1.8.1
129
	 */
130
	public function getFriendlyName() {
131
		$manifest = $this->getManifest();
132
		if ($manifest) {
133
			return $manifest->getName();
134
		}
135
136
		return $this->getID();
137
	}
138
139
	/**
140
	 * Returns the plugin's full path with trailing slash.
141
	 *
142
	 * @return string
143
	 */
144
	public function getPath() {
145
		return sanitise_filepath($this->path);
146
	}
147
148
	/**
149
	 * Sets the location of this plugin.
150
	 *
151
	 * @param string $id The path to the plugin's dir.
152
	 * @return bool
153
	 */
154
	public function setID($id) {
155
		return $this->attributes['title'] = $id;
156
	}
157
158
	/**
159
	 * Returns an array of available markdown files for this plugin
160
	 *
161
	 * @return array
162
	 */
163
	public function getAvailableTextFiles() {
164
		$filenames = $this->getPackage()->getTextFilenames();
165
166
		$files = array();
167
		foreach ($filenames as $filename) {
168
			if ($this->canReadFile($filename)) {
169
				$files[$filename] = "$this->path/$filename";
170
			}
171
		}
172
173
		return $files;
174
	}
175
176
	// Load Priority
177
178
	/**
179
	 * Gets the plugin's load priority.
180
	 *
181
	 * @return int
182
	 */
183
	public function getPriority() {
184
		$name = _elgg_namespace_plugin_private_setting('internal', 'priority');
185
		return $this->$name;
186
	}
187
188
	/**
189
	 * Sets the priority of the plugin
190
	 *
191
	 * @param mixed $priority  The priority to set. One of +1, -1, first, last, or a number.
192
	 *                         If given a number, this will displace all plugins at that number
193
	 *                         and set their priorities +1
194
	 * @param mixed $site_guid Optional site GUID.
195
	 * @return bool
196
	 */
197
	public function setPriority($priority, $site_guid = null) {
198
		if (!$this->guid) {
199
			return false;
200
		}
201
202
		$db_prefix = _elgg_services()->configTable->get('dbprefix');
203
		$name = _elgg_namespace_plugin_private_setting('internal', 'priority');
204
		// if no priority assume a priority of 1
205
		$old_priority = (int) $this->getPriority();
206
		$old_priority = (!$old_priority) ? 1 : $old_priority;
207
		$max_priority = _elgg_get_max_plugin_priority();
208
209
		// can't use switch here because it's not strict and
210
		// php evaluates +1 == 1
211
		if ($priority === '+1') {
212
			$priority = $old_priority + 1;
213
		} elseif ($priority === '-1') {
214
			$priority = $old_priority - 1;
215
		} elseif ($priority === 'first') {
216
			$priority = 1;
217
		} elseif ($priority === 'last') {
218
			$priority = $max_priority;
219
		}
220
221
		// should be a number by now
222
		if ($priority > 0) {
223
			if (!is_numeric($priority)) {
224
				return false;
225
			}
226
227
			// there's nothing above the max.
228
			if ($priority > $max_priority) {
229
				$priority = $max_priority;
230
			}
231
232
			// there's nothing below 1.
233
			if ($priority < 1) {
234
				$priority = 1;
235
			}
236
237
			if ($priority > $old_priority) {
238
				$op = '-';
239
				$where = "CAST(value as unsigned) BETWEEN $old_priority AND $priority";
240
			} else {
241
				$op = '+';
242
				$where = "CAST(value as unsigned) BETWEEN $priority AND $old_priority";
243
			}
244
245
			// displace the ones affected by this change
246
			$q = "UPDATE {$db_prefix}private_settings
247
				SET value = CAST(value as unsigned) $op 1
248
				WHERE entity_guid != $this->guid
249
				AND name = '$name'
250
				AND $where";
251
252
			if (!$this->getDatabase()->updateData($q)) {
253
				return false;
254
			}
255
256
			// set this priority
257
			if ($this->setPrivateSetting($name, $priority)) {
258
				return true;
259
			} else {
260
				return false;
261
			}
262
		}
263
264
		return false;
265
	}
266
267
268
	// Plugin settings
269
270
	/**
271
	 * Returns a plugin setting
272
	 *
273
	 * @param string $name    The setting name
274
	 * @param mixed  $default The default value to return if none is set
275
	 * @return mixed
276
	 */
277
	public function getSetting($name, $default = null) {
278
		$val = $this->$name;
279
		return $val !== null ? $val : $default;
280
	}
281
282
	/**
283
	 * Returns an array of all settings saved for this plugin.
284
	 *
285
	 * @note Unlike user settings, plugin settings are not namespaced.
286
	 *
287
	 * @return array An array of key/value pairs.
288
	 */
289
	public function getAllSettings() {
290
		if (!$this->guid) {
291
			return false;
292
		}
293
294
		$db_prefix = _elgg_services()->config->get('dbprefix');
295
		// need to remove all namespaced private settings.
296
		$us_prefix = _elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
297
		$is_prefix = _elgg_namespace_plugin_private_setting('internal', '', $this->getID());
298
299
		// Get private settings for user
300
		$q = "SELECT * FROM {$db_prefix}private_settings
301
			WHERE entity_guid = $this->guid
302
			AND name NOT LIKE '$us_prefix%'
303
			AND name NOT LIKE '$is_prefix%'";
304
305
		$private_settings = $this->getDatabase()->getData($q);
306
307
		$return = array();
308
309
		if ($private_settings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $private_settings 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...
310
			foreach ($private_settings as $setting) {
311
				$return[$setting->name] = $setting->value;
312
			}
313
		}
314
315
		return $return;
316
	}
317
318
	/**
319
	 * Set a plugin setting for the plugin
320
	 *
321
	 * @todo This will only work once the plugin has a GUID.
322
	 *
323
	 * @param string $name  The name to set
324
	 * @param string $value The value to set
325
	 *
326
	 * @return bool
327
	 */
328
	public function setSetting($name, $value) {
329
		if (!$this->guid) {
330
			return false;
331
		}
332
		
333
		// Hook to validate setting
334
		$value = elgg_trigger_plugin_hook('setting', 'plugin', array(
335
			'plugin_id' => $this->getID(),
336
			'plugin' => $this,
337
			'name' => $name,
338
			'value' => $value,
339
		), $value);
340
		
341
		return $this->setPrivateSetting($name, $value);
342
	}
343
344
	/**
345
	 * Removes a plugin setting name and value.
346
	 *
347
	 * @param string $name The setting name to remove
348
	 *
349
	 * @return bool
350
	 */
351
	public function unsetSetting($name) {
352
		return remove_private_setting($this->guid, $name);
353
	}
354
355
	/**
356
	 * Removes all settings for this plugin.
357
	 *
358
	 * @todo Should be a better way to do this without dropping to raw SQL.
359
	 * @todo If we could namespace the plugin settings this would be cleaner.
360
	 * @todo this shouldn't work because ps_prefix will be empty string
361
	 * @return bool
362
	 */
363
	public function unsetAllSettings() {
364
		$db_prefix = _elgg_services()->configTable->get('dbprefix');
365
		$us_prefix = _elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
366
		$is_prefix = _elgg_namespace_plugin_private_setting('internal', '', $this->getID());
367
368
		$q = "DELETE FROM {$db_prefix}private_settings
369
			WHERE entity_guid = $this->guid
370
			AND name NOT LIKE '$us_prefix%'
371
			AND name NOT LIKE '$is_prefix%'";
372
373
		return $this->getDatabase()->deleteData($q);
374
	}
375
376
377
	// User settings
378
379
	/**
380
	 * Returns a user's setting for this plugin
381
	 *
382
	 * @param string $name      The setting name
383
	 * @param int    $user_guid The user GUID
384
	 * @param mixed  $default   The default value to return if none is set
385
	 *
386
	 * @return mixed The setting string value, the default value or false if there is no user
387
	 */
388
	public function getUserSetting($name, $user_guid = 0, $default = null) {
389
		$user_guid = (int)$user_guid;
390
391
		if ($user_guid) {
392
			$user = get_entity($user_guid);
393
		} else {
394
			$user = _elgg_services()->session->getLoggedInUser();
395
		}
396
397
		if (!($user instanceof \ElggUser)) {
398
			return false;
399
		}
400
401
		$name = _elgg_namespace_plugin_private_setting('user_setting', $name, $this->getID());
402
		
403
		$val = get_private_setting($user->guid, $name);
404
		return $val !== null ? $val : $default;
405
	}
406
407
	/**
408
	 * Returns an array of all user settings saved for this plugin for the user.
409
	 *
410
	 * @note Plugin settings are saved with a prefix. This removes that prefix.
411
	 *
412
	 * @param int $user_guid The user GUID. Defaults to logged in.
413
	 * @return array An array of key/value pairs.
414
	 */
415
	public function getAllUserSettings($user_guid = 0) {
416
		$user_guid = (int)$user_guid;
417
418
		if ($user_guid) {
419
			$user = get_entity($user_guid);
420
		} else {
421
			$user = _elgg_services()->session->getLoggedInUser();
422
		}
423
424
		if (!($user instanceof \ElggUser)) {
425
			return false;
426
		}
427
428
		$db_prefix = _elgg_services()->config->get('dbprefix');
429
		// send an empty name so we just get the first part of the namespace
430
		$ps_prefix = _elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
431
		$ps_prefix_len = strlen($ps_prefix);
432
433
		// Get private settings for user
434
		$q = "SELECT * FROM {$db_prefix}private_settings
435
			WHERE entity_guid = {$user->guid}
436
			AND name LIKE '$ps_prefix%'";
437
438
		$private_settings = $this->getDatabase()->getData($q);
439
440
		$return = array();
441
442
		if ($private_settings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $private_settings 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...
443
			foreach ($private_settings as $setting) {
444
				$name = substr($setting->name, $ps_prefix_len);
445
				$value = $setting->value;
446
447
				$return[$name] = $value;
448
			}
449
		}
450
451
		return $return;
452
	}
453
454
	/**
455
	 * Sets a user setting for a plugin
456
	 *
457
	 * @param string $name      The setting name
458
	 * @param string $value     The setting value
459
	 * @param int    $user_guid The user GUID
460
	 *
461
	 * @return mixed The new setting ID or false
462
	 */
463
	public function setUserSetting($name, $value, $user_guid = 0) {
464
		$user_guid = (int)$user_guid;
465
466
		if ($user_guid) {
467
			$user = get_entity($user_guid);
468
		} else {
469
			$user = _elgg_services()->session->getLoggedInUser();
470
		}
471
472
		if (!($user instanceof \ElggUser)) {
473
			return false;
474
		}
475
476
		// Hook to validate setting
477
		// note: this doesn't pass the namespaced name
478
		$value = _elgg_services()->hooks->trigger('usersetting', 'plugin', array(
479
			'user' => $user,
480
			'plugin' => $this,
481
			'plugin_id' => $this->getID(),
482
			'name' => $name,
483
			'value' => $value
484
		), $value);
485
486
		// set the namespaced name.
487
		$name = _elgg_namespace_plugin_private_setting('user_setting', $name, $this->getID());
488
489
		return set_private_setting($user->guid, $name, $value);
490
	}
491
492
	/**
493
	 * Removes a user setting name and value.
494
	 *
495
	 * @param string $name      The user setting name
496
	 * @param int    $user_guid The user GUID
497
	 * @return bool
498
	 */
499
	public function unsetUserSetting($name, $user_guid = 0) {
500
		$user_guid = (int)$user_guid;
501
502
		if ($user_guid) {
503
			$user = get_entity($user_guid);
504
		} else {
505
			$user = _elgg_services()->session->getLoggedInUser();
506
		}
507
508
		if (!($user instanceof \ElggUser)) {
509
			return false;
510
		}
511
512
		// set the namespaced name.
513
		$name = _elgg_namespace_plugin_private_setting('user_setting', $name, $this->getID());
514
515
		return remove_private_setting($user->guid, $name);
516
	}
517
518
	/**
519
	 * Removes all User Settings for this plugin for a particular user
520
	 *
521
	 * Use {@link removeAllUsersSettings()} to remove all user
522
	 * settings for all users.  (Note the plural 'Users'.)
523
	 *
524
	 * @warning 0 does not equal logged in user for this method!
525
	 * @todo fix that
526
	 *
527
	 * @param int $user_guid The user GUID to remove user settings.
528
	 * @return bool
529
	 */
530 View Code Duplication
	public function unsetAllUserSettings($user_guid) {
0 ignored issues
show
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...
531
		$db_prefix = _elgg_services()->configTable->get('dbprefix');
532
		$ps_prefix = _elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
533
534
		$q = "DELETE FROM {$db_prefix}private_settings
535
			WHERE entity_guid = $user_guid
536
			AND name LIKE '$ps_prefix%'";
537
538
		return $this->getDatabase()->deleteData($q);
539
	}
540
541
	/**
542
	 * Removes this plugin's user settings for all users.
543
	 *
544
	 * Use {@link removeAllUserSettings()} if you just want to remove
545
	 * settings for a single user.
546
	 *
547
	 * @return bool
548
	 */
549 View Code Duplication
	public function unsetAllUsersSettings() {
0 ignored issues
show
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...
550
		$db_prefix = _elgg_services()->configTable->get('dbprefix');
551
		$ps_prefix = _elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
552
553
		$q = "DELETE FROM {$db_prefix}private_settings
554
			WHERE name LIKE '$ps_prefix%'";
555
556
		return $this->getDatabase()->deleteData($q);
557
	}
558
559
560
	// validation
561
562
	/**
563
	 * Returns if the plugin is complete, meaning has all required files
564
	 * and Elgg can read them and they make sense.
565
	 *
566
	 * @todo bad name? This could be confused with isValid() from \ElggPluginPackage.
567
	 *
568
	 * @return bool
569
	 */
570
	public function isValid() {
571
		if (!$this->getID()) {
572
			$this->errorMsg = _elgg_services()->translator->translate('ElggPlugin:MissingID', array($this->guid));
573
			return false;
574
		}
575
576
		if (!$this->getPackage() instanceof \ElggPluginPackage) {
577
			$this->errorMsg = _elgg_services()->translator->translate('ElggPlugin:NoPluginPackagePackage', array($this->getID(), $this->guid));
578
			return false;
579
		}
580
581
		if (!$this->getPackage()->isValid()) {
582
			$this->errorMsg = $this->getPackage()->getError();
583
			return false;
584
		}
585
586
		return true;
587
	}
588
589
	/**
590
	 * Is this plugin active?
591
	 *
592
	 * @param int $site_guid Optional site guid.
593
	 * @return bool
594
	 */
595
	public function isActive($site_guid = null) {
596
		if (!$this->guid) {
597
			return false;
598
		}
599
600
		if ($site_guid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $site_guid of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
601
			$site = get_entity($site_guid);
602
		} else {
603
			$site = _elgg_services()->configTable->get('site');
604
		}
605
606
		if (!($site instanceof \ElggSite)) {
607
			return false;
608
		}
609
610
		return check_entity_relationship($this->guid, 'active_plugin', $site->guid);
611
	}
612
613
	/**
614
	 * Checks if this plugin can be activated on the current
615
	 * Elgg installation.
616
	 *
617
	 * @todo remove $site_guid param or implement it
618
	 *
619
	 * @param mixed $site_guid Optional site guid
620
	 * @return bool
621
	 */
622
	public function canActivate($site_guid = null) {
623
		if ($this->getPackage()) {
624
			$result = $this->getPackage()->isValid() && $this->getPackage()->checkDependencies();
625
			if (!$result) {
626
				$this->errorMsg = $this->getPackage()->getError();
627
			}
628
629
			return $result;
630
		}
631
632
		return false;
633
	}
634
635
636
	// activating and deactivating
637
638
	/**
639
	 * Actives the plugin for the current site.
640
	 *
641
	 * @param mixed $site_guid Optional site GUID.
642
	 * @return bool
643
	 */
644
	public function activate($site_guid = null) {
645
		if ($this->isActive($site_guid)) {
646
			return false;
647
		}
648
649
		if (!$this->canActivate()) {
650
			return false;
651
		}
652
653
		// set in the db, now perform tasks and emit events
654
		if ($this->setStatus(true, $site_guid)) {
655
			// emit an event. returning false will make this not be activated.
656
			// we need to do this after it's been fully activated
657
			// or the deactivate will be confused.
658
			$params = array(
659
				'plugin_id' => $this->getID(),
660
				'plugin_entity' => $this,
661
			);
662
663
			$return = _elgg_services()->events->trigger('activate', 'plugin', $params);
664
665
			// if there are any on_enable functions, start the plugin now and run them
666
			// Note: this will not run re-run the init hooks!
667
			if ($return) {
668
				if ($this->canReadFile('activate.php')) {
669
					$flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_CLASSES |
670
							ELGG_PLUGIN_REGISTER_LANGUAGES | ELGG_PLUGIN_REGISTER_VIEWS;
671
672
					$this->start($flags);
673
674
					$return = $this->includeFile('activate.php');
675
				}
676
			}
677
678
			if ($return === false) {
679
				$this->deactivate($site_guid);
680
			}
681
682
			return $return;
683
		}
684
685
		return false;
686
	}
687
688
	/**
689
	 * Deactivates the plugin.
690
	 *
691
	 * @param mixed $site_guid Optional site GUID.
692
	 * @return bool
693
	 */
694
	public function deactivate($site_guid = null) {
695
		if (!$this->isActive($site_guid)) {
696
			return false;
697
		}
698
699
		// emit an event. returning false will cause this to not be deactivated.
700
		$params = array(
701
			'plugin_id' => $this->getID(),
702
			'plugin_entity' => $this,
703
		);
704
705
		$return = _elgg_services()->events->trigger('deactivate', 'plugin', $params);
706
707
		// run any deactivate code
708
		if ($return) {
709
			if ($this->canReadFile('deactivate.php')) {
710
				$return = $this->includeFile('deactivate.php');
711
			}
712
		}
713
714
		if ($return === false) {
715
			return false;
716
		} else {
717
			return $this->setStatus(false, $site_guid);
718
		}
719
	}
720
721
	/**
722
	 * Start the plugin.
723
	 *
724
	 * @param int $flags Start flags for the plugin. See the constants in lib/plugins.php for details.
725
	 * @return true
726
	 * @throws PluginException
727
	 */
728
	public function start($flags) {
729
		//if (!$this->canActivate()) {
730
		//	return false;
731
		//}
732
733
		// include classes
734
		if ($flags & ELGG_PLUGIN_REGISTER_CLASSES) {
735
			$this->registerClasses();
736
		}
737
		
738
		// include start file
739
		if ($flags & ELGG_PLUGIN_INCLUDE_START) {
740
			$this->includeFile('start.php');
741
		}
742
743
		// include views
744
		if ($flags & ELGG_PLUGIN_REGISTER_VIEWS) {
745
			$this->registerViews();
746
		}
747
748
		// include languages
749
		if ($flags & ELGG_PLUGIN_REGISTER_LANGUAGES) {
750
			$this->registerLanguages();
751
		}
752
753
		return true;
754
	}
755
756
757
	// start helpers
758
759
	/**
760
	 * Get the config object in a deprecation wrapper
761
	 *
762
	 * @return \Elgg\DeprecationWrapper
763
	 */
764
	protected static function getConfigWrapper() {
765
		static $wrapper;
766
		if (null === $wrapper) {
767
			global $CONFIG;
768
			$warning = 'Do not rely on local $CONFIG being available in start.php';
769
			$wrapper = new \Elgg\DeprecationWrapper($CONFIG, $warning, "1.10");
770
		}
771
		return $wrapper;
772
	}
773
774
	/**
775
	 * Includes one of the plugins files
776
	 *
777
	 * @param string $filename The name of the file
778
	 *
779
	 * @throws PluginException
780
	 * @return mixed The return value of the included file (or 1 if there is none)
781
	 */
782
	protected function includeFile($filename) {
783
		// This needs to be here to be backwards compatible for 1.0-1.7.
784
		// They expect the global config object to be available in start.php.
785
		if ($filename == 'start.php') {
786
			$CONFIG = self::getConfigWrapper();
787
		}
788
789
		$filepath = "$this->path/$filename";
790
791 View Code Duplication
		if (!$this->canReadFile($filename)) {
792
			$msg = _elgg_services()->translator->translate('ElggPlugin:Exception:CannotIncludeFile',
793
							array($filename, $this->getID(), $this->guid, $this->path));
794
			throw new \PluginException($msg);
795
		}
796
797
		return include $filepath;
798
	}
799
800
	/**
801
	 * Checks whether a plugin file with the given name exists
802
	 *
803
	 * @param string $filename The name of the file
804
	 * @return bool
805
	 */
806
	protected function canReadFile($filename) {
807
		return is_readable($this->path . '/' . $filename);
808
	}
809
810
	/**
811
	 * Registers the plugin's views
812
	 *
813
	 * @throws PluginException
814
	 * @return void
815
	 */
816
	protected function registerViews() {
817 View Code Duplication
		if (!_elgg_services()->views->registerPluginViews($this->path, $failed_dir)) {
818
			$msg = _elgg_services()->translator->translate('ElggPlugin:Exception:CannotRegisterViews',
819
				array($this->getID(), $this->guid, $failed_dir));
820
			throw new \PluginException($msg);
821
		}
822
	}
823
824
	/**
825
	 * Registers the plugin's languages
826
	 *
827
	 * @throws PluginException
828
	 * @return true
829
	 */
830
	protected function registerLanguages() {
831
		$languages_path = "$this->path/languages";
832
833
		// don't need to have classes
834
		if (!is_dir($languages_path)) {
835
			return true;
836
		}
837
838
		// but need to have working ones.
839 View Code Duplication
		if (!_elgg_services()->translator->registerTranslations($languages_path)) {
840
			$msg = _elgg_services()->translator->translate('ElggPlugin:Exception:CannotRegisterLanguages',
841
							array($this->getID(), $this->guid, $languages_path));
842
			throw new \PluginException($msg);
843
		}
844
845
		return true;
846
	}
847
848
	/**
849
	 * Registers the plugin's classes
850
	 *
851
	 * @throws PluginException
852
	 * @return true
853
	 */
854
	protected function registerClasses() {
855
		$classes_path = "$this->path/classes";
856
857
		if (is_dir($classes_path)) {
858
			_elgg_services()->autoloadManager->addClasses($classes_path);
859
		}
860
861
		return true;
862
	}
863
864
	/**
865
	 * Get an attribute or private setting value
866
	 *
867
	 * @param string $name Name of the attribute or private setting
868
	 * @return mixed
869
	 */
870
	public function __get($name) {
871
		// rewrite for old and inaccurate plugin:setting
872
		if (strstr($name, 'plugin:setting:')) {
873
			$msg = 'Direct access of user settings is deprecated. Use ElggPlugin->getUserSetting()';
874
			elgg_deprecated_notice($msg, 1.8);
875
			$name = str_replace('plugin:setting:', '', $name);
876
			$name = _elgg_namespace_plugin_private_setting('user_setting', $name, $this->getID());
877
		}
878
879
		// See if its in our base attribute
880
		if (array_key_exists($name, $this->attributes)) {
881
			return $this->attributes[$name];
882
		}
883
884
		// @todo clean below - getPrivateSetting() should return null now
885
		// No, so see if its in the private data store.
886
		// get_private_setting() returns false if it doesn't exist
887
		$meta = $this->getPrivateSetting($name);
888
889
		if ($meta === false) {
890
			// Can't find it, so return null
891
			return null;
892
		}
893
894
		return $meta;
895
	}
896
897
	/**
898
	 * Get a value from private settings.
899
	 *
900
	 * @param string $name Name
901
	 * @return mixed
902
	 * @deprecated 1.9
903
	 */
904
	public function get($name) {
905
		elgg_deprecated_notice("Use -> instead of get()", 1.9);
906
		return $this->__get($name);
907
	}
908
909
	/**
910
	 * Set a value as private setting or attribute.
911
	 *
912
	 * Attributes include title and description.
913
	 *
914
	 * @param string $name  Name of the attribute or private_setting
915
	 * @param mixed  $value Value to be set
916
	 * @return void
917
	 */
918 View Code Duplication
	public function __set($name, $value) {
0 ignored issues
show
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...
919
		if (array_key_exists($name, $this->attributes)) {
920
			// Check that we're not trying to change the guid!
921
			if ((array_key_exists('guid', $this->attributes)) && ($name == 'guid')) {
922
				return;
923
			}
924
925
			$this->attributes[$name] = $value;
926
		} else {
927
			// to make sure we trigger the correct hooks
928
			$this->setSetting($name, $value);
929
		}
930
	}
931
932
	/**
933
	 * Save a value as private setting or attribute.
934
	 *
935
	 * Attributes include title and description.
936
	 *
937
	 * @param string $name  Name
938
	 * @param mixed  $value Value
939
	 * @return bool
940
	 */
941
	public function set($name, $value) {
942
		elgg_deprecated_notice("Use -> instead of set()", 1.9);
943
		$this->__set($name, $value);
944
945
		return true;
946
	}
947
948
	/**
949
	 * Sets the plugin to active or inactive for $site_guid.
950
	 *
951
	 * @param bool  $active    Set to active or inactive
952
	 * @param mixed $site_guid Int for specific site, null for current site.
953
	 *
954
	 * @return bool
955
	 */
956
	private function setStatus($active, $site_guid = null) {
957
		if (!$this->guid) {
958
			return false;
959
		}
960
961
		if ($site_guid) {
962
			$site = get_entity($site_guid);
963
964
			if (!($site instanceof \ElggSite)) {
965
				return false;
966
			}
967
		} else {
968
			$site = _elgg_services()->configTable->get('site');
969
		}
970
971
		if ($active) {
972
			$result = add_entity_relationship($this->guid, 'active_plugin', $site->guid);
973
		} else {
974
			$result = remove_entity_relationship($this->guid, 'active_plugin', $site->guid);
975
		}
976
977
		_elgg_invalidate_plugins_provides_cache();
978
979
		return $result;
980
	}
981
982
	/**
983
	 * Returns the last error message registered.
984
	 *
985
	 * @return string|null
986
	 */
987
	public function getError() {
988
		return $this->errorMsg;
989
	}
990
991
	/**
992
	 * Returns this plugin's \ElggPluginManifest object
993
	 *
994
	 * @return \ElggPluginManifest
995
	 */
996
	public function getManifest() {
997
		if ($this->manifest instanceof \ElggPluginManifest) {
998
			return $this->manifest;
999
		}
1000
1001
		try {
1002
			$this->manifest = $this->getPackage()->getManifest();
1003
		} catch (Exception $e) {
1004
			_elgg_services()->logger->warn("Failed to load manifest for plugin $this->guid. " . $e->getMessage());
1005
			$this->errorMsg = $e->getmessage();
1006
		}
1007
1008
		return $this->manifest;
1009
	}
1010
1011
	/**
1012
	 * Returns this plugin's \ElggPluginPackage object
1013
	 *
1014
	 * @return \ElggPluginPackage
1015
	 */
1016
	public function getPackage() {
1017
		if ($this->package instanceof \ElggPluginPackage) {
1018
			return $this->package;
1019
		}
1020
1021
		try {
1022
			$this->package = new \ElggPluginPackage($this->path, false);
1023
		} catch (Exception $e) {
1024
			_elgg_services()->logger->warn("Failed to load package for $this->guid. " . $e->getMessage());
1025
			$this->errorMsg = $e->getmessage();
1026
		}
1027
1028
		return $this->package;
1029
	}
1030
}
1031