AccessCollections   F
last analyzed

Complexity

Total Complexity 96

Size/Duplication

Total Lines 832
Duplicated Lines 6.61 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 0.83%

Importance

Changes 0
Metric Value
dl 55
loc 832
ccs 3
cts 363
cp 0.0083
rs 1.768
c 0
b 0
f 0
wmc 96
lcom 1
cbo 9

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
B getAccessList() 0 33 7
D getAccessArray() 14 82 15
D getWhereSql() 0 74 12
A hasAccessToEntity() 0 28 4
B getWriteAccessArray() 0 51 9
B canEdit() 0 22 6
B create() 0 37 7
B update() 0 26 8
A delete() 0 22 2
A get() 0 12 1
A addUser() 0 32 4
A removeUser() 0 29 4
A getEntityCollections() 20 20 3
A getMembers() 0 26 4
A getCollectionsByMember() 21 21 3
B getReadableAccessLevel() 0 37 6

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

1
<?php
2
3
namespace Elgg\Database;
4
5
/**
6
 * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
7
 *
8
 * @access private
9
 *
10
 * @package    Elgg.Core
11
 * @subpackage Database
12
 * @since      1.10.0
13
 */
14
class AccessCollections {
15
	/**
16
	 * @var int
17
	 */
18
	private $site_guid;
19
20
	/**
21
	 * Constructor
22
	 *
23
	 * @param int $site_guid The GUID of the default Elgg site
24
	 */
25 1
	public function __construct($site_guid) {
26 1
		$this->site_guid = $site_guid;
27 1
	}
28
29
	/**
30
	 * Return a string of access_ids for $user_guid appropriate for inserting into an SQL IN clause.
31
	 *
32
	 * @uses get_access_array
33
	 *
34
	 * @see get_access_array()
35
	 *
36
	 * @param int  $user_guid User ID; defaults to currently logged in user
37
	 * @param int  $site_guid Site ID; defaults to current site
38
	 * @param bool $flush     If set to true, will refresh the access list from the
39
	 *                        database rather than using this function's cache.
40
	 *
41
	 * @return string A list of access collections suitable for using in an SQL call
42
	 * @access private
43
	 */
44
	function getAccessList($user_guid = 0, $site_guid = 0, $flush = false) {
45
		global $init_finished;
46
		$cache = _elgg_services()->accessCache;
47
		
48
		if ($flush) {
49
			$cache->clear();
50
		}
51
	
52
		if ($user_guid == 0) {
53
			$user_guid = _elgg_services()->session->getLoggedInUserGuid();
54
		}
55
	
56
		if (($site_guid == 0) && $this->site_guid) {
57
			$site_guid = $this->site_guid;
58
		}
59
		$user_guid = (int) $user_guid;
60
		$site_guid = (int) $site_guid;
61
	
62
		$hash = $user_guid . $site_guid . 'get_access_list';
63
	
64
		if ($cache[$hash]) {
65
			return $cache[$hash];
66
		}
67
		
68
		$access_array = get_access_array($user_guid, $site_guid, $flush);
69
		$access = "(" . implode(",", $access_array) . ")";
70
	
71
		if ($init_finished) {
72
			$cache[$hash] = $access;
73
		}
74
		
75
		return $access;
76
	}
77
	
78
	/**
79
	 * Returns an array of access IDs a user is permitted to see.
80
	 *
81
	 * Can be overridden with the 'access:collections:read', 'user' plugin hook.
82
	 * @warning A callback for that plugin hook needs to either not retrieve data
83
	 * from the database that would use the access system (triggering the plugin again)
84
	 * or ignore the second call. Otherwise, an infinite loop will be created.
85
	 *
86
	 * This returns a list of all the collection ids a user owns or belongs
87
	 * to plus public and logged in access levels. If the user is an admin, it includes
88
	 * the private access level.
89
	 *
90
	 * @internal this is only used in core for creating the SQL where clause when
91
	 * retrieving content from the database. The friends access level is handled by
92
	 * _elgg_get_access_where_sql().
93
	 *
94
	 * @see get_write_access_array() for the access levels that a user can write to.
95
	 *
96
	 * @param int  $user_guid User ID; defaults to currently logged in user
97
	 * @param int  $site_guid Site ID; defaults to current site
98
	 * @param bool $flush     If set to true, will refresh the access ids from the
99
	 *                        database rather than using this function's cache.
100
	 *
101
	 * @return array An array of access collections ids
102
	 */
103
	function getAccessArray($user_guid = 0, $site_guid = 0, $flush = false) {
104
		global $init_finished;
105
	
106
		$cache = _elgg_services()->accessCache;
107
	
108
		if ($flush) {
109
			$cache->clear();
110
		}
111
	
112
		if ($user_guid == 0) {
113
			$user_guid = _elgg_services()->session->getLoggedInUserGuid();
114
		}
115
	
116
		if (($site_guid == 0) && $this->site_guid) {
117
			$site_guid = $this->site_guid;
118
		}
119
	
120
		$user_guid = (int) $user_guid;
121
		$site_guid = (int) $site_guid;
122
	
123
		$hash = $user_guid . $site_guid . 'get_access_array';
124
	
125
		if ($cache[$hash]) {
126
			$access_array = $cache[$hash];
127
		} else {
128
			$access_array = array(ACCESS_PUBLIC);
129
	
130
			// The following can only return sensible data for a known user.
131
			if ($user_guid) {
132
				$db = _elgg_services()->db;
133
				$prefix = $db->getTablePrefix();
134
135
				$access_array[] = ACCESS_LOGGED_IN;
136
	
137
				// Get ACL memberships
138
				$query = "SELECT am.access_collection_id"
139
					. " FROM {$prefix}access_collection_membership am"
140
					. " LEFT JOIN {$prefix}access_collections ag ON ag.id = am.access_collection_id"
141
					. " WHERE am.user_guid = $user_guid AND (ag.site_guid = $site_guid OR ag.site_guid = 0)";
142
	
143
				$collections = $db->getData($query);
144 View Code Duplication
				if ($collections) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collections 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...
145
					foreach ($collections as $collection) {
146
						if (!empty($collection->access_collection_id)) {
147
							$access_array[] = (int)$collection->access_collection_id;
148
						}
149
					}
150
				}
151
	
152
				// Get ACLs owned.
153
				$query = "SELECT ag.id FROM {$prefix}access_collections ag ";
154
				$query .= "WHERE ag.owner_guid = $user_guid AND (ag.site_guid = $site_guid OR ag.site_guid = 0)";
155
	
156
				$collections = $db->getData($query);
157 View Code Duplication
				if ($collections) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collections 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...
158
					foreach ($collections as $collection) {
159
						if (!empty($collection->id)) {
160
							$access_array[] = (int)$collection->id;
161
						}
162
					}
163
				}
164
	
165
				$ignore_access = elgg_check_access_overrides($user_guid);
166
	
167
				if ($ignore_access == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
168
					$access_array[] = ACCESS_PRIVATE;
169
				}
170
			}
171
	
172
			if ($init_finished) {
173
				$cache[$hash] = $access_array;
174
			}
175
		}
