Api   D
last analyzed

Complexity

Total Complexity 138

Size/Duplication

Total Lines 758
Duplicated Lines 4.62 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 77.64%

Importance

Changes 0
Metric Value
dl 35
loc 758
ccs 316
cts 407
cp 0.7764
rs 4.4444
c 0
b 0
f 0
wmc 138
lcom 1
cbo 2

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getRole() 0 4 2
A hasRole() 0 3 1
A setRole() 0 19 4
A unsetRole() 0 3 1
A getAll() 0 11 3
A getSelectable() 0 6 1
A getPermissions() 0 11 4
C cachePermissions() 0 36 11
A getRoleByName() 0 4 2
A filterName() 0 11 4
C createFromConfig() 7 49 8
A checkUpdate() 0 11 2
A prepareMenuVars() 0 9 2
B replaceDynamicPaths() 0 30 4
A matchPath() 0 13 3
A checkContext() 0 14 4
A getReservedRoleNames() 0 3 1
A isReservedRoleName() 0 3 1
D setupViews() 0 31 9
A supressView() 0 3 1
C setupHooks() 14 47 12
C setupEvents() 14 48 11
A actionGatekeeper() 0 12 4
C pageGatekeeper() 0 29 8
C setupMenu() 0 73 21
D cleanMenu() 0 43 13

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 Api 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 Api, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Elgg\Roles;
4
5
use ElggMenuItem;
6
use ElggRole;
7
use ElggUser;
8
9
class Api {
10
11
	const DEFAULT_ROLE = 'default';
12
	const ADMIN_ROLE = 'admin';
13
	const VISITOR_ROLE = 'visitor';
14
	const NO_ROLE = '_no_role_';
15
	const DENY = 'deny';
16
	const ALLOW = 'allow';
17
	const REPLACE = 'replace';
18
	const EXTEND = 'extend';
19
	const REDIRECT = 'redirect';
20
	const FORWARD = 'forward';
21
22
	/**
23
	 * @var DbInterface
24
	 */
25
	private $db;
26
27
	/**
28
	 * Permissions cache
29
	 * @var array
30
	 */
31
	private $cache;
32
33
	/**
34
	 * Roles cache
35
	 * @var ElggRole[]
36
	 */
37
	private $roles;
38
39
	/**
40
	 * Constructor
41
	 */
42 41
	public function __construct(DbInterface $db) {
43 41
		$this->cache = array();
44 41
		$this->db = $db;
45 41
	}
46
47
	/**
48
	 * Obtains the role of a given user
49
	 *
50
	 * @param ElggUser $user User entity
51
	 * @return ElggRole The role the user belongs to
52
	 */
53 1
	public function getRole(ElggUser $user) {
54 1
		$role = $this->db->getUserRole($user);
55 1
		return $role ? : $this->getRoleByName($this->filterName(self::NO_ROLE, $user));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $role ?: $this->getRoleB...self::NO_ROLE, $user)); of type ElggRole|false adds false to the return on line 55 which is incompatible with the return type documented by Elgg\Roles\Api::getRole of type ElggRole. It seems like you forgot to handle an error condition.
Loading history...
56
	}
57
58
	/**
59
	 * Checks if the user has a specific role
60
	 *
61
	 * @param ElggUser $user User entity
62
	 * @return bool True if the user belongs to the passed role, false otherwise
63
	 */
64
	public function hasRole(ElggUser $user, $role_name = self::DEFAULT_ROLE) {
65
		return $this->getRole($user)->name == $role_name;
66
	}
67
68
	/**
69
	 * Assigns a role to a particular user
70
	 *
71
	 * @param ElggUser $user The user the role needs to be assigned to
72
	 * @param ElggRole $role The role to be assigned
73
	 * @return bool|void True if the role change was successful, false if could not update user role, and null if there was no change in user role
74
	 */
