Completed
Push — master ( be3b3f...d1dc3f )
by Ismayil
13:04
created

ElggEntity::disable()   D

Complexity

Conditions 13
Paths 35

Size

Total Lines 90
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 43
CRAP Score 15.113

Importance

Changes 0
Metric Value
cc 13
eloc 50
nc 35
nop 2
dl 0
loc 90
ccs 43
cts 56
cp 0.7679
crap 15.113
rs 4.9922
c 0
b 0
f 0

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
/**
4
 * The parent class for all Elgg Entities.
5
 *
6
 * An \ElggEntity is one of the basic data models in Elgg.  It is the primary
7
 * means of storing and retrieving data from the database.  An \ElggEntity
8
 * represents one row of the entities table.
9
 *
10
 * The \ElggEntity class handles CRUD operations for the entities table.
11
 * \ElggEntity should always be extended by another class to handle CRUD
12
 * operations on the type-specific table.
13
 *
14
 * \ElggEntity uses magic methods for get and set, so any property that isn't
15
 * declared will be assumed to be metadata and written to the database
16
 * as metadata on the object.  All children classes must declare which
17
 * properties are columns of the type table or they will be assumed
18
 * to be metadata.  See \ElggObject::initializeAttributes() for examples.
19
 *
20
 * Core supports 4 types of entities: \ElggObject, \ElggUser, \ElggGroup, and
21
 * \ElggSite.
22
 *
23
 * @tip Plugin authors will want to extend the \ElggObject class, not this class.
24
 *
25
 * @package    Elgg.Core
26
 * @subpackage DataModel.Entities
27
 *
28
 * @property       string $type           object, user, group, or site (read-only after save)
29
 * @property-write string $subtype        Further clarifies the nature of the entity (this should not be read)
30
 * @property-read  int    $guid           The unique identifier for this entity (read only)
31
 * @property       int    $owner_guid     The GUID of the owner of this entity (usually the creator)
32
 * @property       int    $container_guid The GUID of the entity containing this entity
33
 * @property       int    $access_id      Specifies the visibility level of this entity
34
 * @property       int    $time_created   A UNIX timestamp of when the entity was created
35
 * @property-read  int    $time_updated   A UNIX timestamp of when the entity was last updated (automatically updated on save)
36
 * @property-read  int    $last_action    A UNIX timestamp of when the entity was last acted upon
37
 * @property       string $enabled        Is this entity enabled ('yes' or 'no')
38
 *
39
 * Metadata (the above are attributes)
40
 * @property       string $location       A location of the entity
41
 */