176
	
177
		$options = array(
178
			'user_id' => $user_guid,
179
			'site_id' => $site_guid
180
		);
181
	
182
		// see the warning in the docs for this function about infinite loop potential
183
		return _elgg_services()->hooks->trigger('access:collections:read', 'user', $options, $access_array);
184
	}
185
	
186
	/**
187
	 * Returns the SQL where clause for enforcing read access to data.
188
	 *
189
	 * Note that if this code is executed in privileged mode it will return (1=1).
190
	 * 
191
	 * Otherwise it returns a where clause to retrieve the data that a user has
192
	 * permission to read.
193
	 *
194
	 * Plugin authors can hook into the 'get_sql', 'access' plugin hook to modify,
195
	 * remove, or add to the where clauses. The plugin hook will pass an array with the current
196
	 * ors and ands to the function in the form:
197
	 *  array(
198
	 *      'ors' => array(),
199
	 *      'ands' => array()
200
	 *  )
201
	 *
202
	 * The results will be combined into an SQL where clause in the form:
203
	 *  ((or1 OR or2 OR orN) AND (and1 AND and2 AND andN))
204
	 * 
205
	 * @param array $options Array in format:
206
	 *
207
	 * 	table_alias => STR Optional table alias. This is based on the select and join clauses.
208
	 *                     Default is 'e'. 
209
	 *
210
	 *  user_guid => INT Optional GUID for the user that we are retrieving data for.
211
	 *                   Defaults to the logged in user.
212
	 * 
213
	 *  use_enabled_clause => BOOL Optional. Should we append the enabled clause? The default 
214
	 *                             is set by access_show_hidden_entities().
215
	 * 
216
	 *  access_column => STR Optional access column name. Default is 'access_id'.
217
	 * 
218
	 *  owner_guid_column => STR Optional owner_guid column. Default is 'owner_guid'.
219
	 * 
220
	 *  guid_column => STR Optional guid_column. Default is 'guid'.
221
	 * 
222
	 * @return string
223
	 * @access private
224
	 */