75 1
	public function setRole(ElggUser $user, ElggRole $role) {
76
77 1
		$current_role = $this->getRole($user);
78 1
		if ($role->name == $current_role->name) {
79
			// There was no change necessary, old and new role are the same
80
			return;
81
		}
82
83 1
		if (!$this->unsetRole($user)) {
84
			return false;
85
		}
86
87 1
		if ($role->isReservedRole()) {
88
			// Changed to reserved role which is resolved without relationships
89 1
			return true;
90
		}
91
92 1
		return $this->db->setUserRole($user, $role);
93
	}
94
95
	/**
96
	 * Clear user roles
97
	 * 
98
	 * @param ElggUser $user User entity
99
	 * @return bool
100
	 */
101 1
	public function unsetRole(ElggUser $user) {
102 1
		return $this->db->unsetUserRole($user);
103
	}
104
105
	/**
106
	 * Gets all role objects
107
	 * @return ElggRole[]|false An array of ElggRole objects defined in the system, or false if none found
108
	 */
109 27
	public function getAll() {
110 27
		if (!isset($this->roles)) {
111 27
			$this->roles = array();
112 27
			$roles = $this->db->getAllRoles();
113 27
			foreach ($roles as $role) {
114
				/* @var $role ElggRole */
115 27
				$this->roles[$role->name] = $role;
116 27
			}
117 27
		}
118 27
		return $this->roles;
119
	}
120
121
	/**
122
	 * Gets all non-default role objects
123
	 *
124
	 * This is used by the role selector view. Default roles (VISITOR_ROLE, ADMIN_ROLE, DEFAULT_ROLE) need to be omitted from
125
	 * the list of selectable roles - as default roles are automatically assigned to users based on their Elgg membership type
126
	 *
127
	 * @return ElggRole[]|false An array of non-default ElggRole objects defined in the system, or false if none found
128
	 */
129 1
	public function getSelectable() {
130 1
		$roles = $this->getAll();
131 1
		return array_filter($roles, function(ElggRole $role) {
132 1
			return !$role->isReservedRole();
133 1
		});
134
	}
135
136
	/**
137
	 * Obtains a list of permissions associated with a particular role object
138
	 *
139
	 * @param ElggRole $role            The role to check for permissions
140
	 * @param string   $permission_type The section from the configuration array ('actions', 'menus', 'views', etc.)
141
	 * @return array The permission rules for the given role and permission type
142
	 */
143 24
	public function getPermissions(ElggRole $role, $permission_type = null) {
144 24
		if (!isset($this->cache[$role->name])) {
145 24
			$this->cachePermissions($role);
146 24
		}
147
148 24
		if ($permission_type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $permission_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...
149 24
			return (isset($this->cache[$role->name][$permission_type])) ? (array) $this->cache[$role->name][$permission_type] : array();
150
		} else {
151
			return (array) $this->cache[$role->name];
152
		}
153
	}
154
155
	/**
156
	 * Caches permissions associated with a role object. Also resolves all role extensions.
157
	 *
158
	 * @param ElggRole $role The role to cache permissions for
159
	 * @return void
160
	 */
161 24
	public function cachePermissions(ElggRole $role) {
162 24
		if (empty($this->cache[$role->name])) {
163 24
			$this->cache[$role->name] = array();
164 24
		}
165
166
		// Let' start by processing role extensions
167 24
		$extends = $role->getExtends();
168 24
		if (!empty($extends)) {
169 17
			foreach ($extends as $extended_role_name) {
170 17
				$extended_role = $this->getRoleByName($extended_role_name);
171 17
				if (!isset($this->cache[$extended_role->name])) {
172 17
					$this->cachePermissions($extended_role);
0 ignored issues
show
Security Bug introduced by
It seems like $extended_role defined by $this->getRoleByName($extended_role_name) on line 170 can also be of type false; however, Elgg\Roles\Api::cachePermissions() does only seem to accept object<ElggRole>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
173 17
				}
174
175 17
				foreach ($this->cache[$extended_role->name] as $type => $permission_rules) {
176 17
					if (empty($this->cache[$role->name][$type])) {
177 17
						$this->cache[$role->name][$type] = array();
178 17
					}
179 17
					if (is_array($this->cache[$role->name][$type])) {
180 17
						$this->cache[$role->name][$type] = array_merge($this->cache[$role->name][$type], $permission_rules);
181 17
					} else {
182
						$this->cache[$role->name][$type] = $permission_rules;
183
					}
184 17
				}
185 17
			}
186 17
		}
187
188 24
		$permissions = $role->getPermissions();
189 24
		foreach ($permissions as $type => $permission_rules) {
190 24
			if (isset($this->cache[$role->name][$type]) && is_array($this->cache[$role->name][$type])) {
191 17
				$this->cache[$role->name][$type] = array_merge($this->cache[$role->name][$type], $permission_rules);
192 17
			} else {
193 24
				$this->cache[$role->name][$type] = $permission_rules;
194
			}
195 24
		}
196 24
	}
