ElggEntity::create()   F
last analyzed

Complexity

Conditions 23
Paths 178

Size

Total Lines 130
Code Lines 76

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 58
CRAP Score 30.0242

Importance

Changes 0
Metric Value
cc 23
eloc 76
nc 178
nop 0
dl 0
loc 130
rs 3.5166
c 0
b 0
f 0
ccs 58
cts 76
cp 0.7632
crap 30.0242

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
use Elgg\EntityIcon;
4
5
/**
6
 * The parent class for all Elgg Entities.
7
 *
8
 * An \ElggEntity is one of the basic data models in Elgg.
9
 * It is the primary means of storing and retrieving data from the database.
10
 * An \ElggEntity represents one row of the entities table.
11
 *
12
 * The \ElggEntity class handles CRUD operations for the entities table.
13
 * \ElggEntity should always be extended by another class to handle CRUD
14
 * operations on the type-specific table.
15
 *
16
 * \ElggEntity uses magic methods for get and set, so any property that isn't
17
 * declared will be assumed to be metadata and written to the database
18
 * as metadata on the object.  All children classes must declare which
19
 * properties are columns of the type table or they will be assumed
20
 * to be metadata.  See \ElggObject::initializeAttributes() for examples.
21
 *
22
 * Core supports 4 types of entities: \ElggObject, \ElggUser, \ElggGroup, and \ElggSite.
23
 *
24
 * @tip Plugin authors will want to extend the \ElggObject class, not this class.
25
 *
26
 * @package    Elgg.Core
27
 * @subpackage DataModel.Entities
28
 *
29
 * @property       string $type           object, user, group, or site (read-only after save)
30
 * @property       string $subtype        Further clarifies the nature of the entity
31
 * @property-read  int    $guid           The unique identifier for this entity (read only)
32
 * @property       int    $owner_guid     The GUID of the owner of this entity (usually the creator)
33
 * @property       int    $container_guid The GUID of the entity containing this entity
34
 * @property       int    $access_id      Specifies the visibility level of this entity
35
 * @property       int    $time_created   A UNIX timestamp of when the entity was created
36
 * @property-read  int    $time_updated   A UNIX timestamp of when the entity was last updated (automatically updated on save)
37
 * @property-read  int    $last_action    A UNIX timestamp of when the entity was last acted upon
38
 * @property       string $enabled        Is this entity enabled ('yes' or 'no')
39
 *
40
 * Metadata (the above are attributes)
41
 * @property       string $location       A location of the entity
42
 */
43
abstract class ElggEntity extends \ElggData implements
44
	Locatable, // Geocoding interface
45
	EntityIcon // Icon interface