225
	function getWhereSql(array $options = array()) {
226
		global $ENTITY_SHOW_HIDDEN_OVERRIDE;
227
	
228
		$defaults = array(
229
			'table_alias' => 'e',
230
			'user_guid' => _elgg_services()->session->getLoggedInUserGuid(),
231
			'use_enabled_clause' => !$ENTITY_SHOW_HIDDEN_OVERRIDE,
232
			'access_column' => 'access_id',
233
			'owner_guid_column' => 'owner_guid',
234
			'guid_column' => 'guid',
235
		);
236
	
237
		$options = array_merge($defaults, $options);
238
	
239
		// just in case someone passes a . at the end
240
		$options['table_alias'] = rtrim($options['table_alias'], '.');
241
	
242
		foreach (array('table_alias', 'access_column', 'owner_guid_column', 'guid_column') as $key) {
243
			$options[$key] = sanitize_string($options[$key]);
244
		}
245
		$options['user_guid'] = sanitize_int($options['user_guid'], false);
246
	
247
		// only add dot if we have an alias or table name
248
		$table_alias = $options['table_alias'] ? $options['table_alias'] . '.' : '';
249
	
250
		$options['ignore_access'] = elgg_check_access_overrides($options['user_guid']);
251
	
252
		$clauses = array(
253
			'ors' => array(),
254
			'ands' => array()
255
		);
256
257
		$prefix = _elgg_services()->db->getTablePrefix();
258
	
259
		if ($options['ignore_access']) {
260
			$clauses['ors'][] = '1 = 1';
261
		} else if ($options['user_guid']) {
262
			// include content of user's friends
263
			$clauses['ors'][] = "$table_alias{$options['access_column']} = " . ACCESS_FRIENDS . "
264
				AND $table_alias{$options['owner_guid_column']} IN (
265
					SELECT guid_one FROM {$prefix}entity_relationships
266
					WHERE relationship = 'friend' AND guid_two = {$options['user_guid']}
267
				)";
268
	
269
			// include user's content
270
			$clauses['ors'][] = "$table_alias{$options['owner_guid_column']} = {$options['user_guid']}";
271
		}
272
	
273
		// include standard accesses (public, logged in, access collections)
274
		if (!$options['ignore_access']) {
275
			$access_list = get_access_list($options['user_guid']);
276
			$clauses['ors'][] = "$table_alias{$options['access_column']} IN {$access_list}";
277
		}
278
	
279
		if ($options['use_enabled_clause']) {
280
			$clauses['ands'][] = "{$table_alias}enabled = 'yes'";
281
		}
282
	
283
		$clauses = _elgg_services()->hooks->trigger('get_sql', 'access', $options, $clauses);
284
	
285
		$clauses_str = '';
286
		if (is_array($clauses['ors']) && $clauses['ors']) {
287
			$clauses_str = '(' . implode(' OR ', $clauses['ors']) . ')';
288
		}
289
	
290
		if (is_array($clauses['ands']) && $clauses['ands']) {
291
			if ($clauses_str) {
292
				$clauses_str .= ' AND ';
293
			}
294
			$clauses_str .= '(' . implode(' AND ', $clauses['ands']) . ')';
295
		}
296
	
297
		return "($clauses_str)";
298
	}