42
abstract class ElggEntity extends \ElggData implements
43
	Locatable, // Geocoding interface
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces before interface name; 1 found
Loading history...
44
	\Elgg\EntityIcon // Icon interface
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces before interface name; 1 found
Loading history...
45
{
46
	
47
	/**
48
	 * If set, overrides the value of getURL()
49
	 */
50
	protected $url_override;
51
52
	/**
53
	 * Holds metadata until entity is saved.  Once the entity is saved,
54
	 * metadata are written immediately to the database.
55
	 */
56
	protected $temp_metadata = array();
57
58
	/**
59
	 * Holds annotations until entity is saved.  Once the entity is saved,
60
	 * annotations are written immediately to the database.
61
	 */
62
	protected $temp_annotations = array();
63
64
	/**
65
	 * Holds private settings until entity is saved. Once the entity is saved,
66
	 * private settings are written immediately to the database.
67
	 */
68
	protected $temp_private_settings = array();
69
70
	/**
71
	 * Volatile data structure for this object, allows for storage of data
72
	 * in-memory that isn't sync'd back to the metadata table.
73
	 */
74
	protected $volatile = array();
75
76
	/**
77
	 * Holds the original (persisted) attribute values that have been changed but not yet saved.
78
	 */
79
	protected $orig_attributes = array();
80
	
81
	/**
82
	 * Initialize the attributes array.
83
	 *
84
	 * This is vital to distinguish between metadata and base parameters.
85
	 *
86
	 * @return void
87
	 */
88 306
	protected function initializeAttributes() {
89 306
		parent::initializeAttributes();
90
91 306
		$this->attributes['guid'] = null;
92 306
		$this->attributes['type'] = null;
93 306
		$this->attributes['subtype'] = null;
94
95 306
		$this->attributes['owner_guid'] = _elgg_services()->session->getLoggedInUserGuid();
96 306
		$this->attributes['container_guid'] = _elgg_services()->session->getLoggedInUserGuid();
97
98 306
		$this->attributes['access_id'] = ACCESS_PRIVATE;
99 306
		$this->attributes['time_updated'] = null;
100 306
		$this->attributes['last_action'] = null;
101 306
		$this->attributes['enabled'] = "yes";
102 306
	}
103
104
	/**
105
	 * Clone an entity
106
	 *
107
	 * Resets the guid so that the entity can be saved as a distinct entity from
108
	 * the original. Creation time will be set when this new entity is saved.
109
	 * The owner and container guids come from the original entity. The clone
110
	 * method copies metadata but does not copy annotations or private settings.
111
	 *
112
	 * @note metadata will have its owner and access id set when the entity is saved
113
	 * and it will be the same as that of the entity.
114
	 *
115
	 * @return void
116
	 */
117 70
	public function __clone() {
118 1
		$orig_entity = get_entity($this->guid);
119 1
		if (!$orig_entity) {
120
			_elgg_services()->logger->error("Failed to clone entity with GUID $this->guid");
121
			return;
122
		}
123
124 6
		$metadata_array = elgg_get_metadata(array(
125 6
			'guid' => $this->guid,
126
			'limit' => 0
127 1
		));
128
129 1
		$this->attributes['guid'] = "";
130
131 1
		$this->attributes['subtype'] = $orig_entity->getSubtype();
132
133
		// copy metadata over to new entity - slightly convoluted due to
134
		// handling of metadata arrays
135 1
		if (is_array($metadata_array)) {
136
			// create list of metadata names
137 1
			$metadata_names = array();
138 1
			foreach ($metadata_array as $metadata) {
139
				$metadata_names[] = $metadata['name'];
140 1
			}
141
			// arrays are stored with multiple enties per name
142 1
			$metadata_names = array_unique($metadata_names);
143
144
			// move the metadata over
145 1
			foreach ($metadata_names as $name) {
146
				$this->__set($name, $orig_entity->$name);
147 1
			}
148 1
		}
149 70
	}
150
151
	/**
152
	 * Set an attribute or metadata value for this entity
153
	 *
154
	 * Anything that is not an attribute is saved as metadata.
155
	 *
156
	 * @warning Metadata set this way will inherit the entity's owner and
157
	 * access ID. If you want more control over metadata, use \ElggEntity::setMetadata()
158
	 *
159
	 * @param string $name  Name of the attribute or metadata
160
	 * @param mixed  $value The value to be set
161
	 * @return void
162
	 * @see \ElggEntity::setMetadata()
163
	 */
164 260
	public function __set($name, $value) {
165 260
		if ($this->$name === $value) {
166
			// quick return if value is not changing
167 201
			return;
168
		}
169
170 118
		if (array_key_exists($name, $this->attributes)) {
171
			// if an attribute is 1 (integer) and it's set to "1" (string), don't consider that a change.
172 114
			if (is_int($this->attributes[$name])
173 114
					&& is_string($value)
174 114
					&& ((string)$this->attributes[$name] === $value)) {
175 18
				return;
176 18
			}
177
178
			// Due to https://github.com/Elgg/Elgg/pull/5456#issuecomment-17785173, certain attributes
179
			// will store empty strings as null in the DB. In the somewhat common case that we're re-setting
180
			// the value to empty string, don't consider this a change.
181 114
			if (in_array($name, ['title', 'name', 'description'])
182 114
					&& $this->attributes[$name] === null
183 114
					&& $value === "") {
184
				return;
185 1
			}
186
187
			// keep original values
188 114
			if ($this->guid && !array_key_exists($name, $this->orig_attributes)) {
189 5
				$this->orig_attributes[$name] = $this->attributes[$name];
190 5
			}
191
192
			// Certain properties should not be manually changed!
193
			switch ($name) {
194 114
				case 'guid':
195 114
				case 'time_updated':
196 120
				case 'last_action':
197 3
					return;
198
					break;
0 ignored issues
show
Unused Code introduced by
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...
199 111
				case 'access_id':
200 111
				case 'owner_guid':
201 111
				case 'container_guid':
202 99
					if ($value !== null) {
203 99
						$this->attributes[$name] = (int)$value;
204 99
					} else {
205
						$this->attributes[$name] = null;
206
					}
207 99
					break;
208 16
				default:
209 16
					$this->attributes[$name] = $value;
210 16
					break;
211 39
			}
212 111
			return;
213
		}
214
215 103
		$this->setMetadata($name, $value);
216 103
	}
217
218
	/**
219
	 * Get the original values of attribute(s) that have been modified since the entity was persisted.
220
	 *
221
	 * @return array
222
	 */
223 22
	public function getOriginalAttributes() {
224 22
		return $this->orig_attributes;
225
	}
226
227
	/**
228
	 * Get an attribute or metadata value
229
	 *
230
	 * If the name matches an attribute, the attribute is returned. If metadata
231
	 * does not exist with that name, a null is returned.
232
	 *
233
	 * This only returns an array if there are multiple values for a particular
234
	 * $name key.
235
	 *
236
	 * @param string $name Name of the attribute or metadata
237
	 * @return mixed
238
	 */
239 301
	public function __get($name) {
240 301
		if (array_key_exists($name, $this->attributes)) {
241 301
			if ($name === 'subtype' && $this->attributes['guid']) {
242 239
				_elgg_services()->logger->warn('Reading ->subtype on a persisted entity is unreliable.');
243 239
			}
244 301
			return $this->attributes[$name];
245
		}
246
247 126
		return $this->getMetadata($name);
248
	}
249
250
	/**
251
	 * Get the entity's display name
252
	 *
253
	 * @return string The title or name of this entity.
254
	 */
255
	abstract public function getDisplayName();
256
257
	/**
258
	 * Sets the title or name of this entity.
259
	 *
260
	 * @param string $displayName The title or name of this entity.
261
	 * @return void
262
	 */
263
	abstract public function setDisplayName($displayName);
264
265
	/**
266
	 * Return the value of a piece of metadata.
267
	 *
268
	 * @param string $name Name
269
	 *
270
	 * @return mixed The value, or null if not found.
271
	 */
272 126
	public function getMetadata($name) {
273 126
		$guid = $this->getGUID();
274
275 126
		if (!$guid) {
276 100
			if (isset($this->temp_metadata[$name])) {
277
				// md is returned as an array only if more than 1 entry
278 93
				if (count($this->temp_metadata[$name]) == 1) {
279 93
					return $this->temp_metadata[$name][0];
280
				} else {
281
					return $this->temp_metadata[$name];
282
				}
283
			} else {
284 100
				return null;
285
			}
286
		}
287
288
		// upon first cache miss, just load/cache all the metadata and retry.
289
		// if this works, the rest of this function may not be needed!
290 70
		$cache = _elgg_services()->metadataCache;
291 70
		if ($cache->isLoaded($guid)) {
292 51
			return $cache->getSingle($guid, $name);
293
		} else {
294 70
			$cache->populateFromEntities(array($guid));
295
			// in case ignore_access was on, we have to check again...
296 70
			if ($cache->isLoaded($guid)) {
297 70
				return $cache->getSingle($guid, $name);
298
			}
299
		}
300
301
		$md = elgg_get_metadata(array(
302
			'guid' => $guid,
303
			'metadata_name' => $name,
304
			'limit' => 0,
305
			'distinct' => false,
306
		));
307
308 1
		$value = null;
309
310
		if ($md && !is_array($md)) {
311
			$value = $md->value;
312
		} elseif (count($md) == 1) {
313
			$value = $md[0]->value;
314
		} else if ($md && is_array($md)) {
315
			$value = metadata_array_to_values($md);
316
		}
317
318
		return $value;
319
	}
320
321
	/**
322
	 * Unset a property from metadata or attribute.
323
	 *
324
	 * @warning If you use this to unset an attribute, you must save the object!
325
	 *
326
	 * @param string $name The name of the attribute or metadata.
327
	 *
328
	 * @return void
329
	 * @todo some attributes should be set to null or other default values
330
	 */
331 58
	public function __unset($name) {
332 58
		if (array_key_exists($name, $this->attributes)) {
333 1
			$this->attributes[$name] = "";
334 1
		} else {
335 57
			$this->deleteMetadata($name);
336
		}
337 58
	}
338
339
	/**
340
	 * Set metadata on this entity.
341
	 *
342
	 * Plugin developers usually want to use the magic set method ($entity->name = 'value').
343
	 * Use this method if you want to explicitly set the owner or access of the metadata.
344
	 * You cannot set the owner/access before the entity has been saved.
345
	 *
346
	 * @param string $name       Name of the metadata
347
	 * @param mixed  $value      Value of the metadata (doesn't support assoc arrays)
348
	 * @param string $value_type 'text', 'integer', or '' for automatic detection
349
	 * @param bool   $multiple   Allow multiple values for a single name.
350
	 *                           Does not support associative arrays.
351
	 * @param int    $owner_guid GUID of entity that owns the metadata.
352
	 *                           Default is owner of entity.
353
	 * @param int    $access_id  Who can read the metadata relative to the owner (deprecated).
354
	 *                           Default is the access level of the entity. Use ACCESS_PUBLIC for
355
	 *                           compatibility with Elgg 3.0
356
	 *
357
	 * @return bool
358
	 * @throws InvalidArgumentException
359
	 */
360 103
	public function setMetadata($name, $value, $value_type = '', $multiple = false, $owner_guid = 0, $access_id = null) {
361
362
		// normalize value to an array that we will loop over
363
		// remove indexes if value already an array.
364 103
		if (is_array($value)) {
365 1
			$value = array_values($value);
366 1
		} else {
367 103
			$value = array($value);
368
		}
369
370
		// saved entity. persist md to db.
371 103
		if ($this->guid) {
372
			// if overwriting, delete first.
373 50
			if (!$multiple) {
374
				$options = array(
375 50
					'guid' => $this->getGUID(),
376 50
					'metadata_name' => $name,
377
					'limit' => 0
378 50
				);
379
				// @todo in 1.9 make this return false if can't add metadata
380
				// https://github.com/elgg/elgg/issues/4520
381
				//
382
				// need to remove access restrictions right now to delete
383
				// because this is the expected behavior
384 50
				$ia = elgg_set_ignore_access(true);
385 50
				if (false === elgg_delete_metadata($options)) {
386 50
					return false;
387
				}
388
				elgg_set_ignore_access($ia);
389
			}
390
391
			$owner_guid = $owner_guid ? (int)$owner_guid : $this->owner_guid;
392
393
			if ($access_id === null) {
394
				$access_id = $this->access_id;
395
			} elseif ($access_id != ACCESS_PUBLIC) {
396
				$access_id = (int)$access_id;
397
				elgg_deprecated_notice('Setting $access_id to a value other than ACCESS_PUBLIC is deprecated. '
398
					. 'All metadata will be public in 3.0.', '2.3');
399
			}
400
401
			// add new md
402
			$result = true;
403
			foreach ($value as $value_tmp) {
404
				// at this point $value is appended because it was cleared above if needed.
405
				$md_id = _elgg_services()->metadataTable->create($this->guid, $name, $value_tmp, $value_type,
406
						$owner_guid, $access_id, true);
407
				if (!$md_id) {
408
					return false;
409
				}
410
			}
411
412
			return $result;
413
		} else {
414
			// unsaved entity. store in temp array
415
416
			// returning single entries instead of an array of 1 element is decided in
417
			// getMetaData(), just like pulling from the db.
418
419 96
			if ($owner_guid != 0 || $access_id !== null) {
420
				$msg = "owner guid and access id cannot be used in \ElggEntity::setMetadata() until entity is saved.";
421
				throw new \InvalidArgumentException($msg);
422
			}
423
424
			// if overwrite, delete first
425 96
			if (!$multiple || !isset($this->temp_metadata[$name])) {
426 96
				$this->temp_metadata[$name] = array();
427 96
			}
428
429
			// add new md
430 96
			$this->temp_metadata[$name] = array_merge($this->temp_metadata[$name], $value);
431 96
			return true;
432
		}
433
	}
434
435
	/**
436
	 * Deletes all metadata on this object (metadata.entity_guid = $this->guid).
437
	 * If you pass a name, only metadata matching that name will be deleted.
438
	 *
439
	 * @warning Calling this with no $name will clear all metadata on the entity.
440
	 *
441
	 * @param null|string $name The name of the metadata to remove.
442
	 * @return bool
443
	 * @since 1.8
444
	 */
445 58
	public function deleteMetadata($name = null) {
446
447 58
		if (!$this->guid) {
448 15
			return false;
449
		}
450
451
		$options = array(
452 43
			'guid' => $this->guid,
453
			'limit' => 0
454 43
		);
455 43
		if ($name) {
456 42
			$options['metadata_name'] = $name;
457 42
		}
458
459 43
		return elgg_delete_metadata($options);
460
	}
461
462
	/**
463
	 * Deletes all metadata owned by this object (metadata.owner_guid = $this->guid).
464
	 * If you pass a name, only metadata matching that name will be deleted.
465
	 *
466
	 * @param null|string $name The name of metadata to delete.
467
	 * @return bool
468
	 * @since 1.8
469
	 */
470 1
	public function deleteOwnedMetadata($name = null) {
471
		// access is turned off for this because they might
472
		// no longer have access to an entity they created metadata on.
473 1
		$ia = elgg_set_ignore_access(true);
474
		$options = array(
475 1
			'metadata_owner_guid' => $this->guid,
476
			'limit' => 0
477 1
		);
478 1
		if ($name) {
479
			$options['metadata_name'] = $name;
480
		}
481
482 1
		$r = elgg_delete_metadata($options);
483 1
		elgg_set_ignore_access($ia);
484 1
		return $r;
485
	}
486
487
	/**
488
	 * Disables metadata for this entity, optionally based on name.
489
	 *
490
	 * @param string $name An options name of metadata to disable.
491
	 * @return bool
492
	 * @since 1.8
493
	 */
494 1
	public function disableMetadata($name = '') {
495
		$options = array(
496 1
			'guid' => $this->guid,
497
			'limit' => 0
498 1
		);
499 1
		if ($name) {
500
			$options['metadata_name'] = $name;
501
		}
502
503 1
		return elgg_disable_metadata($options);
504
	}
505
506
	/**
507
	 * Enables metadata for this entity, optionally based on name.
508
	 *
509
	 * @warning Before calling this, you must use {@link access_show_hidden_entities()}
510
	 *
511
	 * @param string $name An options name of metadata to enable.
512
	 * @return bool
513
	 * @since 1.8
514
	 */
515
	public function enableMetadata($name = '') {
516
		$options = array(
517
			'guid' => $this->guid,
518
			'limit' => 0
519
		);
520
		if ($name) {
521
			$options['metadata_name'] = $name;
522
		}
523
524
		return elgg_enable_metadata($options);
525
	}
526
527
	/**
528
	 * Get a piece of volatile (non-persisted) data on this entity.
529
	 *
530
	 * @param string $name The name of the volatile data
531
	 *
532
	 * @return mixed The value or null if not found.
533
	 */
534 1
	public function getVolatileData($name) {
535 1
		return array_key_exists($name, $this->volatile) ? $this->volatile[$name] : null;
536
	}
537
538
	/**
539
	 * Set a piece of volatile (non-persisted) data on this entity
540
	 *
541
	 * @param string $name  Name
542
	 * @param mixed  $value Value
543
	 *
544
	 * @return void
545
	 */
546 7
	public function setVolatileData($name, $value) {
547 7
		$this->volatile[$name] = $value;
548 7
	}
549
550
	/**
551
	 * Cache the entity in a persisted cache
552
	 *
553
	 * @param ElggSharedMemoryCache $cache       Memcache or null cache
554
	 * @param int                   $last_action Last action time
555
	 *
556
	 * @return void
557
	 * @access private
558
	 * @internal
559
	 */
560 56
	public function storeInPersistedCache(\ElggSharedMemoryCache $cache, $last_action = 0) {
561 56
		$tmp = $this->volatile;
562
563
		// don't store volatile data
564 56
		$this->volatile = [];
565 56
		if ($last_action) {
566
			$this->attributes['last_action'] = (int)$last_action;
567
		}
568 56
		$cache->save($this->guid, $this);
0 ignored issues
show
Documentation introduced by
$this is of type this<ElggEntity>, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
569
570 56
		$this->volatile = $tmp;
571 56
	}
572
573
	/**
574
	 * Remove all relationships to and from this entity.
575
	 * If you pass a relationship name, only relationships matching that name
576
	 * will be deleted.
577
	 *
578
	 * @warning Calling this with no $relationship will clear all relationships
579
	 * for this entity.
580
	 *
581
	 * @param null|string $relationship The name of the relationship to remove.
582
	 * @return bool
583
	 * @see \ElggEntity::addRelationship()
584
	 * @see \ElggEntity::removeRelationship()
585
	 */
586 1
	public function deleteRelationships($relationship = null) {
587 1
		$relationship = (string)$relationship;
588 1
		$result = remove_entity_relationships($this->getGUID(), $relationship);
589 1
		return $result && remove_entity_relationships($this->getGUID(), $relationship, true);
590
	}
591
592
	/**
593
	 * Add a relationship between this an another entity.
594
	 *
595
	 * @tip Read the relationship like "This entity is a $relationship of $guid_two."
596
	 *
597
	 * @param int    $guid_two     GUID of the target entity of the relationship.
598
	 * @param string $relationship The type of relationship.
599
	 *
600
	 * @return bool
601
	 * @see \ElggEntity::removeRelationship()
602
	 * @see \ElggEntity::deleteRelationships()
603
	 */
604 1
	public function addRelationship($guid_two, $relationship) {
605 1
		return add_entity_relationship($this->getGUID(), $relationship, $guid_two);
606
	}
607
608
	/**
609
	 * Remove a relationship
610
	 *
611
	 * @param int    $guid_two     GUID of the target entity of the relationship.
612
	 * @param string $relationship The type of relationship.
613
	 *
614
	 * @return bool
615
	 * @see \ElggEntity::addRelationship()
616
	 * @see \ElggEntity::deleteRelationships()
617
	 */
618 1
	public function removeRelationship($guid_two, $relationship) {
619 1
		return remove_entity_relationship($this->getGUID(), $relationship, $guid_two);
620
	}
621
622
	/**
623
	 * Adds a private setting to this entity.
624
	 *
625
	 * Private settings are similar to metadata but will not
626
	 * be searched and there are fewer helper functions for them.
627
	 *
628
	 * @param string $name  Name of private setting
629
	 * @param mixed  $value Value of private setting
630
	 *
631
	 * @return bool
632
	 */
633 10
	public function setPrivateSetting($name, $value) {
634 10
		if ((int) $this->guid > 0) {
635 4
			return set_private_setting($this->getGUID(), $name, $value);
636
		} else {
637 10
			$this->temp_private_settings[$name] = $value;
638 10
			return true;
639
		}
640
	}
641
642
	/**
643
	 * Returns a private setting value
644
	 *
645
	 * @param string $name Name of the private setting
646
	 *
647
	 * @return mixed Null if the setting does not exist
648
	 */
649 10
	public function getPrivateSetting($name) {
650 10
		if ((int) ($this->guid) > 0) {
651 4
			return get_private_setting($this->getGUID(), $name);
652
		} else {
653 10
			if (isset($this->temp_private_settings[$name])) {
654 9
				return $this->temp_private_settings[$name];
655
			}
656
		}
657 2
		return null;
658
	}
659
660
	/**
661
	 * Removes private setting
662
	 *
663
	 * @param string $name Name of the private setting
664
	 *
665
	 * @return bool
666
	 */
667
	public function removePrivateSetting($name) {
668
		return remove_private_setting($this->getGUID(), $name);
669
	}
670
671
	/**
672
	 * Deletes all annotations on this object (annotations.entity_guid = $this->guid).
673
	 * If you pass a name, only annotations matching that name will be deleted.
674
	 *
675
	 * @warning Calling this with no or empty arguments will clear all annotations on the entity.
676
	 *
677
	 * @param null|string $name The annotations name to remove.
678
	 * @return bool
679
	 * @since 1.8
680
	 */
681 1
	public function deleteAnnotations($name = null) {
682
		$options = array(
683 1
			'guid' => $this->guid,
684
			'limit' => 0
685 1
		);
686 1
		if ($name) {
687
			$options['annotation_name'] = $name;
688
		}
689
690 1
		return elgg_delete_annotations($options);
691
	}
692
693
	/**
694
	 * Deletes all annotations owned by this object (annotations.owner_guid = $this->guid).
695
	 * If you pass a name, only annotations matching that name will be deleted.
696
	 *
697
	 * @param null|string $name The name of annotations to delete.
698
	 * @return bool
699
	 * @since 1.8
700
	 */
701 1
	public function deleteOwnedAnnotations($name = null) {
702
		// access is turned off for this because they might
703
		// no longer have access to an entity they created annotations on.
704 1
		$ia = elgg_set_ignore_access(true);
705
		$options = array(
706 1
			'annotation_owner_guid' => $this->guid,
707
			'limit' => 0
708 1
		);
709 1
		if ($name) {
710
			$options['annotation_name'] = $name;
711
		}
712
713 1
		$r = elgg_delete_annotations($options);
714 1
		elgg_set_ignore_access($ia);
715 1
		return $r;
716
	}
717
718
	/**
719
	 * Disables annotations for this entity, optionally based on name.
720
	 *
721
	 * @param string $name An options name of annotations to disable.
722
	 * @return bool
723
	 * @since 1.8
724
	 */
725 1
	public function disableAnnotations($name = '') {
726
		$options = array(
727 1
			'guid' => $this->guid,
728
			'limit' => 0
729 1
		);
730 1
		if ($name) {
731
			$options['annotation_name'] = $name;
732
		}
733
734 1
		return elgg_disable_annotations($options);
735
	}
736
737
	/**
738
	 * Enables annotations for this entity, optionally based on name.
739
	 *
740
	 * @warning Before calling this, you must use {@link access_show_hidden_entities()}
741
	 *
742
	 * @param string $name An options name of annotations to enable.
743
	 * @return bool
744
	 * @since 1.8
745
	 */
746
	public function enableAnnotations($name = '') {
747
		$options = array(
748
			'guid' => $this->guid,
749
			'limit' => 0
750
		);
751
		if ($name) {
752
			$options['annotation_name'] = $name;
753
		}
754
755
		return elgg_enable_annotations($options);
756
	}
757
758
	/**
759
	 * Helper function to return annotation calculation results
760
	 *
761
	 * @param string $name        The annotation name.
762
	 * @param string $calculation A valid MySQL function to run its values through
763
	 * @return mixed
764
	 */
765
	private function getAnnotationCalculation($name, $calculation) {
766
		$options = array(
767
			'guid' => $this->getGUID(),
768
			'distinct' => false,
769
			'annotation_name' => $name,
770
			'annotation_calculation' => $calculation
771
		);
772
773
		return elgg_get_annotations($options);
774
	}
775
776
	/**
777
	 * Adds an annotation to an entity.
778
	 *
779
	 * @warning By default, annotations are private.
780
	 *
781
	 * @warning Annotating an unsaved entity more than once with the same name
782
	 *          will only save the last annotation.
783
	 *
784
	 * @param string $name       Annotation name
785
	 * @param mixed  $value      Annotation value
786
	 * @param int    $access_id  Access ID
787
	 * @param int    $owner_guid GUID of the annotation owner
788
	 * @param string $vartype    The type of annotation value
789
	 *
790
	 * @return bool|int Returns int if an annotation is saved
791
	 */
792 84
	public function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_guid = 0, $vartype = "") {
793 84
		if ((int) $this->guid > 0) {
794 84
			return create_annotation($this->getGUID(), $name, $value, $vartype, $owner_guid, $access_id);
795
		} else {
796
			$this->temp_annotations[$name] = $value;
797
		}
798
		return true;
799
	}