46
{
47
48
	public static $primary_attr_names = [
49
		'guid',
50
		'type',
51
		'subtype',
52
		'owner_guid',
53
		'container_guid',
54
		'access_id',
55
		'time_created',
56
		'time_updated',
57
		'last_action',
58
		'enabled',
59
	];
60
61
	protected static $integer_attr_names = [
62
		'guid',
63
		'owner_guid',
64
		'container_guid',
65
		'access_id',
66
		'time_created',
67
		'time_updated',
68
		'last_action',
69
	];
70
71
	/**
72
	 * Holds metadata until entity is saved.  Once the entity is saved,
73
	 * metadata are written immediately to the database.
74
	 * @var array
75
	 */
76
	protected $temp_metadata = [];
77
78
	/**
79
	 * Holds annotations until entity is saved.  Once the entity is saved,
80
	 * annotations are written immediately to the database.
81
	 * @var array
82
	 */
83
	protected $temp_annotations = [];
84
85
	/**
86
	 * Holds private settings until entity is saved. Once the entity is saved,
87
	 * private settings are written immediately to the database.
88
	 * @var array
89
	 */
90
	protected $temp_private_settings = [];
91
92
	/**
93
	 * Volatile data structure for this object, allows for storage of data
94
	 * in-memory that isn't sync'd back to the metadata table.
95
	 * @var array
96
	 */
97
	protected $volatile = [];
98
99
	/**
100
	 * Holds the original (persisted) attribute values that have been changed but not yet saved.
101
	 * @var array
102
	 */
103
	protected $orig_attributes = [];
104
105
	/**
106
	 * @var bool
107
	 */
108
	protected $_is_cacheable = true;
109
110
	/**
111
	 * Holds metadata key/value pairs acquired from the metadata cache
112
	 * Entity metadata may have mutated since last call to __get,
113
	 * do not rely on this value for any business logic
114
	 * This storage is intended to help with debugging objects during dump,
115
	 * because otherwise it's hard to tell what the object is from it's attributes
116
	 *
117
	 * @var array
118
	 * @internal
119
	 */
120
	protected $_cached_metadata;
121
122
	/**
123
	 * Create a new entity.
124
	 *
125
	 * Plugin developers should only use the constructor to create a new entity.
126
	 * To retrieve entities, use get_entity() and the elgg_get_entities* functions.
127
	 *
128
	 * If no arguments are passed, it creates a new entity.
129
	 * If a database result is passed as a \stdClass instance, it instantiates
130
	 * that entity.
131
	 *
132
	 * @param stdClass $row Database row result. Default is null to create a new object.
133
	 *
134
	 * @throws IOException If cannot load remaining data from db
135
	 */
136 6167
	public function __construct(stdClass $row = null) {
137 6167
		$this->initializeAttributes();
138
139 6167
		if ($row && !$this->load($row)) {
140
			$msg = "Failed to load new " . get_class() . " for GUID:" . $row->guid;
141
			throw new \IOException($msg);
142
		}
143 6167
	}
144
145
	/**
146
	 * Initialize the attributes array.
147
	 *
148
	 * This is vital to distinguish between metadata and base parameters.
149
	 *
150
	 * @return void
151
	 */
152 1506
	protected function initializeAttributes() {
153 1506
		parent::initializeAttributes();
154
155 1506
		$this->attributes['guid'] = null;
156 1506
		$this->attributes['type'] = null;
157 1506
		$this->attributes['subtype'] = null;
158
159 1506
		$this->attributes['owner_guid'] = _elgg_services()->session->getLoggedInUserGuid();
160 1506
		$this->attributes['container_guid'] = _elgg_services()->session->getLoggedInUserGuid();
161
162 1506
		$this->attributes['access_id'] = ACCESS_PRIVATE;
163 1506
		$this->attributes['time_updated'] = null;
164 1506
		$this->attributes['last_action'] = null;
165 1506
		$this->attributes['enabled'] = "yes";
166
167 1506
		$this->attributes['type'] = $this->getType();
168 1506
	}
169
170
	/**
171
	 * Clone an entity
172
	 *
173
	 * Resets the guid so that the entity can be saved as a distinct entity from
174
	 * the original. Creation time will be set when this new entity is saved.
175
	 * The owner and container guids come from the original entity. The clone
176
	 * method copies metadata but does not copy annotations or private settings.
177
	 *
178
	 * @return void
179
	 */
180 2
	public function __clone() {
181 2
		$orig_entity = get_entity($this->guid);
182 2
		if (!$orig_entity) {
0 ignored issues
show
introduced by Brett Profitt
$orig_entity is of type ElggEntity, thus it always evaluated to true.
Loading history...
183
			_elgg_services()->logger->error("Failed to clone entity with GUID $this->guid");
184
			return;
185
		}
186
187 2
		$metadata_array = elgg_get_metadata([
188 2
			'guid' => $this->guid,
189 2
			'limit' => 0
190
		]);
191
192 2
		$this->attributes['guid'] = null;
193 2
		$this->attributes['time_created'] = null;
194 2
		$this->attributes['time_updated'] = null;
195 2
		$this->attributes['last_action'] = null;
196
197 2
		$this->attributes['subtype'] = $orig_entity->getSubtype();
198
199
		// copy metadata over to new entity - slightly convoluted due to
200
		// handling of metadata arrays
201 2
		if (is_array($metadata_array)) {
202
			// create list of metadata names
203 2
			$metadata_names = [];
204 2
			foreach ($metadata_array as $metadata) {
205 2
				$metadata_names[] = $metadata->name;
206
			}
207
			// arrays are stored with multiple enties per name
208 2
			$metadata_names = array_unique($metadata_names);
209
210
			// move the metadata over
211 2
			foreach ($metadata_names as $name) {
212 2
				$this->__set($name, $orig_entity->$name);
213
			}
214
		}
215 2
	}
216
217
	/**
218
	 * Set an attribute or metadata value for this entity
219
	 *
220
	 * Anything that is not an attribute is saved as metadata.
221
	 *
222
	 * Be advised that metadata values are cast to integer or string.
223
	 * You can save booleans, but they will be stored and returned as integers.
224
	 *
225
	 * @param string $name  Name of the attribute or metadata
226
	 * @param mixed  $value The value to be set
227
	 * @return void
228
	 * @see \ElggEntity::setMetadata()
229
	 */
230 1399
	public function __set($name, $value) {
231 1399
		if ($this->$name === $value) {
232
			// quick return if value is not changing
233 441
			return;
234
		}
235
236 1399
		if (array_key_exists($name, $this->attributes)) {
237
			// if an attribute is 1 (integer) and it's set to "1" (string), don't consider that a change.
238 717
			if (is_int($this->attributes[$name])
239 717
					&& is_string($value)
240 717
					&& ((string) $this->attributes[$name] === $value)) {
241 1
				return;
242
			}
243
244
			// keep original values
245 717
			if ($this->guid && !array_key_exists($name, $this->orig_attributes)) {
246 11
				$this->orig_attributes[$name] = $this->attributes[$name];
247
			}
248
249
			// Certain properties should not be manually changed!
250 717
			switch ($name) {
251 717
				case 'guid':
252 717
				case 'time_updated':
253 717
				case 'last_action':
254 1
					return;
255
					break;
0 ignored issues
show
Unused Code introduced by Brett Profitt
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
256 716
				case 'access_id':
257 639
				case 'owner_guid':
258 519
				case 'container_guid':
259 608
					if ($value !== null) {
260 608
						$this->attributes[$name] = (int) $value;
261
					} else {
262
						$this->attributes[$name] = null;
263
					}
264 608
					break;
265
				default:
266 498
					$this->attributes[$name] = $value;
267 498
					break;
268
			}
269 716
			return;
270
		}
271
272 1361
		$this->setMetadata($name, $value);
273 1361
	}
274
275
	/**
276
	 * Get the original values of attribute(s) that have been modified since the entity was persisted.
277
	 *
278
	 * @return array
279
	 */
280 98
	public function getOriginalAttributes() {
281 98
		return $this->orig_attributes;
282
	}
283
284
	/**
285
	 * Get an attribute or metadata value
286
	 *
287
	 * If the name matches an attribute, the attribute is returned. If metadata
288
	 * does not exist with that name, a null is returned.
289
	 *
290
	 * This only returns an array if there are multiple values for a particular
291
	 * $name key.
292
	 *
293
	 * @param string $name Name of the attribute or metadata
294
	 * @return mixed
295
	 */
296 6222
	public function __get($name) {
297 6222
		if (array_key_exists($name, $this->attributes)) {
298 6218
			return $this->attributes[$name];
299
		}
300
301 6219
		return $this->getMetadata($name);
302
	}
303
304
	/**
305
	 * Get the entity's display name
306
	 *
307
	 * @return string The title or name of this entity.
308
	 */
309 509
	public function getDisplayName() {
310 509
		return $this->name;
311
	}
312
313
	/**
314
	 * Sets the title or name of this entity.
315
	 *
316
	 * @param string $display_name The title or name of this entity.
317
	 * @return void
318
	 */
319
	public function setDisplayName($display_name) {
320
		$this->name = $display_name;
0 ignored issues
show
Bug Best Practice introduced by Jeroen Dalsem
The property name does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
321
	}
322
323
	/**
324
	 * Return the value of a piece of metadata.
325
	 *
326
	 * @param string $name Name
327
	 *
328
	 * @return mixed The value, or null if not found.
329
	 */
330 6219
	public function getMetadata($name) {
331 6219
		$metadata = $this->getAllMetadata();
332 6219
		return elgg_extract($name, $metadata);
333
	}
334
335
	/**
336
	 * Get all entity metadata
337
	 *
338
	 * @return array
339
	 */
340 6219
	public function getAllMetadata() {
341 6219
		if (!$this->guid) {
342 709
			return array_map(function($values) {
343 588
				return count($values) > 1 ? $values : $values[0];
344 709
			}, $this->temp_metadata);
345
		}
346
347 6210
		$this->_cached_metadata = _elgg_services()->metadataCache->getAll($this->guid);
348
349 6210
		return $this->_cached_metadata;
350
	}
351
352
	/**
353
	 * Unset a property from metadata or attribute.
354
	 *
355
	 * @warning If you use this to unset an attribute, you must save the object!
356
	 *
357
	 * @param string $name The name of the attribute or metadata.
358
	 *
359
	 * @return void
360
	 * @todo some attributes should be set to null or other default values
361
	 */
362 77
	public function __unset($name) {
363 77
		if (array_key_exists($name, $this->attributes)) {
364 1
			$this->attributes[$name] = "";
365
		} else {
366 76
			$this->deleteMetadata($name);
367
		}
368 77
	}
369
370
	/**
371
	 * Set metadata on this entity.
372
	 *
373
	 * Plugin developers usually want to use the magic set method ($entity->name = 'value').
374
	 * Use this method if you want to explicitly set the owner or access of the metadata.
375
	 * You cannot set the owner/access before the entity has been saved.
376
	 *
377
	 * @param string $name       Name of the metadata
378
	 * @param mixed  $value      Value of the metadata (doesn't support assoc arrays)
379
	 * @param string $value_type 'text', 'integer', or '' for automatic detection
380
	 * @param bool   $multiple   Allow multiple values for a single name.
381
	 *                           Does not support associative arrays.
382
	 *
383
	 * @return bool
384
	 * @throws InvalidArgumentException
385
	 */
386 1361
	public function setMetadata($name, $value, $value_type = '', $multiple = false) {
387
388
		// normalize value to an array that we will loop over
389
		// remove indexes if value already an array.
390 1361
		if (is_array($value)) {
391 207
			$value = array_values($value);
392
		} else {
393 1360
			$value = [$value];
394
		}
395
396
		// strip null values from array
397 1361
		$value = array_filter($value, function($var) {
398 1361
			return !is_null($var);
399 1361
		});
400
401 1361
		if (empty($this->guid)) {
402
			// unsaved entity. store in temp array
403 703
			return $this->setTempMetadata($name, $value, $multiple);
404
		}
405
406
		// saved entity. persist md to db.
407 1291
		if (!$multiple) {
408 1291
			$current_metadata = $this->getMetadata($name);
409
410 1291
			if ((is_array($current_metadata) || count($value) > 1 || $value === []) && isset($current_metadata)) {
411
				// remove current metadata if needed
412
				// need to remove access restrictions right now to delete
413
				// because this is the expected behavior
414 8
				$delete_result = elgg_call(ELGG_IGNORE_ACCESS, function() use ($name) {
415 8
					return elgg_delete_metadata([
416 8
						'guid' => $this->guid,
417 8
						'metadata_name' => $name,
418
						'limit' => false,
419
					]);
420 8
				});
421
422 8
				if (false === $delete_result) {
423
					return false;
424
				}
425
			}
426
427 1291
			if (count($value) > 1) {
428
				// new value is a multiple valued metadata
429 207
				$multiple = true;
430
			}
431
		}
432
433
		// create new metadata
434 1291
		foreach ($value as $value_tmp) {
435 1291
			$metadata = new ElggMetadata();
436 1291
			$metadata->entity_guid = $this->guid;
437 1291
			$metadata->name = $name;
438 1291
			$metadata->value_type = $value_type;
439 1291
			$metadata->value = $value_tmp;
440 1291
			$md_id = _elgg_services()->metadataTable->create($metadata, $multiple);
441 1291
			if (!$md_id) {
0 ignored issues
show
Bug Best Practice introduced by Jeroen Dalsem
The expression $md_id of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false 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...
442 1291
				return false;
443
			}
444
		}
445
446 1291
		return true;
447
	}
448
449
	/**
450
	 * Set temp metadata on this entity.
451
	 *
452
	 * @param string $name     Name of the metadata
453
	 * @param mixed  $value    Value of the metadata (doesn't support assoc arrays)
454
	 * @param bool   $multiple Allow multiple values for a single name.
455
	 *                         Does not support associative arrays.
456
	 *
457
	 * @return bool
458
	 */
459 703
	protected function setTempMetadata($name, $value, $multiple = false) {
460
		// if overwrite, delete first
461 703
		if (!$multiple) {
462 703
			unset($this->temp_metadata[$name]);
463 703
			if (count($value)) {
464
				// only save if value array contains data
465 703
				$this->temp_metadata[$name] = $value;
466
			}
467 703
			return true;
468
		}
469
470 3
		if (!isset($this->temp_metadata[$name])) {
471
			$this->temp_metadata[$name] = [];
472
		}
473
474 3
		$this->temp_metadata[$name] = array_merge($this->temp_metadata[$name], $value);
475
476 3
		return true;
477
	}
478
479
480
481
	/**
482
	 * Deletes all metadata on this object (metadata.entity_guid = $this->guid).
483
	 * If you pass a name, only metadata matching that name will be deleted.
484
	 *
485
	 * @warning Calling this with no $name will clear all metadata on the entity.
486
	 *
487
	 * @param null|string $name The name of the metadata to remove.
488
	 * @return bool|null
489
	 * @since 1.8
490
	 */
491 325
	public function deleteMetadata($name = null) {
492
493 325
		if (!$this->guid) {
494
			// remove from temp_metadata
495 17
			if ($name) {
496 17
				if (!isset($this->temp_metadata[$name])) {
497 15
					return null;
498
				} else {
499 2
					unset($this->temp_metadata[$name]);
500 2
					return true;
501
				}
502
			} else {
503
				$this->temp_metadata = [];
504
				return true;
505
			}
506
		}
507
508
		$options = [
509 310
			'guid' => $this->guid,
510 310
			'limit' => 0
511
		];
512 310
		if ($name) {
513 67
			$options['metadata_name'] = $name;
514
		}
515
516 310
		return elgg_delete_metadata($options);
517
	}
518
519
	/**
520
	 * Get a piece of volatile (non-persisted) data on this entity.
521
	 *
522
	 * @param string $name The name of the volatile data
523
	 *
524
	 * @return mixed The value or null if not found.
525
	 */
526 33
	public function getVolatileData($name) {
527 33
		return array_key_exists($name, $this->volatile) ? $this->volatile[$name] : null;
528
	}
529
530
	/**
531
	 * Set a piece of volatile (non-persisted) data on this entity
532
	 *
533
	 * @param string $name  Name
534
	 * @param mixed  $value Value
535
	 *
536
	 * @return void
537
	 */
538 36
	public function setVolatileData($name, $value) {
539 36
		$this->volatile[$name] = $value;
540 36
	}
541
542
	/**
543
	 * Remove all relationships to and from this entity.
544
	 * If you pass a relationship name, only relationships matching that name
545
	 * will be deleted.
546
	 *
547
	 * @warning Calling this with no $relationship will clear all relationships
548
	 * for this entity.
549
	 *
550
	 * @param null|string $relationship The name of the relationship to remove.
551
	 * @return bool
552
	 * @see \ElggEntity::addRelationship()
553
	 * @see \ElggEntity::removeRelationship()
554
	 */
555 246
	public function deleteRelationships($relationship = null) {
556 246
		$relationship = (string) $relationship;
557 246
		$result = remove_entity_relationships($this->getGUID(), $relationship);
558 246
		return $result && remove_entity_relationships($this->getGUID(), $relationship, true);
559
	}
560
561
	/**
562
	 * Add a relationship between this an another entity.
563
	 *
564
	 * @tip Read the relationship like "This entity is a $relationship of $guid_two."
565
	 *
566
	 * @param int    $guid_two     GUID of the target entity of the relationship.
567
	 * @param string $relationship The type of relationship.
568
	 *
569
	 * @return bool
570
	 * @see \ElggEntity::removeRelationship()
571
	 * @see \ElggEntity::deleteRelationships()
572
	 */
573 6
	public function addRelationship($guid_two, $relationship) {
574 6
		return add_entity_relationship($this->getGUID(), $relationship, $guid_two);
575
	}
576
577
	/**
578
	 * Remove a relationship
579
	 *
580
	 * @param int    $guid_two     GUID of the target entity of the relationship.
581
	 * @param string $relationship The type of relationship.
582
	 *
583
	 * @return bool
584
	 * @see \ElggEntity::addRelationship()
585
	 * @see \ElggEntity::deleteRelationships()
586
	 */
587 2
	public function removeRelationship($guid_two, $relationship) {
588 2
		return remove_entity_relationship($this->getGUID(), $relationship, $guid_two);
589
	}
590
591
	/**
592
	 * Adds a private setting to this entity.
593
	 *
594
	 * @warning Private settings are stored as strings
595
	 *          Unlike metadata and annotations there is no reverse casting when you retrieve the setting
596
	 *          When saving integers, they will be cast to strings
597
	 *          Booleans will be cast to '0' and '1'
598
	 *
599
	 * @param string $name  Name of private setting
600
	 * @param mixed  $value Value of private setting
601
	 *
602
	 * @return bool
603
	 * @throws DatabaseException
604
	 */
605 150
	public function setPrivateSetting($name, $value) {
606 150
		if (is_bool($value)) {
607 33
			$value = (int) $value;
608
		}
609
610 150
		if (!$this->guid) {
611 65
			$this->temp_private_settings[$name] = $value;
612
613 65
			return true;
614
		}
615
616 144
		return _elgg_services()->privateSettings->set($this, $name, $value);
617
	}
618
619
	/**
620
	 * Returns a private setting value
621
	 *
622
	 * @warning Private settings are always returned as strings
623
	 *          Make sure you can your values back to expected type
624
	 *
625
	 * @param string $name Name of the private setting
626
	 *
627
	 * @return string|null
628
	 * @throws DatabaseException
629
	 */
630 78
	public function getPrivateSetting($name) {
631 78
		if (!$this->guid) {
632 25
			return elgg_extract($name, $this->temp_private_settings);
633
		}
634
635 67
		return _elgg_services()->privateSettings->get($this, $name);
636
	}
637
638
	/**
639
	 * Returns all private settings
640
	 *
641
	 * @return array
642
	 * @throws DatabaseException
643
	 */
644 2
	public function getAllPrivateSettings() {
645 2
		if (!$this->guid) {
646
			return $this->temp_private_settings;
647
		}
648
649 2
		return _elgg_services()->privateSettings->getAllForEntity($this);
650
	}
651
652
	/**
653
	 * Removes private setting
654
	 *
655
	 * @param string $name Name of the private setting
656
	 *
657
	 * @return bool
658
	 * @throws DatabaseException
659
	 */
660 5
	public function removePrivateSetting($name) {
661 5
		if (!$this->guid) {
662
			unset($this->temp_private_settings[$name]);
663
			return true;
664
		}
665
666 5
		return _elgg_services()->privateSettings->remove($this, $name);
667
	}
668
669
	/**
670
	 * Removes all private settings
671
	 *
672
	 * @return bool
673
	 * @throws DatabaseException
674
	 */
675 247
	public function removeAllPrivateSettings() {
676 247
		if (!$this->guid) {
677
			$this->temp_private_settings = [];
678
			return true;
679
		}
680
681 247
		return _elgg_services()->privateSettings->removeAllForEntity($this);
682
	}
683
684
	/**
685
	 * Deletes all annotations on this object (annotations.entity_guid = $this->guid).
686
	 * If you pass a name, only annotations matching that name will be deleted.
687
	 *
688
	 * @warning Calling this with no or empty arguments will clear all annotations on the entity.
689
	 *
690
	 * @param null|string $name The annotations name to remove.
691
	 * @return bool
692
	 * @since 1.8
693
	 */
694 246
	public function deleteAnnotations($name = null) {
695
		$options = [
696 246
			'guid' => $this->guid,
697 246
			'limit' => 0
698
		];
699 246
		if ($name) {
700 1
			$options['annotation_name'] = $name;
701
		}
702
703 246
		return elgg_delete_annotations($options);
704
	}
705
706
	/**
707
	 * Deletes all annotations owned by this object (annotations.owner_guid = $this->guid).
708
	 * If you pass a name, only annotations matching that name will be deleted.
709
	 *
710
	 * @param null|string $name The name of annotations to delete.
711
	 * @return bool
712
	 * @since 1.8
713
	 */
714 246
	public function deleteOwnedAnnotations($name = null) {
715
		// access is turned off for this because they might
716
		// no longer have access to an entity they created annotations on
717
718 246
		$flags = ELGG_IGNORE_ACCESS;
719 246
		$callback = function() use ($name) {
720 246
			return elgg_delete_annotations([
721 246
				'annotation_owner_guid' => $this->guid,
722 246
				'limit' => 0,
723 246
				'annotation_name' => $name,
724
			]);
725 246
		};
726
727 246
		return elgg_call($flags, $callback);
728
	}
729
730
	/**
731
	 * Disables annotations for this entity, optionally based on name.
732
	 *
733
	 * @param string $name An options name of annotations to disable.
734
	 * @return bool
735
	 * @since 1.8
736
	 */
737 5
	public function disableAnnotations($name = '') {
738
		$options = [
739 5
			'guid' => $this->guid,
740 5
			'limit' => 0
741
		];
742 5
		if ($name) {
743
			$options['annotation_name'] = $name;
744
		}
745
746 5
		return elgg_disable_annotations($options);
747
	}
748
749
	/**
750
	 * Enables annotations for this entity, optionally based on name.
751
	 *
752
	 * @warning Before calling this, you must use {@link access_show_hidden_entities()}
753
	 *
754
	 * @param string $name An options name of annotations to enable.
755
	 * @return bool
756
	 * @since 1.8
757
	 */
758 3
	public function enableAnnotations($name = '') {
759
		$options = [
760 3
			'guid' => $this->guid,
761 3
			'limit' => 0
762
		];
763 3
		if ($name) {
764
			$options['annotation_name'] = $name;
765
		}
766
767 3
		return elgg_enable_annotations($options);
768
	}
769
770
	/**
771
	 * Helper function to return annotation calculation results
772
	 *
773
	 * @param string $name        The annotation name.
774
	 * @param string $calculation A valid MySQL function to run its values through
775
	 * @return mixed
776
	 */
777 2
	private function getAnnotationCalculation($name, $calculation) {
778
		$options = [
779 2
			'guid' => $this->getGUID(),
780
			'distinct' => false,
781 2
			'annotation_name' => $name,
782 2
			'annotation_calculation' => $calculation
783
		];
784
785 2
		return elgg_get_annotations($options);
786
	}
787
788
	/**
789
	 * Adds an annotation to an entity.
790
	 *
791
	 * @warning By default, annotations are private.
792
	 *
793
	 * @warning Annotating an unsaved entity more than once with the same name
794
	 *          will only save the last annotation.
795
	 *
796
	 * @todo Update temp_annotations to store an instance of ElggAnnotation and simply call ElggAnnotation::save(),
797
	 *       after entity is saved
798
	 *
799
	 * @param string $name       Annotation name
800
	 * @param mixed  $value      Annotation value
801
	 * @param int    $access_id  Access ID
802
	 * @param int    $owner_guid GUID of the annotation owner
803
	 * @param string $value_type The type of annotation value
804
	 *
805
	 * @return bool|int Returns int if an annotation is saved
806
	 */
807 103
	public function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_guid = 0, $value_type = "") {
808 103
		if ($this->guid) {
809 103
			if (!$owner_guid) {
810 102
				$owner_guid = _elgg_services()->session->getLoggedInUserGuid();
811
			}
812 103
			$annotation = new ElggAnnotation();
813 103
			$annotation->entity_guid = $this->guid;
814 103
			$annotation->name = $name;
815 103
			$annotation->value_type = $value_type;
816 103
			$annotation->value = $value;
817 103
			$annotation->owner_guid = $owner_guid;
818 103
			$annotation->access_id = $access_id;
819 103
			return $annotation->save();
820
		} else {
821 18
			$this->temp_annotations[$name] = $value;
822
		}
823 18
		return true;
824
	}