197
198
	/**
199
	 * Gets a role object based on it's name
200
	 *
201
	 * @param string $role_name The name of the role
202
	 * @return ElggRole|false An ElggRole object if it could be found based on the name, false otherwise
203
	 */
204 27
	public function getRoleByName($role_name = '') {
205 27
		$roles = $this->getAll();
206 27
		return isset($roles[$role_name]) ? $roles[$role_name] : false;
207
	}
208
209
	/**
210
	 * Resolves the default role for specified or currently logged in user
211
	 *
212
	 * @param string    $role_name The name of the user's role
213
	 * @param ElggUser $user      User whose default role needs to be resolved
214
	 * @return string
215
	 */
216 2
	public function filterName($role_name, ElggUser $user = null) {
217 2
		if ($role_name !== self::NO_ROLE) {
218 1
			return $role_name;
219
		}
220
221 2
		if ($user instanceof ElggUser) {
0 ignored issues
show
Bug introduced by
The class ElggUser does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
222 2
			return $user->isAdmin() ? self::ADMIN_ROLE : self::DEFAULT_ROLE;
223
		}
224
225 1
		return self::VISITOR_ROLE;
226
	}
227
228
	/**
229
	 * Processes the configuration files and generates the appropriate ElggRole objects.
230
	 *
231
	 * If, for any role definition, there is an already existing role with the same name,
232
	 * the role permissions will be updated for the given role object.
233
	 * If there is no previously existing, corresponding role object, it will be created now.
234
	 *
235
	 * @param array $roles_array The roles configuration array
236
	 * @return void
237
	 */
238
	public function createFromConfig($roles_array) {
239
240
		elgg_log('Creating roles from config', 'DEBUG');
241
242
		$existing_roles = $this->getAll();
243
244
		foreach ($roles_array as $rname => $rdetails) {
245
			$current_role = $existing_roles[$rname];
246
			if ($current_role instanceof ElggRole) {
247
				elgg_log("Role '$rname' already exists; updating permissions", 'DEBUG');
248
				// Update existing role obejct
249
				$current_role->title = $rdetails['title'];
250
				$current_role->setExtends($rdetails['extends']);
251
				$current_role->setPermissions($rdetails['permissions']);
252 View Code Duplication
				if ($current_role->save()) {
0 ignored issues
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...
253
					elgg_log("Permissions for role '$rname' have been updated: " . print_r($rdetails['permissions'], true), 'DEBUG');
254
				}
255
			} else {
256
				elgg_log("Creating a new role '$rname'", 'DEBUG');
257
				// Create new role object
258
				$new_role = new ElggRole();
259
				$new_role->title = $rdetails['title'];
260
				$new_role->owner_guid = elgg_get_logged_in_user_guid();
261
				$new_role->container_guid = $new_role->owner_guid;
262
				$new_role->access_id = ACCESS_PUBLIC;
263
				if (!($new_role->save())) {
264
					elgg_log("Could not create new role '$rname'", 'DEBUG');
265
				} else {
266
					// Add metadata
267
					$new_role->name = $rname;
268
					$new_role->setExtends($rdetails['extends']);
269
					$new_role->setPermissions($rdetails['permissions']);
270 View Code Duplication
					if ($new_role->save()) {
0 ignored issues
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...
271
						elgg_log("Role object with guid $new_role->guid has been created", 'DEBUG');
272
						elgg_log("Permissions for '$rname' have been set: " . print_r($rdetails['permissions'], true), 'DEBUG');
273
					}
274
				}
275
			}
276
		}
277
278
		// remove old roles
279
		$config_roles = array_keys($roles_array);
280
		foreach ($existing_roles as $name => $role) {
281
			if (!in_array($name, $config_roles)) {
282
				elgg_log("Deleting role '$rname'");
0 ignored issues
show
Bug introduced by
The variable $rname seems to be defined by a foreach iteration on line 244. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
283
				$role->delete();
284
			}
285
		}
286
	}