800
801
	/**
802
	 * Gets an array of annotations.
803
	 *
804
	 * To retrieve annotations on an unsaved entity, pass array('name' => [annotation name])
805
	 * as the options array.
806
	 *
807
	 * @param array $options Array of options for elgg_get_annotations() except guid.
808
	 *
809
	 * @return array
810
	 * @see elgg_get_annotations()
811
	 */
812
	public function getAnnotations(array $options = []) {
813
		if ($this->guid) {
814
			$options['guid'] = $this->guid;
815
816
			return elgg_get_annotations($options);
817
		} else {
818
			$name = elgg_extract('annotation_name', $options, '');
819
820
			if (isset($this->temp_annotations[$name])) {
821
				return array($this->temp_annotations[$name]);
822
			}
823
		}
824
825
		return array();
826
	}
827
828
	/**
829
	 * Count annotations.
830
	 *
831
	 * @param string $name The type of annotation.
832
	 *
833
	 * @return int
834
	 */
835
	public function countAnnotations($name = "") {
836
		return $this->getAnnotationCalculation($name, 'count');
837
	}
838
839
	/**
840
	 * Get the average of an integer type annotation.
841
	 *
842
	 * @param string $name Annotation name
843
	 *
844
	 * @return int
845
	 */
846
	public function getAnnotationsAvg($name) {
847
		return $this->getAnnotationCalculation($name, 'avg');
848
	}