825
826
	/**
827
	 * Gets an array of annotations.
828
	 *
829
	 * To retrieve annotations on an unsaved entity, pass array('name' => [annotation name])
830
	 * as the options array.
831
	 *
832
	 * @param array $options Array of options for elgg_get_annotations() except guid.
833
	 *
834
	 * @return array
835
	 * @see elgg_get_annotations()
836
	 */
837 12
	public function getAnnotations(array $options = []) {
838 12
		if ($this->guid) {
839 12
			$options['guid'] = $this->guid;
840
841 12
			return elgg_get_annotations($options);
0 ignored issues
show
Bug Best Practice introduced by Brett Profitt
The expression return elgg_get_annotations($options) also could return the type false|integer which is incompatible with the documented return type array.
Loading history...
842
		} else {
843
			$name = elgg_extract('annotation_name', $options, '');
844
845
			if (isset($this->temp_annotations[$name])) {
846
				return [$this->temp_annotations[$name]];
847
			}
848
		}
849
850
		return [];
851
	}
852
853
	/**
854
	 * Count annotations.
855
	 *
856
	 * @param string $name The type of annotation.
857
	 *
858
	 * @return int
859
	 */
860 2
	public function countAnnotations($name = "") {
861 2
		return $this->getAnnotationCalculation($name, 'count');
0 ignored issues
show
Bug Best Practice introduced by Brett Profitt
The expression return $this->getAnnotat...ulation($name, 'count') also could return the type ElggAnnotation[]|false which is incompatible with the documented return type integer.
Loading history...
862
	}