299
	
300
	/**
301
	 * Can a user access an entity.
302
	 *
303
	 * @warning If a logged in user doesn't have access to an entity, the
304
	 * core engine will not load that entity.
305
	 *
306
	 * @tip This is mostly useful for checking if a user other than the logged in
307
	 * user has access to an entity that is currently loaded.
308
	 *
309
	 * @todo This function would be much more useful if we could pass the guid of the
310
	 * entity to test access for. We need to be able to tell whether the entity exists
311
	 * and whether the user has access to the entity.
312
	 *
313
	 * @param \ElggEntity $entity The entity to check access for.
314
	 * @param \ElggUser   $user   Optionally user to check access for. Defaults to
315
	 *                           logged in user (which is a useless default).
316
	 *
317
	 * @return bool
318
	 */
319
	function hasAccessToEntity($entity, $user = null) {
320
		if (!$entity instanceof \ElggEntity) {
321
			return false;
322
		}
323
324
		// See #7159. Must not allow ignore access to affect query
325
		$ia = elgg_set_ignore_access(false);
326
	
327
		if (!isset($user)) {
328
			$access_bit = _elgg_get_access_where_sql();
329
		} else {
330
			$access_bit = _elgg_get_access_where_sql(array('user_guid' => $user->getGUID()));
331
		}
332
	
333
		elgg_set_ignore_access($ia);
334
335
		$db = _elgg_services()->db;
336
		$prefix = $db->getTablePrefix();
337
	
338
		$query = "SELECT guid from {$prefix}entities e WHERE e.guid = {$entity->guid}";
339
		// Add access controls
340
		$query .= " AND " . $access_bit;
341
		if ($db->getData($query)) {
342
			return true;
343
		} else {
344
			return false;
345
		}
346
	}
347
	
348
	/**
349
	 * Returns an array of access permissions that the user is allowed to save content with.
350
	 * Permissions returned are of the form (id => 'name').
351
	 *
352
	 * Example return value in English:
353
	 * array(
354
	 *     0 => 'Private',
355
	 *    -2 => 'Friends',
356
	 *     1 => 'Logged in users',
357
	 *     2 => 'Public',
358
	 *    34 => 'My favorite friends',
359
	 * );
360
	 *
361
	 * Plugin hook of 'access:collections:write', 'user'
362
	 *
363
	 * @warning this only returns access collections that the user owns plus the
364
	 * standard access levels. It does not return access collections that the user
365
	 * belongs to such as the access collection for a group.
366
	 *
367
	 * @param int   $user_guid    The user's GUID.
368
	 * @param int   $site_guid    The current site.
369
	 * @param bool  $flush        If this is set to true, this will ignore a cached access array
370
	 * @param array $input_params Some parameters passed into an input/access view
371
	 *
372
	 * @return array List of access permissions
373
	 */
374
	function getWriteAccessArray($user_guid = 0, $site_guid = 0, $flush = false, array $input_params = array()) {
375
		global $init_finished;
376
		$cache = _elgg_services()->accessCache;
377
	
378
		if ($flush) {
379
			$cache->clear();
380
		}
381
	
382
		if ($user_guid == 0) {
383
			$user_guid = _elgg_services()->session->getLoggedInUserGuid();
384
		}
385
	
386
		if (($site_guid == 0) && $this->site_guid) {
387
			$site_guid = $this->site_guid;
388
		}
389
	
390
		$user_guid = (int) $user_guid;
391
		$site_guid = (int) $site_guid;
392
	
393
		$hash = $user_guid . $site_guid . 'get_write_access_array';
394
	
395
		if ($cache[$hash]) {
396
			$access_array = $cache[$hash];
397
		} else {
398
			// @todo is there such a thing as public write access?
399
			$access_array = array(
400
				ACCESS_PRIVATE => $this->getReadableAccessLevel(ACCESS_PRIVATE),
401
				ACCESS_FRIENDS => $this->getReadableAccessLevel(ACCESS_FRIENDS),
402
				ACCESS_LOGGED_IN => $this->getReadableAccessLevel(ACCESS_LOGGED_IN),
403
				ACCESS_PUBLIC => $this->getReadableAccessLevel(ACCESS_PUBLIC)
404
			);
405
406
			$collections = $this->getEntityCollections($user_guid, $site_guid);
407
			if ($collections) {
408
				foreach ($collections as $collection) {
409
					$access_array[$collection->id] = $collection->name;
410
				}
411
			}
412
	
413
			if ($init_finished) {
414
				$cache[$hash] = $access_array;
415
			}
416
		}
417
	
418
		$options = array(
419
			'user_id' => $user_guid,
420
			'site_id' => $site_guid,
421
			'input_params' => $input_params,
422
		);
423
		return _elgg_services()->hooks->trigger('access:collections:write', 'user', $options, $access_array);
424
	}