849
850
	/**
851
	 * Get the sum of integer type annotations of a given name.
852
	 *
853
	 * @param string $name Annotation name
854
	 *
855
	 * @return int
856
	 */
857
	public function getAnnotationsSum($name) {
858
		return $this->getAnnotationCalculation($name, 'sum');
859
	}
860
861
	/**
862
	 * Get the minimum of integer type annotations of given name.
863
	 *
864
	 * @param string $name Annotation name
865
	 *
866
	 * @return int
867
	 */
868
	public function getAnnotationsMin($name) {
869
		return $this->getAnnotationCalculation($name, 'min');
870
	}
871
872
	/**
873
	 * Get the maximum of integer type annotations of a given name.
874
	 *
875
	 * @param string $name Annotation name
876
	 *
877
	 * @return int
878
	 */
879
	public function getAnnotationsMax($name) {
880
		return $this->getAnnotationCalculation($name, 'max');
881
	}
882
883
	/**
884
	 * Count the number of comments attached to this entity.
885
	 *
886
	 * @return int Number of comments
887
	 * @since 1.8.0
888
	 */
889
	public function countComments() {
890
		$params = array('entity' => $this);
891
		$num = _elgg_services()->hooks->trigger('comments:count', $this->getType(), $params);
892
893
		if (is_int($num)) {
894
			return $num;
895
		} else {
896
			return elgg_get_entities(array(
897
				'type' => 'object',
898
				'subtype' => 'comment',
899
				'container_guid' => $this->getGUID(),
900
				'count' => true,
901
				'distinct' => false,
902
			));
903
		}
904
	}
905
906
	/**
907
	 * Gets an array of entities with a relationship to this entity.
908
	 *
909
	 * @param array $options Options array. See elgg_get_entities_from_relationship()
910
	 *                       for a list of options. 'relationship_guid' is set to
911
	 *                       this entity.
912
	 *
913
	 * @return array|false An array of entities or false on failure
914
	 * @see elgg_get_entities_from_relationship()
915
	 */
916
	public function getEntitiesFromRelationship(array $options = []) {
917
		$options['relationship_guid'] = $this->guid;
918
		return elgg_get_entities_from_relationship($options);
919
	}
920
921
	/**
922
	 * Gets the number of entities from a specific relationship type
923
	 *
924
	 * @param string $relationship         Relationship type (eg "friends")
925
	 * @param bool   $inverse_relationship Invert relationship
926
	 *
927
	 * @return int|false The number of entities or false on failure
928
	 */
929
	public function countEntitiesFromRelationship($relationship, $inverse_relationship = false) {
930
		return elgg_get_entities_from_relationship(array(
931
			'relationship' => $relationship,
932
			'relationship_guid' => $this->getGUID(),
933
			'inverse_relationship' => $inverse_relationship,
934
			'count' => true
935
		));
936
	}
937
938
	/**
939
	 * Can a user edit this entity?
940
	 *
941
	 * @tip Can be overridden by registering for the permissions_check plugin hook.
942
	 *
943
	 * @param int $user_guid The user GUID, optionally (default: logged in user)
944
	 *
945
	 * @return bool Whether this entity is editable by the given user.
946
	 * @see elgg_set_ignore_access()
947
	 */
948 22
	public function canEdit($user_guid = 0) {
949 22
		return _elgg_services()->userCapabilities->canEdit($this, $user_guid);
950
	}
951
952
	/**
953
	 * Can a user delete this entity?
954
	 *
955
	 * @tip Can be overridden by registering for the permissions_check:delete plugin hook.
956
	 *
957
	 * @param int $user_guid The user GUID, optionally (default: logged in user)
958
	 *
959
	 * @return bool Whether this entity is deletable by the given user.
960
	 * @since 1.11
961
	 * @see elgg_set_ignore_access()
962
	 */
963 4
	public function canDelete($user_guid = 0) {
964 4
		return _elgg_services()->userCapabilities->canDelete($this, $user_guid);
965
	}
966
967
	/**
968
	 * Can a user edit metadata on this entity?
969
	 *
970
	 * If no specific metadata is passed, it returns whether the user can
971
	 * edit any metadata on the entity.
972
	 *
973
	 * @tip Can be overridden by by registering for the permissions_check:metadata
974
	 * plugin hook.
975
	 *
976
	 * @param \ElggMetadata $metadata  The piece of metadata to specifically check or null for any metadata
977
	 * @param int           $user_guid The user GUID, optionally (default: logged in user)
978
	 *
979
	 * @return bool
980
	 * @see elgg_set_ignore_access()
981
	 */
982 8
	public function canEditMetadata($metadata = null, $user_guid = 0) {
983 8
		return _elgg_services()->userCapabilities->canEditMetadata($this, $user_guid, $metadata);
984
	}
985
986
	/**
987
	 * Can a user add an entity to this container
988
	 *
989
	 * @param int    $user_guid The GUID of the user creating the entity (0 for logged in user).
990
	 * @param string $type      The type of entity we're looking to write
991
	 * @param string $subtype   The subtype of the entity we're looking to write
992
	 *
993
	 * @return bool
994
	 * @see elgg_set_ignore_access()
995
	 */
996 5
	public function canWriteToContainer($user_guid = 0, $type = 'all', $subtype = 'all') {
997 5
		return _elgg_services()->userCapabilities->canWriteToContainer($this, $user_guid, $type, $subtype);
998
	}
999
1000
	/**
1001
	 * Can a user comment on an entity?
1002
	 *
1003
	 * @tip Can be overridden by registering for the permissions_check:comment,
1004
	 * <entity type> plugin hook.
1005
	 *
1006
	 * @param int  $user_guid User guid (default is logged in user)
1007
	 * @param bool $default   Default permission
1008
	 * @return bool
1009
	 */
1010 5
	public function canComment($user_guid = 0, $default = null) {
1011 5
		return _elgg_services()->userCapabilities->canComment($this, $user_guid, $default);
1012
	}
1013
1014
	/**
1015
	 * Can a user annotate an entity?
1016
	 *
1017
	 * @tip Can be overridden by registering for the plugin hook [permissions_check:annotate:<name>,
1018
	 * <entity type>] or [permissions_check:annotate, <entity type>]. The hooks are called in that order.
1019
	 *
1020
	 * @tip If you want logged out users to annotate an object, do not call
1021
	 * canAnnotate(). It's easier than using the plugin hook.
1022
	 *
1023
	 * @param int    $user_guid       User guid (default is logged in user)
1024
	 * @param string $annotation_name The name of the annotation (default is unspecified)
1025
	 *
1026
	 * @return bool
1027
	 */
1028 8
	public function canAnnotate($user_guid = 0, $annotation_name = '') {
1029 8
		return _elgg_services()->userCapabilities->canAnnotate($this, $user_guid, $annotation_name);
1030
	}
1031
1032
	/**
1033
	 * Returns the access_id.
1034
	 *
1035
	 * @return int The access ID
1036
	 */
1037 1
	public function getAccessID() {
1038 1
		return $this->access_id;
1039
	}
1040
1041
	/**
1042
	 * Returns the guid.
1043
	 *
1044
	 * @return int|null GUID
1045
	 */
1046 200
	public function getGUID() {
1047 200
		return $this->guid;
1048
	}
1049
1050
	/**
1051
	 * Returns the entity type
1052
	 *
1053
	 * @return string The entity type
1054
	 */
1055 129
	public function getType() {
1056 129
		return $this->type;
1057
	}
1058
1059
	/**
1060
	 * Get the entity subtype
1061
	 *
1062
	 * @return string The entity subtype
1063
	 */