863
864
	/**
865
	 * Get the average of an integer type annotation.
866
	 *
867
	 * @param string $name Annotation name
868
	 *
869
	 * @return int
870
	 */
871
	public function getAnnotationsAvg($name) {
872
		return $this->getAnnotationCalculation($name, 'avg');
0 ignored issues
show
Bug Best Practice introduced by Brett Profitt
The expression return $this->getAnnotat...lculation($name, 'avg') also could return the type ElggAnnotation[]|false which is incompatible with the documented return type integer.
Loading history...
873
	}
874
875
	/**
876
	 * Get the sum of integer type annotations of a given name.
877
	 *
878
	 * @param string $name Annotation name
879
	 *
880
	 * @return int
881
	 */
882
	public function getAnnotationsSum($name) {
883
		return $this->getAnnotationCalculation($name, 'sum');
0 ignored issues
show
Bug Best Practice introduced by Brett Profitt
The expression return $this->getAnnotat...lculation($name, 'sum') also could return the type ElggAnnotation[]|false which is incompatible with the documented return type integer.
Loading history...
884
	}
885
886
	/**
887
	 * Get the minimum of integer type annotations of given name.
888
	 *
889
	 * @param string $name Annotation name
890
	 *
891
	 * @return int
892
	 */
893
	public function getAnnotationsMin($name) {
894
		return $this->getAnnotationCalculation($name, 'min');
0 ignored issues
show
Bug Best Practice introduced by Brett Profitt
The expression return $this->getAnnotat...lculation($name, 'min') also could return the type ElggAnnotation[]|false which is incompatible with the documented return type integer.
Loading history...
895
	}