425
426
	/**
427
	 * Can the user change this access collection?
428
	 *
429
	 * Use the plugin hook of 'access:collections:write', 'user' to change this.
430
	 * @see get_write_access_array() for details on the hook.
431
	 *
432
	 * Respects access control disabling for admin users and {@link elgg_set_ignore_access()}
433
	 *
434
	 * @see get_write_access_array()
435
	 *
436
	 * @param int   $collection_id The collection id
437
	 * @param mixed $user_guid     The user GUID to check for. Defaults to logged in user.
438
	 * @return bool
439
	 */
440
	function canEdit($collection_id, $user_guid = null) {
441
		if ($user_guid) {
442
			$user = _elgg_services()->entityTable->get((int) $user_guid);
443
		} else {
444
			$user = _elgg_services()->session->getLoggedInUser();
445
		}
446
	
447
		$collection = get_access_collection($collection_id);
448
	
449
		if (!($user instanceof \ElggUser) || !$collection) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collection 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...
450
			return false;
451
		}
452
	
453
		$write_access = get_write_access_array($user->getGUID(), 0, true);
454
	
455
		// don't ignore access when checking users.
456
		if ($user_guid) {
457
			return array_key_exists($collection_id, $write_access);
458
		} else {
459
			return elgg_get_ignore_access() || array_key_exists($collection_id, $write_access);
460
		}
461
	}
462
	
463
	/**
464
	 * Creates a new access collection.
465
	 *
466
	 * Access colletions allow plugins and users to create granular access
467
	 * for entities.
468
	 *
469
	 * Triggers plugin hook 'access:collections:addcollection', 'collection'
470
	 *
471
	 * @internal Access collections are stored in the access_collections table.
472
	 * Memberships to collections are in access_collections_membership.
473
	 *
474
	 * @param string $name       The name of the collection.
475
	 * @param int    $owner_guid The GUID of the owner (default: currently logged in user).
476
	 * @param int    $site_guid  The GUID of the site (default: current site).
477
	 *
478
	 * @return int|false The collection ID if successful and false on failure.
479
	 */
480
	function create($name, $owner_guid = 0, $site_guid = 0) {
481
		$name = trim($name);
482
		if (empty($name)) {
483
			return false;
484
		}
485
	
486
		if ($owner_guid == 0) {
487
			$owner_guid = _elgg_services()->session->getLoggedInUserGuid();
488
		}
489
		if (($site_guid == 0) && $this->site_guid) {
490
			$site_guid = $this->site_guid;
491
		}
492
493
		$db = _elgg_services()->db;
494
		$prefix = $db->getTablePrefix();
495
496
		$name = $db->sanitizeString($name);
497
	
498
		$q = "INSERT INTO {$prefix}access_collections
499
			SET name = '{$name}',
500
				owner_guid = {$owner_guid},
501
				site_guid = {$site_guid}";
502
		$id = $db->insertData($q);
503
		if (!$id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|false is loosely compared to false; 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...
504
			return false;
505
		}
506
	
507
		$params = array(
508
			'collection_id' => $id
509
		);
510
	
511
		if (!_elgg_services()->hooks->trigger('access:collections:addcollection', 'collection', $params, true)) {
512
			return false;
513
		}
514
	
515
		return $id;
516
	}