1064 88
	public function getSubtype() {
1065
		// If this object hasn't been saved, then return the subtype string.
1066 88
		if ($this->attributes['guid']) {
1067 83
			return get_subtype_from_id($this->attributes['subtype']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression get_subtype_from_id($thi...attributes['subtype']); of type string|false adds false to the return on line 1067 which is incompatible with the return type declared by the interface Loggable::getSubtype of type string. It seems like you forgot to handle an error condition.
Loading history...
1068
		}
1069 8
		return $this->attributes['subtype'];
1070
	}
1071
1072
	/**
1073
	 * Get the guid of the entity's owner.
1074
	 *
1075
	 * @return int The owner GUID
1076
	 */
1077 77
	public function getOwnerGUID() {
1078 77
		return (int)$this->owner_guid;
1079
	}
1080
1081
	/**
1082
	 * Gets the \ElggEntity that owns this entity.
1083
	 *
1084
	 * @return \ElggEntity The owning entity
1085
	 */
1086 1
	public function getOwnerEntity() {
1087 1
		return get_entity($this->owner_guid);
1088
	}
1089
1090
	/**
1091
	 * Set the container for this object.
1092
	 *
1093
	 * @param int $container_guid The ID of the container.
1094
	 *
1095
	 * @return bool
1096
	 */
1097
	public function setContainerGUID($container_guid) {
1098
		return $this->container_guid = (int)$container_guid;
1099
	}
1100
1101
	/**
1102
	 * Gets the container GUID for this entity.
1103
	 *
1104
	 * @return int
1105
	 */
1106 12
	public function getContainerGUID() {
1107 12
		return (int)$this->container_guid;
1108
	}
1109
1110
	/**
1111
	 * Get the container entity for this object.
1112
	 *
1113
	 * @return \ElggEntity
1114
	 * @since 1.8.0
1115
	 */
1116 8
	public function getContainerEntity() {
1117 8
		return get_entity($this->getContainerGUID());
1118
	}
1119
1120
	/**
1121
	 * Returns the UNIX epoch time that this entity was last updated
1122
	 *
1123
	 * @return int UNIX epoch time
1124
	 */
1125 3
	public function getTimeUpdated() {
1126 3
		return $this->time_updated;
1127
	}
1128
1129
	/**
1130
	 * Gets the URL for this entity.
1131
	 *
1132
	 * Plugins can register for the 'entity:url', <type> plugin hook to
1133
	 * customize the url for an entity.
1134
	 *
1135
	 * @return string The URL of the entity
1136
	 */
1137 11
	public function getURL() {
1138 11
		$url = _elgg_services()->hooks->trigger('entity:url', $this->getType(), ['entity' => $this]);
1139
		
1140 11
		if ($url === null || $url === '' || $url === false) {
1141 11
			return '';
1142
		}
1143
1144
		return elgg_normalize_url($url);
1145
	}
1146
1147
	/**
1148
	 * Saves icons using an uploaded file as the source.
1149
	 *
1150
	 * @param string $input_name Form input name
1151
	 * @param string $type       The name of the icon. e.g., 'icon', 'cover_photo'
1152
	 * @param array  $coords     An array of cropping coordinates x1, y1, x2, y2
1153
	 * @return bool
1154
	 */
1155
	public function saveIconFromUploadedFile($input_name, $type = 'icon', array $coords = array()) {
1156
		return _elgg_services()->iconService->saveIconFromUploadedFile($this, $input_name, $type, $coords);
1157
	}
1158
1159
	/**
1160
	 * Saves icons using a local file as the source.
1161
	 *
1162
	 * @param string $filename The full path to the local file
1163
	 * @param string $type     The name of the icon. e.g., 'icon', 'cover_photo'
1164
	 * @param array  $coords   An array of cropping coordinates x1, y1, x2, y2
1165
	 * @return bool
1166
	 */
1167
	public function saveIconFromLocalFile($filename, $type = 'icon', array $coords = array()) {
1168
		return _elgg_services()->iconService->saveIconFromLocalFile($this, $filename, $type, $coords);
1169
	}
1170
1171
	/**
1172
	 * Saves icons using a file located in the data store as the source.
1173
	 *
1174
	 * @param string $file   An ElggFile instance
1175
	 * @param string $type   The name of the icon. e.g., 'icon', 'cover_photo'
1176
	 * @param array  $coords An array of cropping coordinates x1, y1, x2, y2
1177
	 * @return bool
1178
	 */
1179
	public function saveIconFromElggFile(\ElggFile $file, $type = 'icon', array $coords = array()) {
1180
		return _elgg_services()->iconService->saveIconFromElggFile($this, $file, $type, $coords);
1181
	}
1182
	
1183
	/**
1184
	 * Returns entity icon as an ElggIcon object
1185
	 * The icon file may or may not exist on filestore
1186
	 *
1187
	 * @param string $size Size of the icon
1188
	 * @param string $type The name of the icon. e.g., 'icon', 'cover_photo'
1189
	 * @return \ElggIcon
1190
	 */
1191 1
	public function getIcon($size, $type = 'icon') {
1192 1
		return _elgg_services()->iconService->getIcon($this, $size, $type);
1193
	}
1194
1195
	/**
1196
	 * Removes all icon files and metadata for the passed type of icon.
1197
	 *
1198
	 * @param string $type The name of the icon. e.g., 'icon', 'cover_photo'
1199
	 * @return bool
1200
	 */
1201
	public function deleteIcon($type = 'icon') {
1202
		return _elgg_services()->iconService->deleteIcon($this, $type);
1203
	}
1204
	
1205
	/**
1206
	 * Returns the timestamp of when the icon was changed.
1207
	 *
1208
	 * @param string $size The size of the icon
1209
	 * @param string $type The name of the icon. e.g., 'icon', 'cover_photo'
1210
	 *
1211
	 * @return int|null A unix timestamp of when the icon was last changed, or null if not set.
1212
	 */
1213
	public function getIconLastChange($size, $type = 'icon') {
1214
		return _elgg_services()->iconService->getIconLastChange($this, $size, $type);
1215
	}
1216
	
1217
	/**
1218
	 * Returns if the entity has an icon of the passed type.
1219
	 *
1220
	 * @param string $size The size of the icon
1221
	 * @param string $type The name of the icon. e.g., 'icon', 'cover_photo'
1222
	 * @return bool
1223
	 */
1224
	public function hasIcon($size, $type = 'icon') {
1225
		return _elgg_services()->iconService->hasIcon($this, $size, $type);
1226
	}
1227
1228
	/**
1229
	 * Get the URL for this entity's icon
1230
	 *
1231
	 * Plugins can register for the 'entity:icon:url', <type> plugin hook
1232
	 * to customize the icon for an entity.
1233
	 *
1234
	 * @param mixed $params A string defining the size of the icon (e.g. tiny, small, medium, large)
1235
	 *                      or an array of parameters including 'size'
1236
	 * @return string The URL
1237
	 * @since 1.8.0
1238
	 */
1239
	public function getIconURL($params = array()) {
1240
		return _elgg_services()->iconService->getIconURL($this, $params);
1241
	}
1242
1243
	/**
1244
	 * Tests to see whether the object has been persisted.
1245
	 *
1246
	 * @return bool
1247
	 */
1248 125
	public function isFullyLoaded() {
1249 125
		return (bool)$this->guid;
1250
	}
1251
1252
	/**
1253
	 * Save an entity.
1254
	 *
1255
	 * @return bool|int
1256
	 * @throws InvalidParameterException
1257
	 * @throws IOException
1258
	 */
1259 8
	public function save() {
1260 8
		$guid = $this->guid;
1261 8
		if ($guid > 0) {
1262 2
			$guid = $this->update();
1263 2
		} else {
1264 6
			$guid = $this->create();
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->create(); of type false|integer adds the type integer to the return on line 1279 which is incompatible with the return type declared by the abstract method ElggData::save of type boolean.
Loading history...
1265 5
			if ($guid && !_elgg_services()->events->trigger('create', $this->type, $this)) {
1266
				// plugins that return false to event don't need to override the access system
1267
				$ia = elgg_set_ignore_access(true);
1268
				$this->delete();
1269
				elgg_set_ignore_access($ia);
1270
				return false;
1271
			}
1272
		}
1273
1274 7
		if ($guid) {
1275 6
			_elgg_services()->entityCache->set($this);
1276 6
			$this->storeInPersistedCache(_elgg_get_memcache('new_entity_cache'));
1277 6
		}
1278
1279 7
		return $guid;
1280
	}
1281
	
1282
	/**
1283
	 * Create a new entry in the entities table.
1284
	 *
1285
	 * Saves the base information in the entities table for the entity.  Saving
1286
	 * the type-specific information is handled in the calling class method.
1287
	 *
1288
	 * @warning Entities must have an entry in both the entities table and their type table
1289
	 * or they will throw an exception when loaded.
1290
	 *
1291
	 * @return int The new entity's GUID
1292
	 * @throws InvalidParameterException If the entity's type has not been set.
1293
	 * @throws IOException If the new row fails to write to the DB.
1294
	 */
1295 6
	protected function create() {
1296
1297 6
		$allowed_types = elgg_get_config('entity_types');
1298 6
		$type = $this->getDatabase()->sanitizeString($this->attributes['type']);
0 ignored issues
show
Deprecated Code introduced by
The method Elgg\Database::sanitizeString() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
1299 6
		if (!in_array($type, $allowed_types)) {
1300 1
			throw new \InvalidParameterException('Entity type must be one of the allowed types: '
1301 1
					. implode(', ', $allowed_types));
1302
		}
1303
		
1304 5
		$subtype = $this->attributes['subtype'];
1305 5
		$subtype_id = add_subtype($type, $subtype);
1306 5
		$owner_guid = (int)$this->attributes['owner_guid'];
1307 5
		$access_id = (int)$this->attributes['access_id'];
1308 5
		$now = $this->getCurrentTime()->getTimestamp();
1309 5
		$time_created = isset($this->attributes['time_created']) ? (int)$this->attributes['time_created'] : $now;
1310
		
1311 5
		$container_guid = $this->attributes['container_guid'];
1312 5
		if ($container_guid == 0) {
1313 4
			$container_guid = $owner_guid;
1314 4
			$this->attributes['container_guid'] = $container_guid;
1315 4
		}
1316 5
		$container_guid = (int)$container_guid;
1317
1318 5
		if ($access_id == ACCESS_DEFAULT) {
1319
			throw new \InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h');
1320
		}
1321
1322 5
		$user_guid = elgg_get_logged_in_user_guid();
1323
1324
		// If given an owner, verify it can be loaded
1325 5
		if ($owner_guid) {
1326 1
			$owner = $this->getOwnerEntity();
1327 1
			if (!$owner) {
1328
				_elgg_services()->logger->error("User $user_guid tried to create a ($type, $subtype), but the given"
1329
					. " owner $owner_guid could not be loaded.");
1330
				return false;
1331
			}
1332
1333
			// If different owner than logged in, verify can write to container.
1334
1335 1
			if ($user_guid != $owner_guid && !$owner->canWriteToContainer(0, $type, $subtype)) {
1336
				_elgg_services()->logger->error("User $user_guid tried to create a ($type, $subtype) with owner"
1337
					. " $owner_guid, but the user wasn't permitted to write to the owner's container.");
1338
				return false;
1339
			}
1340 1
		}
1341
1342
		// If given a container, verify it can be loaded and that the current user can write to it
1343 5
		if ($container_guid) {
1344 1
			$container = $this->getContainerEntity();
1345 1
			if (!$container) {
1346
				_elgg_services()->logger->error("User $user_guid tried to create a ($type, $subtype), but the given"
1347
					. " container $container_guid could not be loaded.");
1348
				return false;
1349
			}
1350
1351 1
			if (!$container->canWriteToContainer(0, $type, $subtype)) {
1352
				_elgg_services()->logger->error("User $user_guid tried to create a ($type, $subtype), but was not"
1353
					. " permitted to write to container $container_guid.");
1354
				return false;
1355
			}
1356 1
		}
1357
1358 5
		$result = _elgg_services()->entityTable->insertRow((object) [
1359 5
			'type' => $type,
1360 5
			'subtype_id' => $subtype_id,
1361 5
			'owner_guid' => $owner_guid,
1362 5
			'container_guid' => $container_guid,
1363 5
			'access_id' => $access_id,
1364 5
			'time_created' => $time_created,
1365 5
			'time_updated' => $now,
1366 5
			'last_action' => $now,
1367 5
		], $this->attributes);
1368
1369 5
		if (!$result) {
1370
			throw new \IOException("Unable to save new object's base entity information!");
1371
		}
1372
	
1373
		// for BC with 1.8, ->subtype always returns ID, ->getSubtype() the string
1374 5
		$this->attributes['subtype'] = (int)$subtype_id;
1375 5
		$this->attributes['guid'] = (int)$result;
1376 5
		$this->attributes['time_created'] = (int)$time_created;
1377 5
		$this->attributes['time_updated'] = (int)$now;
1378 5
		$this->attributes['last_action'] = (int)$now;
1379 5
		$this->attributes['container_guid'] = (int)$container_guid;
1380
1381
		// We are writing this new entity to cache to make sure subsequent calls
1382
		// to get_entity() load entity from cache and not from the DB
1383
		// At this point, secondary attributes have not yet been written to the DB,
1384
		// but metadata and annotation event handlers may be calling get_entity()
1385 5
		_elgg_services()->entityCache->set($this);
1386
1387
		// Save any unsaved metadata
1388 5
		if (sizeof($this->temp_metadata) > 0) {
1389
			foreach ($this->temp_metadata as $name => $value) {
1390
				$this->$name = $value;
1391
			}
1392
			
1393
			$this->temp_metadata = array();
1394
		}
1395
1396
		// Save any unsaved annotations.
1397 5
		if (sizeof($this->temp_annotations) > 0) {
1398
			foreach ($this->temp_annotations as $name => $value) {
1399
				$this->annotate($name, $value);
1400
			}
1401
			
1402
			$this->temp_annotations = array();
1403
		}
1404
1405
		// Save any unsaved private settings.
1406 5
		if (sizeof($this->temp_private_settings) > 0) {
1407 4
			foreach ($this->temp_private_settings as $name => $value) {
1408 4
				$this->setPrivateSetting($name, $value);
1409 4
			}
1410
			
1411 4
			$this->temp_private_settings = array();
1412 4
		}
1413
		
1414 5
		return $result;
1415
	}
1416
1417
	/**
1418
	 * Update the entity in the database.
1419
	 *
1420
	 * @return bool Whether the update was successful.
1421
	 *
1422
	 * @throws InvalidParameterException
1423
	 */
1424 2
	protected function update() {
1425
		
1426 2
		_elgg_services()->boot->invalidateCache($this->guid);
0 ignored issues
show
Unused Code introduced by
The call to BootService::invalidateCache() has too many arguments starting with $this->guid.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1427
1428 2
		if (!$this->canEdit()) {
1429 1
			return false;
1430 2
		}
1431
1432
		// give old update event a chance to stop the update
1433 1
		if (!_elgg_services()->events->trigger('update', $this->type, $this)) {
1434
			return false;
1435
		}
1436
1437
		// See #6225. We copy these after the update event in case a handler changed one of them.
1438 1
		$guid = (int)$this->guid;
1439 1
		$owner_guid = (int)$this->owner_guid;
1440 1
		$access_id = (int)$this->access_id;
1441 1
		$container_guid = (int)$this->container_guid;
1442 1
		$time_created = (int)$this->time_created;
1443 1
		$time = $this->getCurrentTime()->getTimestamp();
1444
1445 1
		if ($access_id == ACCESS_DEFAULT) {
1446
			throw new \InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.php');
1447
		}
1448
1449 1
		$ret = _elgg_services()->entityTable->updateRow($guid, (object) [
1450 1
			'owner_guid' => $owner_guid,
1451 1
			'container_guid' => $container_guid,
1452 1
			'access_id' => $access_id,
1453 1
			'time_created' => $time_created,
1454 1
			'time_updated' => $time,
1455 1
			'guid' => $guid,
1456 1
		]);
1457
1458 1
		elgg_trigger_after_event('update', $this->type, $this);
0 ignored issues
show
Documentation introduced by
$this is of type this<ElggEntity>, but the function expects a string|null.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1459
1460
		// TODO(evan): Move this to \ElggObject?
1461 1
		if ($this instanceof \ElggObject) {
1462 1
			update_river_access_by_object($guid, $access_id);
1463 1
		}
1464
1465 1
		if ($ret !== false) {
1466 1
			$this->attributes['time_updated'] = $time;
1467 1
		}
1468
1469 1
		$this->orig_attributes = [];
1470
1471
		// Handle cases where there was no error BUT no rows were updated!
1472 1
		return $ret !== false;
1473
	}
1474
1475
	/**
1476
	 * Loads attributes from the entities table into the object.
1477
	 *
1478
	 * @param mixed $guid GUID of entity or \stdClass object from entities table
1479
	 *
1480
	 * @return bool
1481
	 */
1482
	protected function load($guid) {
1483
		if ($guid instanceof \stdClass) {
1484
			$row = $guid;
1485
		} else {
1486
			$row = get_entity_as_row($guid);
1487
		}
1488
1489
		if ($row) {
1490
			// Create the array if necessary - all subclasses should test before creating
1491
			if (!is_array($this->attributes)) {
1492
				$this->attributes = array();
1493
			}
1494
1495
			// Now put these into the attributes array as core values
1496
			$objarray = (array) $row;
1497
			foreach ($objarray as $key => $value) {
1498
				$this->attributes[$key] = $value;
1499
			}
1500
1501
			// guid needs to be an int  https://github.com/elgg/elgg/issues/4111
1502
			$this->attributes['guid'] = (int)$this->attributes['guid'];
1503
1504
			// for BC with 1.8, ->subtype always returns ID, ->getSubtype() the string
1505
			$this->attributes['subtype'] = (int)$this->attributes['subtype'];
1506
1507
			// Cache object handle
1508
			if ($this->attributes['guid']) {
1509
				_elgg_services()->entityCache->set($this);
1510
			}
1511
1512
			return true;
1513
		}
1514
1515
		return false;
1516
	}
1517
1518
	/**
1519
	 * Stores non-attributes from the loading of the entity as volatile data
1520
	 *
1521
	 * @param array $data Key value array
1522
	 * @return void
1523
	 */
1524 239
	protected function loadAdditionalSelectValues(array $data) {
1525 239
		foreach ($data as $name => $value) {
1526 6
			$this->setVolatileData("select:$name", $value);
1527 239
		}
1528 239
	}
1529
	
1530
	/**
1531
	 * Load new data from database into existing entity. Overwrites data but
1532
	 * does not change values not included in the latest data.
1533
	 *
1534
	 * @internal This is used when the same entity is selected twice during a
1535
	 * request in case different select clauses were used to load different data
1536
	 * into volatile data.
1537
	 *
1538
	 * @param \stdClass $row DB row with new entity data
1539
	 * @return bool
1540
	 * @access private
1541
	 */
1542
	public function refresh(\stdClass $row) {
1543
		if ($row instanceof \stdClass) {
1544
			return $this->load($row);
1545
		}
1546
		return false;
1547
	}
1548
1549
	/**
1550
	 * Disable this entity.
1551
	 *
1552
	 * Disabled entities are not returned by getter functions.
1553
	 * To enable an entity, use {@link \ElggEntity::enable()}.
1554
	 *
1555
	 * Recursively disabling an entity will disable all entities
1556
	 * owned or contained by the parent entity.
1557
	 *
1558
	 * You can ignore the disabled field by using {@link access_show_hidden_entities()}.
1559
	 *
1560
	 * @note Internal: Disabling an entity sets the 'enabled' column to 'no'.
1561
	 *
1562
	 * @param string $reason    Optional reason
1563
	 * @param bool   $recursive Recursively disable all contained entities?
1564
	 *
1565
	 * @return bool
1566
	 * @see \ElggEntity::enable()
1567
	 */
1568 2
	public function disable($reason = "", $recursive = true) {
1569 2
		if (!$this->guid) {
1570 1
			return false;
1571
		}
1572
		
1573 1
		if (!_elgg_services()->events->trigger('disable', $this->type, $this)) {
1574
			return false;
1575
		}
1576
		
1577 1
		if (!$this->canEdit()) {
1578
			return false;
1579
		}
1580
1581 1
		if ($this instanceof ElggUser && $this->banned === 'no') {
1582
			// temporarily ban to prevent using the site during disable
1583
			_elgg_services()->usersTable->markBanned($this->guid, true);
1584
			$unban_after = true;
1585
		} else {
1586 1
			$unban_after = false;
1587
		}
1588
1589 1
		if ($reason) {
1590
			$this->disable_reason = $reason;
1 ignored issue
show
Documentation introduced by
The property disable_reason does not exist on object<ElggEntity>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1591
		}
1592
1593 1
		$dbprefix = elgg_get_config('dbprefix');
1594
		
1595 1
		$guid = (int) $this->guid;
1596
		
1597 1
		if ($recursive) {
1598
			// Only disable enabled subentities
1599 1
			$hidden = access_get_show_hidden_status();
1600 1
			access_show_hidden_entities(false);
1601
1602 1
			$ia = elgg_set_ignore_access(true);
1603
1604
			$base_options = [
1605
				'wheres' => [
1606 1
					"e.guid != $guid",
1607 1
				],
1608 1
				'limit' => false,
1609 1
			];
1610
			
1611 1
			foreach (['owner_guid', 'container_guid'] as $db_column) {
1612 1
				$options = $base_options;
1613 1
				$options[$db_column] = $guid;
1614
				
1615 1
				$subentities = new \ElggBatch('elgg_get_entities', $options);
1616 1
				$subentities->setIncrementOffset(false);
1617
				
1618 1
				foreach ($subentities as $subentity) {
1619
					/* @var $subentity \ElggEntity */
1620
					if (!$subentity->isEnabled()) {
1621
						continue;
1622
					}
1623
					add_entity_relationship($subentity->guid, 'disabled_with', $guid);
1624
					$subentity->disable($reason);
1625 1
				}
1626 1
			}
1627
1628 1
			access_show_hidden_entities($hidden);
1629 1
			elgg_set_ignore_access($ia);
1630 1
		}
1631
1632 1
		$this->disableMetadata();
1633 1
		$this->disableAnnotations();
1634
1635 1
		_elgg_services()->entityCache->remove($guid);
1636
		
1637
		$sql = "
1638 1
			UPDATE {$dbprefix}entities
1639
			SET enabled = 'no'
1640
			WHERE guid = :guid
1641 1
		";
1642
		$params = [
1643 1
			':guid' => $guid,
1644 1
		];
1645 1
		$disabled = $this->getDatabase()->updateData($sql, false, $params);
1646
1647 1
		if ($unban_after) {
1648
			_elgg_services()->usersTable->markBanned($this->guid, false);
1649
		}
1650
1651 1
		if ($disabled) {
1652 1
			$this->attributes['enabled'] = 'no';
1653 1
			_elgg_services()->events->trigger('disable:after', $this->type, $this);
1654 1
		}
1655
1656 1
		return (bool) $disabled;
1657
	}
1658
1659
	/**
1660
	 * Enable the entity
1661
	 *
1662
	 * @warning Disabled entities can't be loaded unless
1663
	 * {@link access_show_hidden_entities(true)} has been called.
1664
	 *
1665
	 * @param bool $recursive Recursively enable all entities disabled with the entity?
1666
	 * @see access_show_hiden_entities()
1667
	 * @return bool
1668
	 */
1669
	public function enable($recursive = true) {
1670
		$guid = (int)$this->guid;
1671
		if (!$guid) {
1672
			return false;
1673
		}
1674
		
1675
		if (!_elgg_services()->events->trigger('enable', $this->type, $this)) {
1676
			return false;
1677
		}
1678
		
1679
		if (!$this->canEdit()) {
1680
			return false;
1681
		}
1682
		
1683
		global $CONFIG;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1684
	
1685
		// Override access only visible entities
1686
		$old_access_status = access_get_show_hidden_status();
1687
		access_show_hidden_entities(true);
1688
	
1689
		$result = $this->getDatabase()->updateData("UPDATE {$CONFIG->dbprefix}entities
1690
			SET enabled = 'yes'
1691
			WHERE guid = $guid");
1692
1693
		$this->deleteMetadata('disable_reason');
1694
		$this->enableMetadata();
1695
		$this->enableAnnotations();
1696
1697
		if ($recursive) {
1698
			$disabled_with_it = elgg_get_entities_from_relationship(array(
1699
				'relationship' => 'disabled_with',
1700
				'relationship_guid' => $guid,
1701
				'inverse_relationship' => true,
1702
				'limit' => 0,
1703
			));
1704
1705
			foreach ($disabled_with_it as $e) {
0 ignored issues
show
Bug introduced by
The expression $disabled_with_it of type false|object<ElggBatch>|integer|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1706
				$e->enable();
1707
				remove_entity_relationship($e->guid, 'disabled_with', $guid);
1708
			}
1709
		}
1710
	
1711
		access_show_hidden_entities($old_access_status);
1712
	
1713
		if ($result) {
1714
			$this->attributes['enabled'] = 'yes';
1715
			_elgg_services()->events->trigger('enable:after', $this->type, $this);
1716
		}
1717
1718
		return $result;
1719
	}
1720
1721
	/**
1722
	 * Is this entity enabled?
1723
	 *
1724
	 * @return boolean Whether this entity is enabled.
1725
	 */
1726 1
	public function isEnabled() {
1727 1
		return $this->enabled == 'yes';
1728
	}
1729
1730
	/**
1731
	 * Deletes the entity.
1732
	 *
1733
	 * Removes the entity and its metadata, annotations, relationships,
1734
	 * river entries, and private data.
1735
	 *
1736
	 * Optionally can remove entities contained and owned by this entity.
1737
	 *
1738
	 * @warning If deleting recursively, this bypasses ownership of items contained by
1739
	 * the entity.  That means that if the container_guid = $this->guid, the item will
1740
	 * be deleted regardless of who owns it.
1741
	 *
1742
	 * @param bool $recursive If true (default) then all entities which are
1743
	 *                        owned or contained by $this will also be deleted.
1744
	 *
1745
	 * @return bool
1746
	 */
1747 1
	public function delete($recursive = true) {
1748
1749 1
		$guid = $this->guid;
1750 1
		if (!$guid) {
1751
			return false;
1752
		}
1753
		
1754
		// first check if we can delete this entity
1755
		// NOTE: in Elgg <= 1.10.3 this was after the delete event,
1756
		// which could potentially remove some content if the user didn't have access
1757 1
		if (!$this->canDelete()) {
1758
			return false;
1759
		}
1760
1761
		// now trigger an event to let others know this entity is about to be deleted
1762
		// so they can prevent it or take their own actions
1763 1
		if (!_elgg_services()->events->trigger('delete', $this->type, $this)) {
1764
			return false;
1765
		}
1766
1767 1
		if ($this instanceof ElggUser) {
1768
			// ban to prevent using the site during delete
1769
			_elgg_services()->usersTable->markBanned($this->guid, true);
1770
		}
1771
1772
		// Delete contained owned and otherwise releated objects (depth first)
1773 1
		if ($recursive) {
1774
			// Temporarily overriding access controls
1775 1
			$entity_disable_override = access_get_show_hidden_status();
1776 1
			access_show_hidden_entities(true);
1777 1
			$ia = elgg_set_ignore_access(true);
1778
1779
			// @todo there was logic in the original code that ignored
1780
			// entities with owner or container guids of themselves.
1781
			// this should probably be prevented in \ElggEntity instead of checked for here
1782
			$base_options = [
1783
				'wheres' => [
1784 1
					"e.guid != $guid",
1785 1
				],
1786 1
				'limit' => false,
1787 1
			];
1788
			
1789 1
			foreach (['owner_guid', 'container_guid'] as $db_column) {
1790 1
				$options = $base_options;
1791 1
				$options[$db_column] = $guid;
1792
				
1793 1
				$batch = new \ElggBatch('elgg_get_entities', $options);
1794 1
				$batch->setIncrementOffset(false);
1795
				
1796
				/* @var $e \ElggEntity */
1797 1
				foreach ($batch as $e) {
1798
					$e->delete(true);
1799 1
				}
1800 1
			}
1801
			
1802 1
			access_show_hidden_entities($entity_disable_override);
1803 1
			elgg_set_ignore_access($ia);
1804 1
		}
1805
1806 1
		$entity_disable_override = access_get_show_hidden_status();
1807 1
		access_show_hidden_entities(true);
1808 1
		$ia = elgg_set_ignore_access(true);
1809
		
1810
		// Now delete the entity itself
1811 1
		$this->deleteMetadata();
1812 1
		$this->deleteOwnedMetadata();
1813 1
		$this->deleteAnnotations();
1814 1
		$this->deleteOwnedAnnotations();
1815 1
		$this->deleteRelationships();
1816 1
		$this->deleteAccessCollectionMemberships();
1817 1
		$this->deleteOwnedAccessCollections();
1818
1819 1
		access_show_hidden_entities($entity_disable_override);
1820 1
		elgg_set_ignore_access($ia);
1821
1822 1
		_elgg_delete_river(array('subject_guid' => $guid));
1823 1
		_elgg_delete_river(array('object_guid' => $guid));
1824 1
		_elgg_delete_river(array('target_guid' => $guid));
1825 1
		remove_all_private_settings($guid);
1826
1827 1
		_elgg_invalidate_cache_for_entity($guid);
1828 1
		_elgg_invalidate_memcache_for_entity($guid);
1829
1830 1
		$dbprefix = elgg_get_config('dbprefix');
1831
		
1832
		$sql = "
1833 1
			DELETE FROM {$dbprefix}entities
1834
			WHERE guid = :guid
1835 1
		";
1836
		$params = [
1837 1
			':guid' => $guid,
1838 1
		];
1839
1840 1
		$deleted = $this->getDatabase()->deleteData($sql, $params);
1841
1842 1
		if ($deleted && in_array($this->type, ['object', 'user', 'group', 'site'])) {
1843
			// delete from type-specific subtable
1844
			$sql = "
1845 1
				DELETE FROM {$dbprefix}{$this->type}s_entity
1846
				WHERE guid = :guid
1847 1
			";
1848 1
			$this->getDatabase()->deleteData($sql, $params);
1849 1
		}
1850
		
1851 1
		_elgg_clear_entity_files($this);
1852
1853 1
		return (bool)$deleted;
1854
	}
1855
1856
	/**
1857
	 * {@inheritdoc}
1858
	 */
1859 2
	public function toObject() {
1860 2
		$object = $this->prepareObject(new \stdClass());
1861 2
		$params = array('entity' => $this);
1862 2
		$object = _elgg_services()->hooks->trigger('to:object', 'entity', $params, $object);
1863 2
		return $object;
1864
	}
1865
1866
	/**
1867
	 * Prepare an object copy for toObject()
1868
	 *
1869
	 * @param \stdClass $object Object representation of the entity
1870
	 * @return \stdClass
1871
	 */
1872 2
	protected function prepareObject($object) {
1873 2
		$object->guid = $this->guid;
1874 2
		$object->type = $this->getType();
1875 2
		$object->subtype = $this->getSubtype();
1876 2
		$object->owner_guid = $this->getOwnerGUID();
1877 2
		$object->container_guid = $this->getContainerGUID();
1878 2
		$object->time_created = date('c', $this->getTimeCreated());
1879 2
		$object->time_updated = date('c', $this->getTimeUpdated());
1880 2
		$object->url = $this->getURL();
1881 2
		$object->read_access = (int)$this->access_id;
1882 2
		return $object;
1883
	}
1884
1885
	/*
1886
	 * LOCATABLE INTERFACE
1887
	 */
1888
1889
	/**
1890
	 * Gets the 'location' metadata for the entity
1891
	 *
1892
	 * @return string The location
1893
	 */
1894
	public function getLocation() {
1895
		return $this->location;
1896
	}
1897
1898
	/**
1899
	 * Sets the 'location' metadata for the entity
1900
	 *
1901
	 * @param string $location String representation of the location
1902
	 *
1903
	 * @return void
1904
	 */
1905
	public function setLocation($location) {
1906
		$this->location = $location;
1907
	}
1908
1909
	/**
1910
	 * Set latitude and longitude metadata tags for a given entity.
1911
	 *
1912
	 * @param float $lat  Latitude
1913
	 * @param float $long Longitude
1914
	 *
1915
	 * @return void
1916
	 * @todo Unimplemented
1917
	 */
1918 1
	public function setLatLong($lat, $long) {
1919 1
		$this->{"geo:lat"} = $lat;
1920 1
		$this->{"geo:long"} = $long;
1921 1
	}
1922
1923
	/**
1924
	 * Return the entity's latitude.
1925
	 *
1926
	 * @return float
1927
	 * @todo Unimplemented
1928
	 */
1929 1
	public function getLatitude() {
1930 1
		return (float)$this->{"geo:lat"};
1931
	}
1932
1933
	/**
1934
	 * Return the entity's longitude
1935
	 *
1936
	 * @return float
1937
	 * @todo Unimplemented
1938
	 */
1939 1
	public function getLongitude() {
1940 1
		return (float)$this->{"geo:long"};
1941
	}
1942
1943
	/*
1944
	 * SYSTEM LOG INTERFACE
1945
	 */
1946
1947
	/**
1948
	 * Return an identification for the object for storage in the system log.
1949
	 * This id must be an integer.
1950
	 *
1951
	 * @return int
1952
	 */
1953
	public function getSystemLogID() {
1954
		return $this->getGUID();
1955
	}
1956
1957
	/**
1958
	 * For a given ID, return the object associated with it.
1959
	 * This is used by the system log. It can be called on any Loggable object.
1960
	 *
1961
	 * @param int $id GUID.
1962
	 * @return int GUID
1963
	 */
1964
	public function getObjectFromID($id) {
1965
		return get_entity($id);
1966
	}
1967
1968
	/**
1969
	 * Returns tags for this entity.
1970
	 *
1971
	 * @warning Tags must be registered by {@link elgg_register_tag_metadata_name()}.
1972
	 *
1973
	 * @param array $tag_names Optionally restrict by tag metadata names.
1974
	 *
1975
	 * @return array
1976
	 */
1977
	public function getTags($tag_names = null) {
1978
		if ($tag_names && !is_array($tag_names)) {
1979
			$tag_names = array($tag_names);
1980
		}
1981
1982
		$valid_tags = elgg_get_registered_tag_metadata_names();
1983
		$entity_tags = array();
1984
1985
		foreach ($valid_tags as $tag_name) {
1986
			if (is_array($tag_names) && !in_array($tag_name, $tag_names)) {
1987
				continue;
1988
			}
1989
1990
			if ($tags = $this->$tag_name) {
1991
				// if a single tag, metadata returns a string.
1992
				// if multiple tags, metadata returns an array.
1993
				if (is_array($tags)) {
1994
					$entity_tags = array_merge($entity_tags, $tags);
1995
				} else {
1996
					$entity_tags[] = $tags;
1997
				}
1998
			}
1999
		}
2000
2001
		return $entity_tags;
2002
	}
2003
	
2004
	/**
2005
	 * Remove the membership of all access collections for this entity (if the entity is a user)
2006
	 *
2007
	 * @return bool
2008
	 * @since 1.11
2009
	 */
2010 1
	public function deleteAccessCollectionMemberships() {
2011
	
2012 1
		if (!$this->guid) {
2013
			return false;
2014
		}
2015
		
2016 1
		if ($this->type !== 'user') {
2017 1
			return true;
2018
		}
2019
		
2020
		$ac = _elgg_services()->accessCollections;
2021
		
2022
		$collections = $ac->getCollectionsByMember($this->guid);
2023
		if (empty($collections)) {
2024
			return true;
2025
		}
2026
		
2027
		$result = true;
2028
		foreach ($collections as $collection) {
2029
			$result = $result & $ac->removeUser($this->guid, $collection->id);
2030
		}
2031
		
2032
		return $result;
2033
	}
2034
	
2035
	/**
2036
	 * Remove all access collections owned by this entity
2037
	 *
2038
	 * @return bool
2039
	 * @since 1.11
2040
	 */
2041 1
	public function deleteOwnedAccessCollections() {
2042
		
2043 1
		if (!$this->guid) {
2044
			return false;
2045
		}
2046
		
2047 1
		$ac = _elgg_services()->accessCollections;
2048
		
2049 1
		$collections = $ac->getEntityCollections($this->guid);
2050 1
		if (empty($collections)) {
2051 1
			return true;
2052
		}
2053
		
2054
		$result = true;
2055
		foreach ($collections as $collection) {
2056
			$result = $result & $ac->delete($collection->id);
2057
		}
2058
		
2059
		return $result;
2060
	}
2061
2062
	/**
2063
	 * Update the last_action column in the entities table.
2064
	 *
2065
	 * @warning This is different to time_updated.  Time_updated is automatically set,
2066
	 * while last_action is only set when explicitly called.
2067
	 *
2068
	 * @param int $posted Timestamp of last action
2069
	 * @return int|false
2070
	 * @access private
2071
	 */
2072 2
	public function updateLastAction($posted = null) {
2073 2
		$posted = _elgg_services()->entityTable->updateLastAction($this, $posted);
2074 2
		if ($posted) {
2075 2
			$this->attributes['last_action'] = $posted;
2076 2
			_elgg_services()->entityCache->set($this);
2077 2
			$this->storeInPersistedCache(_elgg_get_memcache('new_entity_cache'));
2078 2
		}
2079 2
		return $posted;
2080
	}
2081
}
2082