287
288
	/**
289
	 * Checks if the configuration array has been updated and updates role objects accordingly if needed
290
	 * @return void
291
	 */
292
	public function checkUpdate() {
293
		$hash = elgg_get_plugin_setting('roles_hash', 'roles');
294
		$roles_array = elgg_trigger_plugin_hook('roles:config', 'role', array(), null);
295
296
		$current_hash = sha1(serialize($roles_array));
297
298
		if ($hash != $current_hash) {
299
			roles_create_from_config($roles_array);
300
			elgg_set_plugin_setting('roles_hash', $current_hash, 'roles');
301
		}
302
	}
303
304
	/**
305
	 * Substitutes dynamic parts of a menu's target URL
306
	 *
307
	 * @param array $vars An associative array holding the menu permissions
308
	 * @return The substituted menu permission array
309
	 */
310 2
	public function prepareMenuVars($vars) {
311
312 2
		$prepared_vars = $vars;
313 2
		if (isset($prepared_vars['href'])) {
314 2
			$prepared_vars['href'] = $this->replaceDynamicPaths($prepared_vars['href']);
315 2
		}
316
317 2
		return $prepared_vars;
318
	}
319
320
	/**
321
	 * Replaces certain parts of path and URL type definitions with dynamic values
322
	 *
323
	 * @param string $str The string to operate on
324
	 * @return string The updated, substituted string
325
	 */
326 10
	public function replaceDynamicPaths($str) {
327 10
		$res = $str;
328 10
		$user = elgg_get_logged_in_user_entity();
329 10
		if ($user instanceof ElggUser) {
0 ignored issues
show
Bug introduced by
The class ElggUser does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
330
			$self_username = $user->username;
331
			$self_guid = $user->guid;
332
			$role = roles_get_role($user);
333
334
			$res = str_replace('{$self_username}', $self_username, $str);
335
			$res = str_replace('{$self_guid}', $self_guid, $res);
336
			if ($role instanceof ElggRole) {
337
				$res = str_replace('{$self_rolename}', $role->name, $res);
338
			}
339
		}
340
341
		// Safe way to get hold of the page owner before system, ready event
342 10
		$pageowner_guid = elgg_trigger_plugin_hook('page_owner', 'system', NULL, 0);
343 10
		$pageowner = get_entity($pageowner_guid);
344
345 10
		if ($pageowner instanceof ElggUser) {
0 ignored issues
show
Bug introduced by
The class ElggUser does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
346
			$pageowner_username = $pageowner->username;
347
			$pageowner_role = roles_get_role($pageowner);
348
349
			$res = str_replace('{$pageowner_name}', $pageowner_username, $res);
350
			$res = str_replace('{$pageowner_guid}', $pageowner_guid, $res);
351
			$res = str_replace('{$pageowner_rolename}', $pageowner_role->name, $res);
352
		}
353
354 10
		return $res;
355
	}
356
357
	/**
358
	 * Checks if a path or URL type rule matches a given path. Also processes regular expressions
359
	 *
360
	 * @param string $rule The permission rule to check
361
	 * @param string $path The path to match against
362
	 * @return bool True if the rule matches the path, false otherwise
363
	 */