896
897
	/**
898
	 * Get the maximum of integer type annotations of a given name.
899
	 *
900
	 * @param string $name Annotation name
901
	 *
902
	 * @return int
903
	 */
904
	public function getAnnotationsMax($name) {
905
		return $this->getAnnotationCalculation($name, 'max');
0 ignored issues
show
Bug Best Practice introduced by Brett Profitt
The expression return $this->getAnnotat...lculation($name, 'max') also could return the type ElggAnnotation[]|false which is incompatible with the documented return type integer.
Loading history...
906
	}
907
908
	/**
909
	 * Count the number of comments attached to this entity.
910
	 *
911
	 * @return int Number of comments
912
	 * @since 1.8.0
913
	 */
914 2
	public function countComments() {
915 2
		$params = ['entity' => $this];
916 2
		$num = _elgg_services()->hooks->trigger('comments:count', $this->getType(), $params);
917
918 2
		if (is_int($num)) {
919
			return $num;
920
		}
921
922 2
		return elgg_get_entities([
0 ignored issues
show
Bug Best Practice introduced by Jeroen Dalsem
The expression return elgg_get_entities..., 'distinct' => false)) also could return the type ElggEntity[]|false which is incompatible with the documented return type integer.
Loading history...
923 2
			'type' => 'object',
924 2
			'subtype' => 'comment',
925 2
			'container_guid' => $this->getGUID(),
926
			'count' => true,
927
			'distinct' => false,
928
		]);