517
	
518
	/**
519
	 * Updates the membership in an access collection.
520
	 *
521
	 * @warning Expects a full list of all members that should
522
	 * be part of the access collection
523
	 *
524
	 * @note This will run all hooks associated with adding or removing
525
	 * members to access collections.
526
	 *
527
	 * @param int   $collection_id The ID of the collection.
528
	 * @param array $members       Array of member GUIDs
529
	 *
530
	 * @return bool
531
	 */
532
	function update($collection_id, $members) {
533
		$acl = $this->get($collection_id);
534
	
535
		if (!$acl) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $acl 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...
536
			return false;
537
		}
538
		$members = (is_array($members)) ? $members : array();
539
	
540
		$cur_members = $this->getMembers($collection_id, true);
541
		$cur_members = (is_array($cur_members)) ? $cur_members : array();
542
	
543
		$remove_members = array_diff($cur_members, $members);
544
		$add_members = array_diff($members, $cur_members);
545
	
546
		$result = true;
547
	
548
		foreach ($add_members as $guid) {
549
			$result = $result && $this->addUser($guid, $collection_id);
550
		}
551
	
552
		foreach ($remove_members as $guid) {
553
			$result = $result && $this->removeUser($guid, $collection_id);
554
		}
555
	
556
		return $result;
557
	}
558
	
559
	/**
560
	 * Deletes a specified access collection and its membership.
561
	 *
562
	 * @param int $collection_id The collection ID
563
	 *
564
	 * @return bool
565
	 */
566
	function delete($collection_id) {
567
		$collection_id = (int) $collection_id;
568
		$params = array('collection_id' => $collection_id);
569
	
570
		if (!_elgg_services()->hooks->trigger('access:collections:deletecollection', 'collection', $params, true)) {
571
			return false;
572
		}
573
574
		$db = _elgg_services()->db;
575
		$prefix = $db->getTablePrefix();
576
	
577
		// Deleting membership doesn't affect result of deleting ACL.
578
		$q = "DELETE FROM {$prefix}access_collection_membership
579
			WHERE access_collection_id = {$collection_id}";
580
		$db->deleteData($q);
581
	
582
		$q = "DELETE FROM {$prefix}access_collections
583
			WHERE id = {$collection_id}";
584
		$result = $db->deleteData($q);
585
	
586
		return (bool)$result;
587
	}
588
	
589
	/**
590
	 * Get a specified access collection
591
	 *
592
	 * @note This doesn't return the members of an access collection,
593
	 * just the database row of the actual collection.
594
	 *
595
	 * @see get_members_of_access_collection()
596
	 *
597
	 * @param int $collection_id The collection ID
598
	 *
599
	 * @return object|false
600
	 */
601
	function get($collection_id) {
602
		
603
		$collection_id = (int) $collection_id;
604
605
		$db = _elgg_services()->db;
606
		$prefix = $db->getTablePrefix();
607
	
608
		$query = "SELECT * FROM {$prefix}access_collections WHERE id = {$collection_id}";
609
		$get_collection = $db->getDataRow($query);
610
	
611
		return $get_collection;
612
	}
613
	
614
	/**
615
	 * Adds a user to an access collection.
616
	 *
617
	 * Triggers the 'access:collections:add_user', 'collection' plugin hook.
618
	 *
619
	 * @param int $user_guid     The GUID of the user to add
620
	 * @param int $collection_id The ID of the collection to add them to
621
	 *
622
	 * @return bool
623
	 */
624
	function addUser($user_guid, $collection_id) {
625
		$collection_id = (int) $collection_id;
626
		$user_guid = (int) $user_guid;
627
		$user = get_user($user_guid);
628
	
629
		$collection = $this->get($collection_id);
630
	
631
		if (!($user instanceof \ElggUser) || !$collection) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collection 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...
632
			return false;
633
		}
634
	