364 15
	public function matchPath($rule, $path) {
365 15
		if (preg_match('/^regexp\((.+)\)$/', $rule) > 0) {
366
			// The rule contains regular expression; use regexp matching for the current path
367 2
			$pattern = preg_replace('/^regexp\(/', '', $rule);
368 2
			$pattern = preg_replace('/\)$/', '', $pattern);
369 2
		} else {
370 13
			$rule = trim($rule, '/');
371 13
			$wwwroot = elgg_get_config('wwwroot') ? : elgg_get_site_url();
372 13
			$pattern = "`^{$wwwroot}$rule/*$`i";
373 13
			$path = elgg_normalize_url($path);
374
		}
375 15
		return (bool) preg_match($pattern, $path);
376
	}
377
378
	/**
379
	 * Checks if a permission rule should be executed for the current context
380
	 *
381
	 * @param string  $permission_details The permission rule configuration
382
	 * @param boolean $strict             If strict context matching should be used.
383
	 * 							          If true, only the last context will be checked for the rule matching.
384
	 * 							          If false, any context value in the context stack will be considered.
385
	 * @return bool True if the rule should be executed, false otherwise
386
	 */
387 6
	public function checkContext($permission_details, $strict = false) {
388 6
		$context = elgg_extract('context', $permission_details);
389 6
		if (!isset($context)) {
390 4
			return true;
391
		}
392 2
		if (!is_array($context)) {
393 2
			$context = array($context);
394 2
		}
395 2
		if ($strict) {
396 1
			return in_array(elgg_get_context(), $context);
397
		}
398 1
		$stack = (array) elgg_get_context_stack();
399 1
		return count(array_intersect($context, $stack)) > 0;
400
	}
401
402
	/**
403
	 * Gets all reserved role names
404
	 * @return array The list of reserved role names
405
	 */
406 4
	public function getReservedRoleNames() {
407 4
		return array(self::DEFAULT_ROLE, self::ADMIN_ROLE, self::VISITOR_ROLE);
408
	}
409
410
	/**
411
	 *
412
	 * Checks if a role name is reserved in the system
413
	 *
414
	 * @param string $role_name The name of the role to check
415
	 * @return boolean True if the passed $role_name is a reserved role name
416
	 */
417 3
	public function isReservedRoleName($role_name) {
418 3
		return in_array($role_name, $this->getReservedRoleNames());
419
	}
420
421
	/**
422
	 * Setup views for a given role
423
	 * 
424
	 * @param ElggRole $role Role
425
	 * @return void
426
	 */
427 4
	public function setupViews(\ElggRole $role) {
428
429 4
		$role_perms = $this->getPermissions($role, 'views');
430 4
		foreach ($role_perms as $view => $perm_details) {
431 4
			switch ($perm_details['rule']) {
432
433 4
				case self::DENY:
434 2
					elgg_register_plugin_hook_handler('view', $view, array($this, 'supressView'));
435 2
					break;
436
437 3
				case self::EXTEND:
438 1
					$params = $perm_details['view_extension'];
439 1
					$view_extension = $this->replaceDynamicPaths($params['view']);
440 1
					$priority = isset($params['priority']) ? $params['priority'] : 501;
441 1
					$viewtype = isset($params['viewtype']) ? $params['viewtype'] : '';
442 1
					elgg_extend_view($view, $view_extension, $priority, $viewtype);
443 1
					break;
444
445 2
				case self::REPLACE:
446 1
					$params = $perm_details['view_replacement'];
447 1
					$location = elgg_get_root_path() . $this->replaceDynamicPaths($params['location']);
448 1
					$viewtype = isset($params['viewtype']) ? $params['viewtype'] : '';
449 1
					elgg_set_view_location($view, $location, $viewtype);
450 1
					break;
451
452 1
				case self::ALLOW:
453 1
					elgg_unregister_plugin_hook_handler('view', $view, array($this, 'supressView'));
454 1
					break;
455 4
			}
456 4
		}
457 4
	}