929
	}
930
931
	/**
932
	 * Returns the ACLs owned by the entity
933
	 *
934
	 * @param array $options additional options to get the access collections with
935
	 *
936
	 * @return \ElggAccessCollection[]
937
	 *
938
	 * @see elgg_get_access_collections()
939
	 * @since 3.0
940
	 */
941 251
	public function getOwnedAccessCollections($options = []) {
942 251
		$options['owner_guid'] = $this->guid;
943 251
		return _elgg_services()->accessCollections->getEntityCollections($options);
944
	}
945
	
946
	/**
947
	 * Returns the first ACL owned by the entity with a given subtype
948
	 *
949
	 * @param string $subtype subtype of the ACL
950
	 *
951
	 * @return \ElggAccessCollection|false
952
	 *
953
	 * @since 3.0
954
	 */
955 9
	public function getOwnedAccessCollection($subtype) {
956 9
		if (!is_string($subtype) || $subtype === '') {
0 ignored issues
show
introduced by Jeroen Dalsem
The condition is_string($subtype) is always true.
Loading history...
957
			return false;
958
		}
959
		
960 9
		$acls = $this->getOwnedAccessCollections([
961 9
			'subtype' => $subtype,
962
		]);
963
		
964 9
		return elgg_extract(0, $acls, false);
965
	}