635
		$params = array(
636
			'collection_id' => $collection_id,
637
			'user_guid' => $user_guid
638
		);
639
	
640
		$result = _elgg_services()->hooks->trigger('access:collections:add_user', 'collection', $params, true);
641
		if ($result == false) {
642
			return false;
643
		}
644
645
		$db = _elgg_services()->db;
646
		$prefix = $db->getTablePrefix();
647
	
648
		// if someone tries to insert the same data twice, we do a no-op on duplicate key
649
		$q = "INSERT INTO {$prefix}access_collection_membership
650
				SET access_collection_id = $collection_id, user_guid = $user_guid
651
				ON DUPLICATE KEY UPDATE user_guid = user_guid";
652
		$result = $db->insertData($q);
653
	
654
		return $result !== false;
655
	}
656
	
657
	/**
658
	 * Removes a user from an access collection.
659
	 *
660
	 * Triggers the 'access:collections:remove_user', 'collection' plugin hook.
661
	 *
662
	 * @param int $user_guid     The user GUID
663
	 * @param int $collection_id The access collection ID
664
	 *
665
	 * @return bool
666
	 */
667
	function removeUser($user_guid, $collection_id) {
668
		$collection_id = (int) $collection_id;
669
		$user_guid = (int) $user_guid;
670
		$user = get_user($user_guid);
671
	
672
		$collection = $this->get($collection_id);
673
	
674
		if (!($user instanceof \ElggUser) || !$collection) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collection 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...
675
			return false;
676
		}
677
	
678
		$params = array(
679
			'collection_id' => $collection_id,
680
			'user_guid' => $user_guid,
681
		);
682
	
683
		if (!_elgg_services()->hooks->trigger('access:collections:remove_user', 'collection', $params, true)) {
684
			return false;
685
		}
686
687
		$db = _elgg_services()->db;
688
		$prefix = $db->getTablePrefix();
689
	
690
		$q = "DELETE FROM {$prefix}access_collection_membership
691
			WHERE access_collection_id = {$collection_id}
692
				AND user_guid = {$user_guid}";
693
	
694
		return (bool)$db->deleteData($q);
695
	}
696
	
697
	/**
698
	 * Returns an array of database row objects of the access collections owned by $owner_guid.
699
	 *
700
	 * @param int $owner_guid The entity guid
701
	 * @param int $site_guid  The GUID of the site (default: current site).
702
	 *
703
	 * @return array|false
704
	 */
705 View Code Duplication
	function getEntityCollections($owner_guid, $site_guid = 0) {
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...
706
		$owner_guid = (int) $owner_guid;
707
		$site_guid = (int) $site_guid;
708
	
709
		if (($site_guid == 0) && $this->site_guid) {
710
			$site_guid = $this->site_guid;
711
		}
712
713
		$db = _elgg_services()->db;
714
		$prefix = $db->getTablePrefix();
715
	
716
		$query = "SELECT * FROM {$prefix}access_collections
717
				WHERE owner_guid = {$owner_guid}
718
				AND site_guid = {$site_guid}
719
				ORDER BY name ASC";
720
	
721
		$collections = $db->getData($query);
722
	
723
		return $collections;
724
	}
725
	
726
	/**
727
	* Get all of members of an access collection
728
	*
729
	* @param int  $collection_id The collection's ID
730
	* @param bool $guids_only    If set to true, will only return the members' GUIDs (default: false)
731
	*
732
	* @return ElggUser[]|int[]|false guids or entities if successful, false if not
733
	*/
734
	function getMembers($collection_id, $guids_only = false) {
735
		$collection_id = (int) $collection_id;
736
737
		$db = _elgg_services()->db;
738
		$prefix = $db->getTablePrefix();
739
740
		if (!$guids_only) {
741
			$query = "SELECT e.* FROM {$prefix}access_collection_membership m"
742
				. " JOIN {$prefix}entities e ON e.guid = m.user_guid"
743
				. " WHERE m.access_collection_id = {$collection_id}";
744
			$collection_members = $db->getData($query, "entity_row_to_elggstar");
745
		} else {
746
			$query = "SELECT e.guid FROM {$prefix}access_collection_membership m"
747
				. " JOIN {$prefix}entities e ON e.guid = m.user_guid"
748
				. " WHERE m.access_collection_id = {$collection_id}";
749
			$collection_members = $db->getData($query);
750
			if (!$collection_members) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collection_members 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...
751
				return false;
752
			}
753
			foreach ($collection_members as $key => $val) {
754
				$collection_members[$key] = $val->guid;
755
			}
756
		}