458
459
	/**
460
	 * Supresses view output
461
	 * @return string
462
	 */
463 1
	public function supressView() {
464 1
		return '';
465
	}
466
467
	/**
468
	 * Setup hooks for a given role
469
	 * 
470
	 * @param ElggRole $role Role object
471
	 * @return void
472
	 */
473 4
	public function setupHooks(\ElggRole $role) {
474 4
		$role_perms = $this->getPermissions($role, 'hooks');
475 4
		foreach ($role_perms as $hook => $perm_details) {
476 4
			list($hook_name, $type) = explode('::', $hook);
477 4
			if (!$type) {
478
				$type = 'all';
479
			}
480 4
			switch ($perm_details['rule']) {
481 4
				case self::DENY:
482 1
					$params = elgg_extract('hook', $perm_details);
483 1
					if (isset($params['handler'])) {
484
						$handler = $params['handler'];
485
						elgg_unregister_plugin_hook_handler($hook_name, $type, $handler);
486
					} else {
487 1
						if (is_callable('elgg_clear_plugin_hook_handlers')) {
488 1
							elgg_clear_plugin_hook_handlers($hook_name, $type);
489 1
						} else {
490
							$handlers = _elgg_services()->hooks->getOrderedHandlers($hook_name, $type);
491
							foreach ($handlers as $handler) {
492
								elgg_unregister_plugin_hook_handler($hook_name, $type, $handler);
493
							}
494
						}
495
					}
496 1
					break;
497
498 3 View Code Duplication
				case self::EXTEND:
0 ignored issues
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...
499 1
					$params = $perm_details['hook'];
500 1
					$handler = $params['handler'];
501 1
					$priority = isset($params['priority']) ? $params['priority'] : 500;
502 1
					elgg_register_plugin_hook_handler($hook_name, $type, $handler, $priority);
503 1
					break;
504
505 2 View Code Duplication
				case self::REPLACE:
0 ignored issues
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...
506 1
					$params = $perm_details['hook'];
507 1
					$old_handler = $params['old_handler'];
508 1
					$new_handler = $params['new_handler'];
509 1
					$priority = isset($params['priority']) ? $params['priority'] : 500;
510 1
					elgg_unregister_plugin_hook_handler($hook_name, $type, $old_handler);
511 1
					elgg_register_plugin_hook_handler($hook_name, $type, $new_handler, $priority);
512 1
					break;
513
514 1
				case self::ALLOW:
515 1
				default:
516 1
					break;
517 4
			}
518 4
		}
519 4
	}
520
521
	/**
522
	 * Setup events for a given role
523
	 * 
524
	 * @param ElggRole $role Role object
525
	 * @return void
526
	 */
527 4
	function setupEvents(\ElggRole $role) {
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...
528
529 4
		$role_perms = $this->getPermissions($role, 'events');
530 4
		foreach ($role_perms as $event => $perm_details) {
531
532 4
			list($event_name, $type) = explode('::', $event);
533 4
			if (!$type) {
534
				$type = 'all';
535
			}
536
537 4
			switch ($perm_details['rule']) {
538
539 4
				case self::DENY:
540 1
					$params = elgg_extract('event', $perm_details);
541 1
					if (isset($params['handler'])) {
542
						$handler = $params['handler'];
543
						elgg_unregister_event_handler($event_name, $type, $handler);
544
					} else {
545
						// @TODO: Update when https://github.com/Elgg/Elgg/issues/9113 is fixed
546 1
						$handlers = _elgg_services()->events->getOrderedHandlers($event_name, $type);
547 1
						foreach ($handlers as $handler) {
548 1
							elgg_unregister_event_handler($event_name, $type, $handler);
549 1
						}
550
					}
551 1
					break;
552
553 3 View Code Duplication
				case self::EXTEND:
0 ignored issues
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...
554 1
					$params = elgg_extract('event', $perm_details);
555 1
					$handler = $params['handler'];
556 1
					$priority = isset($params['priority']) ? $params['priority'] : 500;
557 1
					elgg_register_event_handler($event_name, $type, $handler, $priority);
558 1
					break;
559
560 2 View Code Duplication
				case self::REPLACE:
0 ignored issues
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...
561 1
					$params = elgg_extract('event', $perm_details);
562 1
					$old_handler = $params['old_handler'];
563 1
					$new_handler = $params['new_handler'];
564 1
					$priority = isset($params['priority']) ? $params['priority'] : 500;
565 1
					elgg_unregister_event_handler($event_name, $type, $old_handler);
566 1
					elgg_register_event_handler($event_name, $type, $new_handler, $priority);
567 1
					break;
568
569 1
				case self::ALLOW:
570 1
				default:
571 1
					break;
572 4
			}
573 4
		}
574 4
	}