966
967
	/**
968
	 * Gets an array of entities with a relationship to this entity.
969
	 *
970
	 * @param array $options Options array. See elgg_get_entities()
971
	 *                       for a list of options. 'relationship_guid' is set to
972
	 *                       this entity.
973
	 *
974
	 * @return array|false An array of entities or false on failure
975
	 * @see elgg_get_entities()
976
	 */
977
	public function getEntitiesFromRelationship(array $options = []) {
978
		$options['relationship_guid'] = $this->guid;
979
		return elgg_get_entities($options);
0 ignored issues
show
Bug Best Practice introduced by Ismayil Khayredinov
The expression return elgg_get_entities($options) also could return the type integer which is incompatible with the documented return type array|false.
Loading history...
980
	}
981
982
	/**
983
	 * Gets the number of entities from a specific relationship type
984
	 *
985
	 * @param string $relationship         Relationship type (eg "friends")
986
	 * @param bool   $inverse_relationship Invert relationship
987
	 *
988
	 * @return int|false The number of entities or false on failure
989
	 */
990
	public function countEntitiesFromRelationship($relationship, $inverse_relationship = false) {
991
		return elgg_get_entities([
0 ignored issues
show
Bug Best Practice introduced by Ismayil Khayredinov
The expression return elgg_get_entities...ship, 'count' => true)) also could return the type ElggEntity[] which is incompatible with the documented return type false|integer.
Loading history...
992
			'relationship' => $relationship,
993
			'relationship_guid' => $this->getGUID(),
994
			'inverse_relationship' => $inverse_relationship,
995
			'count' => true
996
		]);