757
	
758
		return $collection_members;
759
	}
760
	
761
	/**
762
	 * Return an array of database row objects of the access collections $entity_guid is a member of.
763
	 * 
764
	 * @param int $member_guid The entity guid
765
	 * @param int $site_guid   The GUID of the site (default: current site).
766
	 * 
767
	 * @return array|false
768
	 */
769 View Code Duplication
	function getCollectionsByMember($member_guid, $site_guid = 0) {
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...
770
		$member_guid = (int) $member_guid;
771
		$site_guid = (int) $site_guid;
772
		
773
		if (($site_guid == 0) && $this->site_guid) {
774
			$site_guid = $this->site_guid;
775
		}
776
		
777
		$db = _elgg_services()->db;
778
		$prefix = $db->getTablePrefix();
779
		
780
		$query = "SELECT ac.* FROM {$prefix}access_collections ac
781
				JOIN {$prefix}access_collection_membership m ON ac.id = m.access_collection_id
782
				WHERE m.user_guid = {$member_guid}
783
				AND ac.site_guid = {$site_guid}
784
				ORDER BY name ASC";
785
		
786
		$collections = $db->getData($query);
787
		
788
		return $collections;
789
	}
790
	
791
	/**
792
	 * Return the name of an ACCESS_* constant or an access collection,
793
	 * but only if the logged in user owns the access collection or is an admin.
794
	 * Ownership requirement prevents us from exposing names of access collections
795
	 * that current user has been added to by other members and may contain
796
	 * sensitive classification of the current user (e.g. close friends vs acquaintances).
797
	 *
798
	 * Returns a string in the language of the user for global access levels, e.g.'Public, 'Friends', 'Logged in', 'Private';
799
	 * or a name of the owned access collection, e.g. 'My work colleagues';
800
	 * or a name of the group or other access collection, e.g. 'Group: Elgg technical support';
801
	 * or 'Limited' if the user access is restricted to read-only, e.g. a friends collection the user was added to
802
	 *
803
	 * @param int $entity_access_id The entity's access id
804
	 * 
805
	 * @return string
806
	 * @since 1.11
807
	 */
808
	function getReadableAccessLevel($entity_access_id) {
809
		$access = (int) $entity_access_id;
810
811
		$translator = _elgg_services()->translator;
812
	
813
		// Check if entity access id is a defined global constant
814
		$access_array = array(
815
			ACCESS_PRIVATE => $translator->translate("PRIVATE"),
816
			ACCESS_FRIENDS => $translator->translate("access:friends:label"),
817
			ACCESS_LOGGED_IN => $translator->translate("LOGGED_IN"),
818
			ACCESS_PUBLIC => $translator->translate("PUBLIC"),
819
		);
820
	
821
		if (array_key_exists($access, $access_array)) {
822
			return $access_array[$access];
823
		}
824
	
825
		$user_guid = _elgg_services()->session->getLoggedInUserGuid();
826
		if (!$user_guid) {
827
			// return 'Limited' if there is no logged in user
828
			return $translator->translate('access:limited:label');
829
		}
830
		
831
		// Entity access id is probably a custom access collection
832
		// Check if the user has write access to it and can see it's label
833
		// Admins should always be able to see the readable version	
834
		$collection = $this->get($access);
835
		
836
		if ($collection) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collection 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...
837
			if (($collection->owner_guid == $user_guid) || _elgg_services()->session->isAdminLoggedIn()) {
838
				return $collection->name;
839
			}
840
		}
841
		
842
		// return 'Limited' if the user does not have access to the access collection
843
		return $translator->translate('access:limited:label');
844
	}
845
}
846