575
576
	/**
577
	 * Check action access permissions for a given role
578
	 * 
579
	 * @param ElggRole $role   Role object
580
	 * @param string   $action Registered action name
581
	 * @return boolean|void
582
	 */
583 2
	function actionGatekeeper(\ElggRole $role, $action = '') {
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...
584 2
		$role_perms = $this->getPermissions($role, 'actions');
585 2
		foreach ($role_perms as $rule_name => $perm_details) {
586 2
			if (!$this->matchPath($this->replaceDynamicPaths($rule_name), $action)) {
587 2
				continue;
588
			}
589 2
			switch ($perm_details['rule']) {
590 2
				case self::DENY:
591 1
					return false;
592 1
			}
593 2
		}
594 2
	}
595
596
	/**
597
	 * Check page access permissions for a given role
598
	 * 
599
	 * @param ElggRole $role Role object
600
	 * @param string   $path URL path
601
	 * @return array
602
	 */
603 3
	function pageGatekeeper(\ElggRole $role, $path = '') {
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...
604 3
		$forward = false;
605 3
		$error = false;
606
607 3
		$role_perms = $this->getPermissions($role, 'pages');
608 3
		foreach ($role_perms as $page => $perm_details) {
609
610 3
			if (!$this->matchPath($this->replaceDynamicPaths($page), $path)) {
611 3
				continue;
612
			}
613
614 3
			switch ($perm_details['rule']) {
615
616 3
				case self::DENY:
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
617 1
					$error = true;
618
619 3
				case self::REDIRECT:
620 3
				case self::REPLACE:
621 3
				case self::FORWARD:
622 2
					$forward = elgg_extract('forward', $perm_details, REFERRER);
623 2
					break;
624 3
			}
625 3
		}
626
627
		return array(
628 3
			'forward' => is_string($forward) ? $this->replaceDynamicPaths($forward) : $forward,
629 3
			'error' => $error,
630 3
		);
631
	}
632
633
	/**
634
	 * Setup menu
635
	 *
636
	 * @param ElggRole       $role      Role object
637
	 * @param string         $menu_name Menu name
638
	 * @param ElggMenuItem[] $menu      Menu items
639
	 * @return ElggMenuItem[]
640
	 */