997
	}
998
999
	/**
1000
	 * Can a user edit this entity?
1001
	 *
1002
	 * @tip Can be overridden by registering for the permissions_check plugin hook.
1003
	 *
1004
	 * @param int $user_guid The user GUID, optionally (default: logged in user)
1005
	 *
1006
	 * @return bool Whether this entity is editable by the given user.
1007
	 * @see elgg_set_ignore_access()
1008
	 */
1009 516
	public function canEdit($user_guid = 0) {
1010 516
		return _elgg_services()->userCapabilities->canEdit($this, $user_guid);
1011
	}
1012
1013
	/**
1014
	 * Can a user delete this entity?
1015
	 *
1016
	 * @tip Can be overridden by registering for the permissions_check:delete plugin hook.
1017
	 *
1018
	 * @param int $user_guid The user GUID, optionally (default: logged in user)
1019
	 *
1020
	 * @return bool Whether this entity is deletable by the given user.
1021
	 * @since 1.11
1022
	 * @see elgg_set_ignore_access()
1023
	 */
1024 516
	public function canDelete($user_guid = 0) {
1025 516
		return _elgg_services()->userCapabilities->canDelete($this, $user_guid);
1026
	}
1027
1028
	/**
1029
	 * Can a user edit metadata on this entity?
1030
	 *
1031
	 * If no specific metadata is passed, it returns whether the user can
1032
	 * edit any metadata on the entity.
1033
	 *
1034
	 * @tip Can be overridden by by registering for the permissions_check:metadata
1035
	 * plugin hook.
1036
	 *
1037
	 * @param \ElggMetadata $metadata  The piece of metadata to specifically check or null for any metadata
1038
	 * @param int           $user_guid The user GUID, optionally (default: logged in user)
1039
	 *
1040
	 * @return bool
1041
	 * @see elgg_set_ignore_access()
1042
	 */
1043 562
	public function canEditMetadata($metadata = null, $user_guid = 0) {
1044 562
		return _elgg_services()->userCapabilities->canEditMetadata($this, $user_guid, $metadata);
0 ignored issues
show
Deprecated Code introduced by Ismayil Khayredinov
The function Elgg\UserCapabilities::canEditMetadata() has been deprecated: 3.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1044
		return /** @scrutinizer ignore-deprecated */ _elgg_services()->userCapabilities->canEditMetadata($this, $user_guid, $metadata);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1045
	}
1046
1047
	/**
1048
	 * Can a user add an entity to this container
1049
	 *
1050
	 * @param int    $user_guid The GUID of the user creating the entity (0 for logged in user).
1051
	 * @param string $type      The type of entity we're looking to write
1052
	 * @param string $subtype   The subtype of the entity we're looking to write
1053
	 *
1054
	 * @return bool
1055
	 * @see elgg_set_ignore_access()
1056
	 */
1057 468
	public function canWriteToContainer($user_guid = 0, $type = 'all', $subtype = 'all') {
1058 468
		return _elgg_services()->userCapabilities->canWriteToContainer($this, $user_guid, $type, $subtype);
1059
	}
1060
1061
	/**
1062
	 * Can a user comment on an entity?
1063
	 *
1064
	 * @tip Can be overridden by registering for the permissions_check:comment,
1065
	 * <entity type> plugin hook.
1066
	 *
1067
	 * @param int  $user_guid User guid (default is logged in user)
1068
	 * @param bool $default   Default permission
1069
	 * @return bool
1070
	 */
1071 7
	public function canComment($user_guid = 0, $default = null) {
1072 7
		return _elgg_services()->userCapabilities->canComment($this, $user_guid, $default);
1073
	}
1074
1075
	/**
1076
	 * Can a user annotate an entity?