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
use Elgg\Database\QueryBuilder;
5
6
/**
7
 * The parent class for all Elgg Entities.
8
 *
9
 * An \ElggEntity is one of the basic data models in Elgg.
10
 * It is the primary means of storing and retrieving data from the database.
11
 * An \ElggEntity represents one row of the entities table.
12
 *
13
 * The \ElggEntity class handles CRUD operations for the entities table.
14
 * \ElggEntity should always be extended by another class to handle CRUD
15
 * operations on the type-specific table.
16
 *
17
 * \ElggEntity uses magic methods for get and set, so any property that isn't
18
 * declared will be assumed to be metadata and written to the database
19
 * as metadata on the object.  All children classes must declare which
20
 * properties are columns of the type table or they will be assumed
21
 * to be metadata.  See \ElggObject::initializeAttributes() for examples.
22
 *
23
 * Core supports 4 types of entities: \ElggObject, \ElggUser, \ElggGroup, and \ElggSite.
24
 *
25
 * @tip Plugin authors will want to extend the \ElggObject class, not this class.
26
 *
27
 * @package    Elgg.Core
28
 * @subpackage DataModel.Entities
29
 *
30
 * @property       string $type           object, user, group, or site (read-only after save)
31
 * @property       string $subtype        Further clarifies the nature of the entity
32
 * @property-read  int    $guid           The unique identifier for this entity (read only)
33
 * @property       int    $owner_guid     The GUID of the owner of this entity (usually the creator)
34
 * @property       int    $container_guid The GUID of the entity containing this entity
35
 * @property       int    $access_id      Specifies the visibility level of this entity
36
 * @property       int    $time_created   A UNIX timestamp of when the entity was created
37
 * @property-read  int    $time_updated   A UNIX timestamp of when the entity was last updated (automatically updated on save)
38
 * @property-read  int    $last_action    A UNIX timestamp of when the entity was last acted upon
39
 * @property       string $enabled        Is this entity enabled ('yes' or 'no')
40
 *
41
 * Metadata (the above are attributes)
42
 * @property       string $location       A location of the entity
43
 */
44
abstract class ElggEntity extends \ElggData implements
45
	Locatable, // Geocoding interface
46
	EntityIcon // Icon interface