641 4
	public function setupMenu(\ElggRole $role, $menu_name = '', $menu = array()) {
642
643 4
		$role_perms = $this->getPermissions($role, 'menus');
644
645 4
		foreach ($role_perms as $menu_rule => $perm_details) {
646 4
			if (empty($menu_rule)) {
647
				continue;
648
			}
649
650 4
			$menu_rule_parts = explode('::', $menu_rule);
651 4
			if (elgg_extract(0, $menu_rule_parts) != $menu_name) {
652 4
				continue;
653
			}
654 4
			$menu_item_name = elgg_extract(1, $menu_rule_parts);
655
656
			// Check if this rule relates to the currently triggered menu and if we're in the right context for the current rule
657 4
			if (!$this->checkContext($perm_details)) {
658
				continue;
659
			}
660
661
			// Try to act on this permission rule
662 4
			switch ($perm_details['rule']) {
663 4
				case self::DENY:
664 1
					if (!$menu_item_name) {
665 1
						$menu = array();
666 1
					} else {
667 1
						foreach ($menu as $key => $item) {
668 1
							if (!$item instanceof ElggMenuItem) {
0 ignored issues
show
Bug introduced by
The class ElggMenuItem does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
669 1
								continue;
670
							}
671 1
							if ($item->getName() == $menu_item_name || $item->getParentName() == $menu_item_name) {
672 1
								unset($menu[$key]);
673 1
							}
674 1
						}
675
					}
676 1
					break;
677
678 3
				case self::EXTEND:
679 1
					$menu_item = elgg_extract('menu_item', $perm_details);
680 1
					if ($menu_item) {
681 1
						$menu[] = ElggMenuItem::factory($this->prepareMenuVars($menu_item));
682 1
					}
683 1
					break;
684
685 2
				case self::REPLACE:
686 1
					$menu_item = elgg_extract('menu_item', $perm_details);
687 1
					if (!$menu_item_name || !$menu_item) {
688
						break;
689
					}
690
691 1
					$new_item = ElggMenuItem::factory($this->prepareMenuVars($menu_item));
692 1
					foreach ($menu as $key => $item) {
693 1
						if (!$item instanceof ElggMenuItem) {
0 ignored issues
show
Bug introduced by
The class ElggMenuItem does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
694 1
							continue;
695
						}
696 1
						if ($item->getName() == $menu_item_name) {
697 1
							$menu[$key] = $new_item;
698 1
						} else if ($item->getParentName() == $menu_item_name) {
699 1
							$item->setParentName($new_item->getName());
700 1
							$menu[$key] = $item;
701 1
						}
702 1
					}
703 1
					break;
704
705 1
				case self::ALLOW:
706 1
				default:
707 1
					break;
708 4
			}
709 4
		}
710
711
		// Return the updated menu to the hook triggering function (elgg_view_menu)
712 4
		return $menu;
713
	}
714
715
	/**
716
	 * Remove items that link to denied pages and actions
717
	 * 
718
	 * @param ElggRole       $role Role object
719
	 * @param ElggMenuItem[] $menu Menu
720
	 * @return ElggMenuItem[]
721
	 */
722 1
	public function cleanMenu(\ElggRole $role, $menu = array()) {
723
724 1
		$pages = $this->getPermissions($role, 'pages');
725 1
		$actions = $this->getPermissions($role, 'actions');
726
727 1
		$paths = array();
728 1
		foreach ($pages as $rule => $opts) {
729
			if ($opts['rule'] == self::DENY) {
730
				$paths[] = $this->replaceDynamicPaths($rule);
731
			}
732 1
		}
733 1
		foreach ($actions as $rule => $opts) {
734 1
			if ($opts['rule'] == self::DENY) {
735 1
				$paths[] = $this->replaceDynamicPaths("action/$rule");
736 1
			}
737 1
		}
738
739 1
		$remove = array();
740
		// Unregister menu items that link to pages and actions that have been denied
741 1
		foreach ($menu as $key => $item) {
742 1
			if (!$item instanceof ElggMenuItem) {
0 ignored issues
show
Bug introduced by
The class ElggMenuItem does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
743
				continue;
744
			}
745
746 1
			$href = $item->getHref();
747 1
			foreach ($paths as $path) {
748 1
				if ($this->matchPath($path, $href)) {
749 1
					$remove[] = $item->getName();
750 1
				}
751 1
			}
752 1
		}
753
754 1
		foreach ($menu as $key => $item) {
755 1
			if (!$item instanceof ElggMenuItem) {
0 ignored issues
show
Bug introduced by
The class ElggMenuItem does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
756
				continue;
757
			}
758 1
			if (in_array($item->getName(), $remove) || in_array($item->getParentName(), $remove)) {
759 1
				unset($menu[$key]);
760 1
			}
761 1
		}
762
763 1
		return $menu;
764
	}
765
766
}
767