47
{
48
49
	public static $primary_attr_names = [
50
		'guid',
51
		'type',
52
		'subtype',
53
		'owner_guid',
54
		'container_guid',
55
		'access_id',
56
		'time_created',
57
		'time_updated',
58
		'last_action',
59
		'enabled',
60
	];
61
62
	protected static $integer_attr_names = [
63
		'guid',
64
		'owner_guid',
65
		'container_guid',
66
		'access_id',
67
		'time_created',
68
		'time_updated',
69
		'last_action',
70
	];
71
72
	/**
73
	 * Holds metadata until entity is saved.  Once the entity is saved,
74
	 * metadata are written immediately to the database.
75
	 * @var array
76
	 */
77
	protected $temp_metadata = [];
78
79
	/**
80
	 * Holds annotations until entity is saved.  Once the entity is saved,
81
	 * annotations are written immediately to the database.
82
	 * @var array
83
	 */
84
	protected $temp_annotations = [];
85
86
	/**
87
	 * Holds private settings until entity is saved. Once the entity is saved,
88
	 * private settings are written immediately to the database.
89
	 * @var array
90
	 */
91
	protected $temp_private_settings = [];
92
93
	/**
94
	 * Volatile data structure for this object, allows for storage of data
95
	 * in-memory that isn't sync'd back to the metadata table.
96
	 * @var array
97
	 */
98
	protected $volatile = [];
99
100
	/**
101
	 * Holds the original (persisted) attribute values that have been changed but not yet saved.
102
	 * @var array
103
	 */
104
	protected $orig_attributes = [];
105
106
	/**
107
	 * @var bool
108
	 */
109
	protected $_is_cacheable = true;
110
111
	/**
112
	 * Holds metadata key/value pairs acquired from the metadata cache
113
	 * Entity metadata may have mutated since last call to __get,
114
	 * do not rely on this value for any business logic
115
	 * This storage is intended to help with debugging objects during dump,
116
	 * because otherwise it's hard to tell what the object is from it's attributes
117
	 *
118
	 * @var array
119
	 * @internal
120
	 */
121
	protected $_cached_metadata;
122
123
	/**
124
	 * Create a new entity.
125
	 *
126
	 * Plugin developers should only use the constructor to create a new entity.
127
	 * To retrieve entities, use get_entity() and the elgg_get_entities* functions.
128
	 *
129
	 * If no arguments are passed, it creates a new entity.
130
	 * If a database result is passed as a \stdClass instance, it instantiates
131
	 * that entity.
132
	 *
133
	 * @param stdClass $row Database row result. Default is null to create a new object.
134
	 *
135
	 * @throws IOException If cannot load remaining data from db
136
	 */
137 5971
	public function __construct(stdClass $row = null) {
138 5971
		$this->initializeAttributes();
139
140 5971
		if ($row && !$this->load($row)) {
141
			$msg = "Failed to load new " . get_class() . " for GUID:" . $row->guid;
142
			throw new \IOException($msg);
143
		}
144 5971
	}
145
146
	/**
147
	 * Initialize the attributes array.
148
	 *
149
	 * This is vital to distinguish between metadata and base parameters.
150
	 *
151
	 * @return void
152
	 */
153 1545
	protected function initializeAttributes() {
154 1545
		parent::initializeAttributes();
155
156 1545
		$this->attributes['guid'] = null;
157 1545
		$this->attributes['type'] = null;
158 1545
		$this->attributes['subtype'] = null;
159
160 1545
		$this->attributes['owner_guid'] = _elgg_services()->session->getLoggedInUserGuid();
161 1545
		$this->attributes['container_guid'] = _elgg_services()->session->getLoggedInUserGuid();
162
163 1545
		$this->attributes['access_id'] = ACCESS_PRIVATE;
164 1545
		$this->attributes['time_updated'] = null;
165 1545
		$this->attributes['last_action'] = null;
166 1545
		$this->attributes['enabled'] = "yes";
167
168 1545
		$this->attributes['type'] = $this->getType();
169 1545
	}
170
171
	/**
172
	 * Clone an entity
173
	 *
174
	 * Resets the guid so that the entity can be saved as a distinct entity from
175
	 * the original. Creation time will be set when this new entity is saved.
176
	 * The owner and container guids come from the original entity. The clone
177
	 * method copies metadata but does not copy annotations or private settings.
178
	 *
179
	 * @return void
180
	 */
181 2
	public function __clone() {
182 2
		$orig_entity = get_entity($this->guid);
183 2
		if (!$orig_entity) {
1 ignored issue
show
introduced by Brett Profitt
$orig_entity is of type ElggEntity, thus it always evaluated to true.
Loading history...
184
			_elgg_services()->logger->error("Failed to clone entity with GUID $this->guid");
185
			return;
186
		}
187
188 2
		$metadata_array = elgg_get_metadata([
189 2
			'guid' => $this->guid,
190 2
			'limit' => 0
191
		]);
192
193 2
		$this->attributes['guid'] = null;
194 2
		$this->attributes['time_created'] = null;
195 2
		$this->attributes['time_updated'] = null;
196 2
		$this->attributes['last_action'] = null;
197
198 2
		$this->attributes['subtype'] = $orig_entity->getSubtype();
199
200
		// copy metadata over to new entity - slightly convoluted due to
201
		// handling of metadata arrays
202 2
		if (is_array($metadata_array)) {
203
			// create list of metadata names
204 2
			$metadata_names = [];
205 2
			foreach ($metadata_array as $metadata) {
206 2
				$metadata_names[] = $metadata->name;
207
			}
208
			// arrays are stored with multiple enties per name
209 2
			$metadata_names = array_unique($metadata_names);
210
211
			// move the metadata over
212 2
			foreach ($metadata_names as $name) {
213 2
				$this->__set($name, $orig_entity->$name);
214
			}
215
		}
216 2
	}
217
218
	/**
219
	 * Set an attribute or metadata value for this entity
220
	 *
221
	 * Anything that is not an attribute is saved as metadata.
222
	 *
223
	 * Be advised that metadata values are cast to integer or string.
224
	 * You can save booleans, but they will be stored and returned as integers.
225
	 *
226
	 * @param string $name  Name of the attribute or metadata
227
	 * @param mixed  $value The value to be set
228
	 * @return void
229
	 * @see \ElggEntity::setMetadata()
230
	 */
231 1489
	public function __set($name, $value) {
232 1489
		if ($this->$name === $value) {
233
			// quick return if value is not changing
234 469
			return;
235
		}
236
237 1489
		if (array_key_exists($name, $this->attributes)) {
238
			// if an attribute is 1 (integer) and it's set to "1" (string), don't consider that a change.
239 741
			if (is_int($this->attributes[$name])
240 741
					&& is_string($value)
241 741
					&& ((string) $this->attributes[$name] === $value)) {
242 1
				return;
243
			}
244
245
			// keep original values
246 741
			if ($this->guid && !array_key_exists($name, $this->orig_attributes)) {
247 11
				$this->orig_attributes[$name] = $this->attributes[$name];
248
			}
249
250
			// Certain properties should not be manually changed!
251 741
			switch ($name) {
252 741
				case 'guid':
253 741
				case 'time_updated':
254 741
				case 'last_action':
255 1
					return;
256 740
				case 'access_id':
257 659
				case 'owner_guid':
258 524
				case 'container_guid':
259 632
					if ($value !== null) {
260 632
						$this->attributes[$name] = (int) $value;
261
					} else {
262
						$this->attributes[$name] = null;
263
					}
264 632
					break;
265
				default:
266 503
					$this->attributes[$name] = $value;
267 503
					break;
268
			}
269 740
			return;
270
		}
271
272 1476
		$this->setMetadata($name, $value);
273 1476
	}
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 6026
	public function __get($name) {
297 6026
		if (array_key_exists($name, $this->attributes)) {
298 6022
			return $this->attributes[$name];
299
		}
300
301 6024
		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 518
	public function getDisplayName() {
310 518
		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;
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 6024
	public function getMetadata($name) {
331 6024
		$metadata = $this->getAllMetadata();
332 6024
		return elgg_extract($name, $metadata);
333
	}
334
335
	/**
336
	 * Get all entity metadata
337
	 *
338
	 * @return array
339
	 */
340 6024
	public function getAllMetadata() {
341 6024
		if (!$this->guid) {
342 1161
			return array_map(function($values) {
343 1148
				return count($values) > 1 ? $values : $values[0];
344 1161
			}, $this->temp_metadata);
345
		}
346
347 6015
		$this->_cached_metadata = _elgg_services()->metadataCache->getAll($this->guid);
348
349 6015
		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 1476
	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 1476
		if (is_array($value)) {
391 212
			$value = array_values($value);
392
		} else {
393 1476
			$value = [$value];
394
		}
395
396
		// strip null values from array
397 1476
		$value = array_filter($value, function($var) {
398 1476
			return !is_null($var);
399 1476
		});
400
401 1476
		if (empty($this->guid)) {
402
			// unsaved entity. store in temp array
403 1157
			return $this->setTempMetadata($name, $value, $multiple);
404
		}
405
406
		// saved entity. persist md to db.
407 1315
		if (!$multiple) {
408 1315
			$current_metadata = $this->getMetadata($name);
409
410 1315
			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 1315
			if (count($value) > 1) {
428
				// new value is a multiple valued metadata
429 212
				$multiple = true;
430
			}
431
		}
432
433
		// create new metadata
434 1315
		foreach ($value as $value_tmp) {
435 1315
			$metadata = new ElggMetadata();
436 1315
			$metadata->entity_guid = $this->guid;
437 1315
			$metadata->name = $name;
438 1315
			$metadata->value_type = $value_type;
439 1315
			$metadata->value = $value_tmp;
440 1315
			$md_id = _elgg_services()->metadataTable->create($metadata, $multiple);
441 1315
			if ($md_id === false) {
442 1315
				return false;
443
			}
444
		}
445
446 1315
		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 1157
	protected function setTempMetadata($name, $value, $multiple = false) {
460
		// if overwrite, delete first
461 1157
		if (!$multiple) {
462 1157
			unset($this->temp_metadata[$name]);
463 1157
			if (count($value)) {
464
				// only save if value array contains data
465 1157
				$this->temp_metadata[$name] = $value;
466
			}
467 1157
			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 330
	public function deleteMetadata($name = null) {
492
493 330
		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 315
			'guid' => $this->guid,
510 315
			'limit' => 0
511
		];
512 315
		if ($name) {
513 67
			$options['metadata_name'] = $name;
514
		}
515
516 315
		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 34
	public function getVolatileData($name) {
527 34
		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 37
	public function setVolatileData($name, $value) {
539 37
		$this->volatile[$name] = $value;
540 37
	}
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 251
	public function deleteRelationships($relationship = null) {
556 251
		$relationship = (string) $relationship;
557 251
		$result = remove_entity_relationships($this->getGUID(), $relationship);
558 251
		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 41
			$value = (int) $value;
608
		}
609
		
610
		// checking if string value matches saved value (as get privatesettings does not know value type) and db column is a string
611 150
		if (strval($value) === $this->getPrivateSetting($name)) {
612
			// no need to update value
613 23
			return true;
614
		}
615
616 150
		if (!$this->guid) {
617 65
			$this->temp_private_settings[$name] = $value;
618
619 65
			return true;
620
		}
621
622 144
		return _elgg_services()->privateSettings->set($this, $name, $value);
623
	}
624
625
	/**
626
	 * Returns a private setting value
627
	 *
628
	 * @warning Private settings are always returned as strings
629
	 *          Make sure you can your values back to expected type
630
	 *
631
	 * @param string $name Name of the private setting
632
	 *
633
	 * @return string|null
634
	 * @throws DatabaseException
635
	 */
636 173
	public function getPrivateSetting($name) {
637 173
		if (!$this->guid) {
638 66
			return elgg_extract($name, $this->temp_private_settings);
639
		}
640
641 166
		return _elgg_services()->privateSettings->get($this, $name);
642
	}
643
644
	/**
645
	 * Returns all private settings
646
	 *
647
	 * @return array
648
	 * @throws DatabaseException
649
	 */
650 2
	public function getAllPrivateSettings() {
651 2
		if (!$this->guid) {
652
			return $this->temp_private_settings;
653
		}
654
655 2
		return _elgg_services()->privateSettings->getAllForEntity($this);
656
	}
657
658
	/**
659
	 * Removes private setting
660
	 *
661
	 * @param string $name Name of the private setting
662
	 *
663
	 * @return bool
664
	 * @throws DatabaseException
665
	 */
666 5
	public function removePrivateSetting($name) {
667 5
		if (!$this->guid) {
668
			unset($this->temp_private_settings[$name]);
669
			return true;
670
		}
671
672 5
		return _elgg_services()->privateSettings->remove($this, $name);
673
	}
674
675
	/**
676
	 * Removes all private settings
677
	 *
678
	 * @return bool
679
	 * @throws DatabaseException
680
	 */
681 252
	public function removeAllPrivateSettings() {
682 252
		if (!$this->guid) {
683
			$this->temp_private_settings = [];
684
			return true;
685
		}
686
687 252
		return _elgg_services()->privateSettings->removeAllForEntity($this);
688
	}
689
690
	/**
691
	 * Removes all river items related to this entity
692
	 *
693
	 * @return void
694
	 */
695 251
	public function removeAllRelatedRiverItems() {
696 251
		elgg_delete_river(['subject_guid' => $this->guid, 'limit' => false]);
697 251
		elgg_delete_river(['object_guid' => $this->guid, 'limit' => false]);
698 251
		elgg_delete_river(['target_guid' => $this->guid, 'limit' => false]);
699 251
	}
700
701
	/**
702
	 * Deletes all annotations on this object (annotations.entity_guid = $this->guid).
703
	 * If you pass a name, only annotations matching that name will be deleted.
704
	 *
705
	 * @warning Calling this with no or empty arguments will clear all annotations on the entity.
706
	 *
707
	 * @param null|string $name The annotations name to remove.
708
	 * @return bool
709
	 * @since 1.8
710
	 */
711 251
	public function deleteAnnotations($name = null) {
712
		$options = [
713 251
			'guid' => $this->guid,
714 251
			'limit' => 0
715
		];
716 251
		if ($name) {
717 1
			$options['annotation_name'] = $name;
718
		}
719
720 251
		return elgg_delete_annotations($options);
721
	}
722
723
	/**
724
	 * Deletes all annotations owned by this object (annotations.owner_guid = $this->guid).
725
	 * If you pass a name, only annotations matching that name will be deleted.
726
	 *
727
	 * @param null|string $name The name of annotations to delete.
728
	 * @return bool
729
	 * @since 1.8
730
	 */
731 251
	public function deleteOwnedAnnotations($name = null) {
732
		// access is turned off for this because they might
733
		// no longer have access to an entity they created annotations on
734
735 251
		$flags = ELGG_IGNORE_ACCESS;
736 251
		$callback = function() use ($name) {
737 251
			return elgg_delete_annotations([
738 251
				'annotation_owner_guid' => $this->guid,
739 251
				'limit' => 0,
740 251
				'annotation_name' => $name,
741
			]);
742 251
		};
743
744 251
		return elgg_call($flags, $callback);
745
	}
746
747
	/**
748
	 * Disables annotations for this entity, optionally based on name.
749
	 *
750
	 * @param string $name An options name of annotations to disable.
751
	 * @return bool
752
	 * @since 1.8
753
	 */
754 5
	public function disableAnnotations($name = '') {
755
		$options = [
756 5
			'guid' => $this->guid,
757 5
			'limit' => 0
758
		];
759 5
		if ($name) {
760
			$options['annotation_name'] = $name;
761
		}
762
763 5
		return elgg_disable_annotations($options);
764
	}
765
766
	/**
767
	 * Enables annotations for this entity, optionally based on name.
768
	 *
769
	 * @warning Before calling this, you must use {@link access_show_hidden_entities()}
770
	 *
771
	 * @param string $name An options name of annotations to enable.
772
	 * @return bool
773
	 * @since 1.8
774
	 */
775 3
	public function enableAnnotations($name = '') {
776
		$options = [
777 3
			'guid' => $this->guid,
778 3
			'limit' => 0
779
		];
780 3
		if ($name) {
781
			$options['annotation_name'] = $name;
782
		}
783
784 3
		return elgg_enable_annotations($options);
785
	}
786
787
	/**
788
	 * Helper function to return annotation calculation results
789
	 *
790
	 * @param string $name        The annotation name.
791
	 * @param string $calculation A valid MySQL function to run its values through
792
	 * @return mixed
793
	 */
794 2
	private function getAnnotationCalculation($name, $calculation) {
795
		$options = [
796 2
			'guid' => $this->getGUID(),
797
			'distinct' => false,
798 2
			'annotation_name' => $name,
799 2
			'annotation_calculation' => $calculation
800
		];
801
802 2
		return elgg_get_annotations($options);
803
	}
804
805
	/**
806
	 * Adds an annotation to an entity.
807
	 *
808
	 * @warning By default, annotations are private.
809
	 *
810
	 * @warning Annotating an unsaved entity more than once with the same name
811
	 *          will only save the last annotation.
812
	 *
813
	 * @todo Update temp_annotations to store an instance of ElggAnnotation and simply call ElggAnnotation::save(),
814
	 *       after entity is saved
815
	 *
816
	 * @param string $name       Annotation name
817
	 * @param mixed  $value      Annotation value
818
	 * @param int    $access_id  Access ID
819
	 * @param int    $owner_guid GUID of the annotation owner
820
	 * @param string $value_type The type of annotation value
821
	 *
822
	 * @return bool|int Returns int if an annotation is saved
823
	 */
824 103
	public function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_guid = 0, $value_type = "") {
825 103
		if ($this->guid) {
826 103
			if (!$owner_guid) {
827 102
				$owner_guid = _elgg_services()->session->getLoggedInUserGuid();
828
			}
829 103
			$annotation = new ElggAnnotation();
830 103
			$annotation->entity_guid = $this->guid;
831 103
			$annotation->name = $name;
832 103
			$annotation->value_type = $value_type;
833 103
			$annotation->value = $value;
834 103
			$annotation->owner_guid = $owner_guid;
835 103
			$annotation->access_id = $access_id;
836 103
			return $annotation->save();
837
		} else {
838 18
			$this->temp_annotations[$name] = $value;
839
		}
840 18
		return true;
841
	}
842
843
	/**
844
	 * Gets an array of annotations.
845
	 *
846
	 * To retrieve annotations on an unsaved entity, pass array('name' => [annotation name])
847
	 * as the options array.
848
	 *
849
	 * @param array $options Array of options for elgg_get_annotations() except guid.
850
	 *
851
	 * @return \ElggAnnotation[]|mixed
852
	 * @see elgg_get_annotations()
853
	 */
854 12
	public function getAnnotations(array $options = []) {
855 12
		if ($this->guid) {
856 12
			$options['guid'] = $this->guid;
857
858 12
			return elgg_get_annotations($options);
859
		} else {
860
			$name = elgg_extract('annotation_name', $options, '');
861
862
			if (isset($this->temp_annotations[$name])) {
863
				return [$this->temp_annotations[$name]];
864
			}
865
		}
866
867
		return [];
868
	}
869
870
	/**
871
	 * Count annotations.
872
	 *
873
	 * @param string $name The type of annotation.
874
	 *
875
	 * @return int
876
	 */
877 2
	public function countAnnotations($name = "") {
878 2
		return $this->getAnnotationCalculation($name, 'count');
879
	}
880
881
	/**
882
	 * Get the average of an integer type annotation.
883
	 *
884
	 * @param string $name Annotation name
885
	 *
886
	 * @return int
887
	 */
888
	public function getAnnotationsAvg($name) {
889
		return $this->getAnnotationCalculation($name, 'avg');
890
	}
891
892
	/**
893
	 * Get the sum of integer type annotations of a given name.
894
	 *
895
	 * @param string $name Annotation name
896
	 *
897
	 * @return int
898
	 */
899
	public function getAnnotationsSum($name) {
900
		return $this->getAnnotationCalculation($name, 'sum');
901
	}
902
903
	/**
904
	 * Get the minimum of integer type annotations of given name.
905
	 *
906
	 * @param string $name Annotation name
907
	 *
908
	 * @return int
909
	 */
910
	public function getAnnotationsMin($name) {
911
		return $this->getAnnotationCalculation($name, 'min');
912
	}
913
914
	/**
915
	 * Get the maximum of integer type annotations of a given name.
916
	 *
917
	 * @param string $name Annotation name
918
	 *
919
	 * @return int
920
	 */
921
	public function getAnnotationsMax($name) {
922
		return $this->getAnnotationCalculation($name, 'max');
923
	}
924
925
	/**
926
	 * Count the number of comments attached to this entity.
927
	 *
928
	 * @return int Number of comments
929
	 * @since 1.8.0
930
	 */
931 2
	public function countComments() {
932 2
		$params = ['entity' => $this];
933 2
		$num = _elgg_services()->hooks->trigger('comments:count', $this->getType(), $params);
934
935 2
		if (is_int($num)) {
936
			return $num;
937
		}
938
939 2
		return elgg_get_entities([
940 2
			'type' => 'object',
941 2
			'subtype' => 'comment',
942 2
			'container_guid' => $this->getGUID(),
943
			'count' => true,
944
			'distinct' => false,
945
		]);
946
	}
947
948
	/**
949
	 * Returns the ACLs owned by the entity
950
	 *
951
	 * @param array $options additional options to get the access collections with
952
	 *
953
	 * @return \ElggAccessCollection[]
954
	 *
955
	 * @see elgg_get_access_collections()
956
	 * @since 3.0
957
	 */
958 256
	public function getOwnedAccessCollections($options = []) {
959 256
		$options['owner_guid'] = $this->guid;
960 256
		return _elgg_services()->accessCollections->getEntityCollections($options);
961
	}
962
	
963
	/**
964
	 * Returns the first ACL owned by the entity with a given subtype
965
	 *
966
	 * @param string $subtype subtype of the ACL
967
	 *
968
	 * @return \ElggAccessCollection|false
969
	 *
970
	 * @since 3.0
971
	 */
972 9
	public function getOwnedAccessCollection($subtype) {
973 9
		if (!is_string($subtype) || $subtype === '') {
1 ignored issue
show
introduced by Jeroen Dalsem
The condition is_string($subtype) is always true.
Loading history...
974
			return false;
975
		}
976
		
977 9
		$acls = $this->getOwnedAccessCollections([
978 9
			'subtype' => $subtype,
979
		]);
980
		
981 9
		return elgg_extract(0, $acls, false);
982
	}
983
984
	/**
985
	 * Gets an array of entities with a relationship to this entity.
986
	 *
987
	 * @param array $options Options array. See elgg_get_entities()
988
	 *                       for a list of options. 'relationship_guid' is set to
989
	 *                       this entity.
990
	 *
991
	 * @return \ElggEntity[]|int|mixed
992
	 * @see elgg_get_entities()
993
	 */
994
	public function getEntitiesFromRelationship(array $options = []) {
995
		$options['relationship_guid'] = $this->guid;
996
		return elgg_get_entities($options);
997
	}
998
999
	/**
1000
	 * Gets the number of entities from a specific relationship type
1001
	 *
1002
	 * @param string $relationship         Relationship type (eg "friends")
1003
	 * @param bool   $inverse_relationship Invert relationship
1004
	 *
1005
	 * @return int
1006
	 */
1007
	public function countEntitiesFromRelationship($relationship, $inverse_relationship = false) {
1008
		return elgg_get_entities([
1009
			'relationship' => $relationship,
1010
			'relationship_guid' => $this->getGUID(),
1011
			'inverse_relationship' => $inverse_relationship,
1012
			'count' => true
1013
		]);
1014
	}
1015
1016
	/**
1017
	 * Can a user edit this entity?
1018
	 *
1019
	 * @tip Can be overridden by registering for the permissions_check plugin hook.
1020
	 *
1021
	 * @param int $user_guid The user GUID, optionally (default: logged in user)
1022
	 *
1023
	 * @return bool Whether this entity is editable by the given user.
1024
	 * @see elgg_set_ignore_access()
1025
	 */
1026 525
	public function canEdit($user_guid = 0) {
1027 525
		return _elgg_services()->userCapabilities->canEdit($this, $user_guid);
1028
	}
1029
1030
	/**
1031
	 * Can a user delete this entity?
1032
	 *
1033
	 * @tip Can be overridden by registering for the permissions_check:delete plugin hook.
1034
	 *
1035
	 * @param int $user_guid The user GUID, optionally (default: logged in user)
1036
	 *
1037
	 * @return bool Whether this entity is deletable by the given user.
1038
	 * @since 1.11
1039
	 * @see elgg_set_ignore_access()
1040
	 */
1041 521
	public function canDelete($user_guid = 0) {
1042 521
		return _elgg_services()->userCapabilities->canDelete($this, $user_guid);
1043
	}
1044
1045
	/**
1046
	 * Can a user edit metadata on this entity?
1047
	 *
1048
	 * If no specific metadata is passed, it returns whether the user can
1049
	 * edit any metadata on the entity.
1050
	 *
1051
	 * @param \ElggMetadata $metadata  The piece of metadata to specifically check or null for any metadata
1052
	 * @param int           $user_guid The user GUID, optionally (default: logged in user)
1053
	 *
1054
	 * @return bool
1055
	 * @see elgg_set_ignore_access()
1056
	 * @deprecated 3.0
1057
	 */
1058 6
	public function canEditMetadata($metadata = null, $user_guid = 0) {
1059 6
		return _elgg_services()->userCapabilities->canEditMetadata($this, $user_guid, $metadata);
1060
	}
1061
1062
	/**
1063
	 * Can a user add an entity to this container
1064
	 *
1065
	 * @param int    $user_guid The GUID of the user creating the entity (0 for logged in user).
1066
	 * @param string $type      The type of entity we're looking to write
1067
	 * @param string $subtype   The subtype of the entity we're looking to write
1068
	 *
1069
	 * @return bool
1070
	 * @see elgg_set_ignore_access()
1071
	 */
1072 473
	public function canWriteToContainer($user_guid = 0, $type = 'all', $subtype = 'all') {
1073 473
		return _elgg_services()->userCapabilities->canWriteToContainer($this, $user_guid, $type, $subtype);
1074
	}
1075
1076
	/**
1077
	 * Can a user comment on an entity?
1078
	 *
1079
	 * @tip Can be overridden by registering for the permissions_check:comment,
1080
	 * <entity type> plugin hook.
1081
	 *
1082
	 * @param int  $user_guid User guid (default is logged in user)
1083
	 * @param bool $default   Default permission
1084
	 * @return bool
1085
	 */
1086 7
	public function canComment($user_guid = 0, $default = null) {
1087 7
		return _elgg_services()->userCapabilities->canComment($this, $user_guid, $default);
1088
	}
1089
1090
	/**
1091
	 * Can a user annotate an entity?
1092
	 *
1093
	 * @tip Can be overridden by registering for the plugin hook [permissions_check:annotate:<name>,
1094
	 * <entity type>] or [permissions_check:annotate, <entity type>]. The hooks are called in that order.
1095
	 *
1096
	 * @tip If you want logged out users to annotate an object, do not call
1097
	 * canAnnotate(). It's easier than using the plugin hook.
1098
	 *
1099
	 * @param int    $user_guid       User guid (default is logged in user)
1100
	 * @param string $annotation_name The name of the annotation (default is unspecified)
1101
	 *
1102
	 * @return bool
1103
	 */
1104 8
	public function canAnnotate($user_guid = 0, $annotation_name = '') {
1105 8
		return _elgg_services()->userCapabilities->canAnnotate($this, $user_guid, $annotation_name);
1106
	}
1107
1108
	/**
1109
	 * Returns the access_id.
1110
	 *
1111
	 * @return int The access ID
1112
	 */
1113 1
	public function getAccessID() {
1114 1
		return $this->access_id;
1115
	}
1116
1117
	/**
1118
	 * Returns the guid.
1119
	 *
1120
	 * @return int|null GUID
1121
	 */
1122 685
	public function getGUID() {
1123 685
		return $this->guid;
1124
	}
1125
1126
	/**
1127
	 * Returns the entity type
1128
	 *
1129
	 * @return string The entity type
1130
	 */
1131 1
	public function getType() {
1132
		// this is just for the PHPUnit mocking framework
1133 1
		return $this->type;
1134
	}
1135
1136
	/**
1137
	 * Get the entity subtype
1138
	 *
1139
	 * @return string The entity subtype
1140
	 */
1141 686
	public function getSubtype() {
1142 686
		return $this->attributes['subtype'];
1143
	}
1144
1145
	/**
1146
	 * Get the guid of the entity's owner.
1147
	 *
1148
	 * @return int The owner GUID
1149
	 */
1150 170
	public function getOwnerGUID() {
1151 170
		return (int) $this->owner_guid;
1152
	}
1153
1154
	/**
1155
	 * Gets the \ElggEntity that owns this entity.
1156
	 *
1157
	 * @return \ElggEntity The owning entity
1158
	 */
1159 474
	public function getOwnerEntity() {
1160 474
		return get_entity($this->owner_guid);
1161
	}
1162
1163
	/**
1164
	 * Set the container for this object.
1165
	 *
1166
	 * @param int $container_guid The ID of the container.
1167
	 *
1168
	 * @return int
1169
	 */
1170 1
	public function setContainerGUID($container_guid) {
1171 1
		return $this->container_guid = (int) $container_guid;
1172
	}
1173
1174
	/**
1175
	 * Gets the container GUID for this entity.
1176
	 *
1177
	 * @return int
1178
	 */
1179 566
	public function getContainerGUID() {
1180 566
		return (int) $this->container_guid;
1181
	}
1182
1183
	/**
1184