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 |
||
44 | \Elgg\EntityIcon // Icon interface |
||
45 | { |
||
46 | |||
47 | /** |
||
48 | * Holds metadata until entity is saved. Once the entity is saved, |
||
49 | * metadata are written immediately to the database. |
||
50 | */ |
||
51 | protected $temp_metadata = []; |
||
52 | |||
53 | /** |
||
54 | * Holds annotations until entity is saved. Once the entity is saved, |
||
55 | * annotations are written immediately to the database. |
||
56 | */ |
||
57 | protected $temp_annotations = []; |
||
58 | |||
59 | /** |
||
60 | * Holds private settings until entity is saved. Once the entity is saved, |
||
61 | * private settings are written immediately to the database. |
||
62 | */ |
||
63 | protected $temp_private_settings = []; |
||
64 | |||
65 | /** |
||
66 | * Volatile data structure for this object, allows for storage of data |
||
67 | * in-memory that isn't sync'd back to the metadata table. |
||
68 | */ |
||
69 | protected $volatile = []; |
||
70 | |||
71 | /** |
||
72 | * Holds the original (persisted) attribute values that have been changed but not yet saved. |
||
73 | */ |
||
74 | protected $orig_attributes = []; |
||
75 | |||
76 | /** |
||
77 | * Create a new entity. |
||
78 | * |
||
79 | * Plugin developers should only use the constructor to create a new entity. |
||
80 | * To retrieve entities, use get_entity() and the elgg_get_entities* functions. |
||
81 | * |
||
82 | * If no arguments are passed, it creates a new entity. |
||
83 | * If a database result is passed as a \stdClass instance, it instantiates |
||
84 | * that entity. |
||
85 | * |
||
86 | * @param \stdClass $row Database row result. Default is null to create a new object. |
||
87 | * |
||
88 | * @throws IOException If cannot load remaining data from db |
||
89 | */ |
||
90 | 3711 | public function __construct(\stdClass $row = null) { |
|
91 | 3711 | $this->initializeAttributes(); |
|
92 | |||
93 | 3711 | if ($row && !$this->load($row)) { |
|
94 | $msg = "Failed to load new " . get_class() . " for GUID:" . $row->guid; |
||
95 | throw new \IOException($msg); |
||
96 | } |
||
97 | 3711 | } |
|
98 | |||
99 | /** |
||
100 | * Initialize the attributes array. |
||
101 | * |
||
102 | * This is vital to distinguish between metadata and base parameters. |
||
103 | * |
||
104 | * @return void |
||
105 | */ |
||
106 | 3711 | protected function initializeAttributes() { |
|
107 | 3711 | parent::initializeAttributes(); |
|
108 | |||
109 | 3711 | $this->attributes['guid'] = null; |
|
110 | 3711 | $this->attributes['type'] = null; |
|
111 | 3711 | $this->attributes['subtype'] = null; |
|
112 | |||
113 | 3711 | $this->attributes['owner_guid'] = _elgg_services()->session->getLoggedInUserGuid(); |
|
114 | 3711 | $this->attributes['container_guid'] = _elgg_services()->session->getLoggedInUserGuid(); |
|
115 | |||
116 | 3711 | $this->attributes['access_id'] = ACCESS_PRIVATE; |
|
117 | 3711 | $this->attributes['time_updated'] = null; |
|
118 | 3711 | $this->attributes['last_action'] = null; |
|
119 | 3711 | $this->attributes['enabled'] = "yes"; |
|
120 | |||
121 | 3711 | $this->attributes['type'] = $this->getType(); |
|
122 | 3711 | } |
|
123 | |||
124 | /** |
||
125 | * Clone an entity |
||
126 | * |
||
127 | * Resets the guid so that the entity can be saved as a distinct entity from |
||
128 | * the original. Creation time will be set when this new entity is saved. |
||
129 | * The owner and container guids come from the original entity. The clone |
||
130 | * method copies metadata but does not copy annotations or private settings. |
||
131 | * |
||
132 | * @note metadata will have its owner and access id set when the entity is saved |
||
133 | * and it will be the same as that of the entity. |
||
134 | * |
||
135 | * @return void |
||
136 | */ |
||
137 | 1 | public function __clone() { |
|
138 | 1 | $orig_entity = get_entity($this->guid); |
|
139 | 1 | if (!$orig_entity) { |
|
140 | _elgg_services()->logger->error("Failed to clone entity with GUID $this->guid"); |
||
141 | return; |
||
142 | } |
||
143 | |||
144 | 1 | $metadata_array = elgg_get_metadata([ |
|
145 | 1 | 'guid' => $this->guid, |
|
146 | 1 | 'limit' => 0 |
|
147 | ]); |
||
148 | |||
149 | 1 | $this->attributes['guid'] = null; |
|
150 | 1 | $this->attributes['time_created'] = null; |
|
151 | 1 | $this->attributes['time_updated'] = null; |
|
152 | 1 | $this->attributes['last_action'] = null; |
|
153 | |||
154 | 1 | $this->attributes['subtype'] = $orig_entity->getSubtype(); |
|
155 | |||
156 | // copy metadata over to new entity - slightly convoluted due to |
||
157 | // handling of metadata arrays |
||
158 | 1 | if (is_array($metadata_array)) { |
|
159 | // create list of metadata names |
||
160 | 1 | $metadata_names = []; |
|
161 | 1 | foreach ($metadata_array as $metadata) { |
|
162 | 1 | $metadata_names[] = $metadata['name']; |
|
163 | } |
||
164 | // arrays are stored with multiple enties per name |
||
165 | 1 | $metadata_names = array_unique($metadata_names); |
|
166 | |||
167 | // move the metadata over |
||
168 | 1 | foreach ($metadata_names as $name) { |
|
169 | 1 | $this->__set($name, $orig_entity->$name); |
|
170 | } |
||
171 | } |
||
172 | 1 | } |
|
173 | |||
174 | /** |
||
175 | * Set an attribute or metadata value for this entity |
||
176 | * |
||
177 | * Anything that is not an attribute is saved as metadata. |
||
178 | * |
||
179 | * @warning Metadata set this way will inherit the entity's owner and |
||
180 | * access ID. If you want more control over metadata, use \ElggEntity::setMetadata() |
||
181 | * |
||
182 | * @param string $name Name of the attribute or metadata |
||
183 | * @param mixed $value The value to be set |
||
184 | * @return void |
||
185 | * @see \ElggEntity::setMetadata() |
||
186 | */ |
||
187 | 3599 | public function __set($name, $value) { |
|
188 | 3599 | if ($this->$name === $value) { |
|
189 | // quick return if value is not changing |
||
190 | 68 | return; |
|
191 | } |
||
192 | |||
193 | 3599 | if (array_key_exists($name, $this->attributes)) { |
|
194 | // if an attribute is 1 (integer) and it's set to "1" (string), don't consider that a change. |
||
195 | 229 | if (is_int($this->attributes[$name]) |
|
196 | 229 | && is_string($value) |
|
197 | 229 | && ((string) $this->attributes[$name] === $value)) { |
|
198 | 1 | return; |
|
199 | } |
||
200 | |||
201 | // Due to https://github.com/Elgg/Elgg/pull/5456#issuecomment-17785173, certain attributes |
||
202 | // will store empty strings as null in the DB. In the somewhat common case that we're re-setting |
||
203 | // the value to empty string, don't consider this a change. |
||
204 | 229 | if (in_array($name, ['title', 'name', 'description']) |
|
205 | 229 | && $this->attributes[$name] === null |
|
206 | 229 | && $value === "") { |
|
207 | return; |
||
208 | } |
||
209 | |||
210 | // keep original values |
||
211 | 229 | if ($this->guid && !array_key_exists($name, $this->orig_attributes)) { |
|
212 | 7 | $this->orig_attributes[$name] = $this->attributes[$name]; |
|
213 | } |
||
214 | |||
215 | // Certain properties should not be manually changed! |
||
216 | switch ($name) { |
||
217 | 229 | case 'guid': |
|
218 | 229 | case 'time_updated': |
|
219 | 229 | case 'last_action': |
|
220 | return; |
||
221 | break; |
||
222 | 229 | case 'access_id': |
|
223 | 227 | case 'owner_guid': |
|
224 | 164 | case 'container_guid': |
|
225 | 183 | if ($value !== null) { |
|
226 | 183 | $this->attributes[$name] = (int) $value; |
|
227 | } else { |
||
228 | $this->attributes[$name] = null; |
||
229 | } |
||
230 | 183 | break; |
|
231 | default: |
||
232 | 156 | $this->attributes[$name] = $value; |
|
233 | 156 | break; |
|
234 | } |
||
235 | 229 | return; |
|
236 | } |
||
237 | |||
238 | 3582 | $this->setMetadata($name, $value); |
|
239 | 3582 | } |
|
240 | |||
241 | /** |
||
242 | * Get the original values of attribute(s) that have been modified since the entity was persisted. |
||
243 | * |
||
244 | * @return array |
||
245 | */ |
||
246 | 32 | public function getOriginalAttributes() { |
|
247 | 32 | return $this->orig_attributes; |
|
248 | } |
||
249 | |||
250 | /** |
||
251 | * Get an attribute or metadata value |
||
252 | * |
||
253 | * If the name matches an attribute, the attribute is returned. If metadata |
||
254 | * does not exist with that name, a null is returned. |
||
255 | * |
||
256 | * This only returns an array if there are multiple values for a particular |
||
257 | * $name key. |
||
258 | * |
||
259 | * @param string $name Name of the attribute or metadata |
||
260 | * @return mixed |
||
261 | */ |
||
262 | 3711 | public function __get($name) { |
|
263 | 3711 | if (array_key_exists($name, $this->attributes)) { |
|
264 | 3711 | if ($name === 'subtype' && $this->attributes['guid']) { |
|
265 | 1 | _elgg_services()->logger->warn('Reading ->subtype on a persisted entity is unreliable.'); |
|
266 | } |
||
267 | 3711 | return $this->attributes[$name]; |
|
268 | } |
||
269 | |||
270 | 3711 | return $this->getMetadata($name); |
|
271 | } |
||
272 | |||
273 | /** |
||
274 | * Get the entity's display name |
||
275 | * |
||
276 | * @return string The title or name of this entity. |
||
277 | */ |
||
278 | 4 | public function getDisplayName() { |
|
279 | 4 | return $this->name; |
|
280 | } |
||
281 | |||
282 | /** |
||
283 | * Sets the title or name of this entity. |
||
284 | * |
||
285 | * @param string $display_name The title or name of this entity. |
||
286 | * @return void |
||
287 | */ |
||
288 | public function setDisplayName($display_name) { |
||
289 | $this->name = $display_name; |
||
290 | } |
||
291 | |||
292 | /** |
||
293 | * Return the value of a piece of metadata. |
||
294 | * |
||
295 | * @param string $name Name |
||
296 | * |
||
297 | * @return mixed The value, or null if not found. |
||
298 | */ |
||
299 | 3711 | public function getMetadata($name) { |
|
300 | 3711 | $guid = $this->guid; |
|
301 | |||
302 | 3711 | if (!$guid) { |
|
303 | 231 | if (isset($this->temp_metadata[$name])) { |
|
304 | // md is returned as an array only if more than 1 entry |
||
305 | 75 | if (count($this->temp_metadata[$name]) == 1) { |
|
306 | 71 | return $this->temp_metadata[$name][0]; |
|
307 | } else { |
||
308 | 4 | return $this->temp_metadata[$name]; |
|
309 | } |
||
310 | } else { |
||
311 | 231 | return null; |
|
312 | } |
||
313 | } |
||
314 | |||
315 | // upon first cache miss, just load/cache all the metadata and retry. |
||
316 | // if this works, the rest of this function may not be needed! |
||
317 | 3711 | $cache = _elgg_services()->metadataCache; |
|
318 | 3711 | if ($cache->isLoaded($guid)) { |
|
319 | 3711 | return $cache->getSingle($guid, $name); |
|
320 | } else { |
||
321 | 3711 | $cache->populateFromEntities([$guid]); |
|
322 | // in case ignore_access was on, we have to check again... |
||
323 | 3711 | if ($cache->isLoaded($guid)) { |
|
324 | 3711 | return $cache->getSingle($guid, $name); |
|
325 | } |
||
326 | } |
||
327 | |||
328 | $md = elgg_get_metadata([ |
||
329 | 'guid' => $guid, |
||
330 | 'metadata_name' => $name, |
||
331 | 'limit' => 0, |
||
332 | 'distinct' => false, |
||
333 | ]); |
||
334 | |||
335 | $value = null; |
||
336 | |||
337 | if ($md && !is_array($md)) { |
||
338 | $value = $md->value; |
||
339 | } elseif (count($md) == 1) { |
||
340 | $value = $md[0]->value; |
||
341 | } else if ($md && is_array($md)) { |
||
342 | $value = metadata_array_to_values($md); |
||
343 | } |
||
344 | |||
345 | return $value; |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * Unset a property from metadata or attribute. |
||
350 | * |
||
351 | * @warning If you use this to unset an attribute, you must save the object! |
||
352 | * |
||
353 | * @param string $name The name of the attribute or metadata. |
||
354 | * |
||
355 | * @return void |
||
356 | * @todo some attributes should be set to null or other default values |
||
357 | */ |
||
358 | 42 | public function __unset($name) { |
|
359 | 42 | if (array_key_exists($name, $this->attributes)) { |
|
360 | $this->attributes[$name] = ""; |
||
361 | } else { |
||
362 | 42 | $this->deleteMetadata($name); |
|
363 | } |
||
364 | 42 | } |
|
365 | |||
366 | /** |
||
367 | * Set metadata on this entity. |
||
368 | * |
||
369 | * Plugin developers usually want to use the magic set method ($entity->name = 'value'). |
||
370 | * Use this method if you want to explicitly set the owner or access of the metadata. |
||
371 | * You cannot set the owner/access before the entity has been saved. |
||
372 | * |
||
373 | * @param string $name Name of the metadata |
||
374 | * @param mixed $value Value of the metadata (doesn't support assoc arrays) |
||
375 | * @param string $value_type 'text', 'integer', or '' for automatic detection |
||
376 | * @param bool $multiple Allow multiple values for a single name. |
||
377 | * Does not support associative arrays. |
||
378 | * @param int $owner_guid GUID of entity that owns the metadata. |
||
379 | * Default is owner of entity. |
||
380 | * |
||
381 | * @return bool |
||
382 | * @throws InvalidArgumentException |
||
383 | */ |
||
384 | 3586 | public function setMetadata($name, $value, $value_type = '', $multiple = false, $owner_guid = 0) { |
|
385 | |||
386 | // normalize value to an array that we will loop over |
||
387 | // remove indexes if value already an array. |
||
388 | 3586 | if (is_array($value)) { |
|
389 | 68 | $value = array_values($value); |
|
390 | } else { |
||
391 | 3583 | $value = [$value]; |
|
392 | } |
||
393 | |||
394 | // saved entity. persist md to db. |
||
395 | 3586 | if ($this->guid) { |
|
396 | // if overwriting, delete first. |
||
397 | 3582 | if (!$multiple) { |
|
398 | $options = [ |
||
399 | 3582 | 'guid' => $this->getGUID(), |
|
400 | 3582 | 'metadata_name' => $name, |
|
401 | 3582 | 'limit' => 0 |
|
402 | ]; |
||
403 | // @todo in 1.9 make this return false if can't add metadata |
||
404 | // https://github.com/elgg/elgg/issues/4520 |
||
405 | // |
||
406 | // need to remove access restrictions right now to delete |
||
407 | // because this is the expected behavior |
||
408 | 3582 | $ia = elgg_set_ignore_access(true); |
|
409 | 3582 | $delete_result = elgg_delete_metadata($options); |
|
410 | 3582 | elgg_set_ignore_access($ia); |
|
411 | |||
412 | 3582 | if (false === $delete_result) { |
|
413 | return false; |
||
414 | } |
||
415 | } |
||
416 | |||
417 | 3582 | $owner_guid = $owner_guid ? (int) $owner_guid : $this->owner_guid; |
|
418 | |||
419 | // add new md |
||
420 | 3582 | foreach ($value as $value_tmp) { |
|
421 | // at this point $value is appended because it was cleared above if needed. |
||
422 | 3582 | $md_id = _elgg_services()->metadataTable->create($this->guid, $name, $value_tmp, $value_type, |
|
423 | 3582 | $owner_guid, null, true); |
|
424 | 3582 | if (!$md_id) { |
|
425 | 3582 | return false; |
|
426 | } |
||
427 | } |
||
428 | |||
429 | 3582 | return true; |
|
430 | } else { |
||
431 | // unsaved entity. store in temp array |
||
432 | // returning single entries instead of an array of 1 element is decided in |
||
433 | // getMetaData(), just like pulling from the db. |
||
434 | |||
435 | 229 | if ($owner_guid != 0) { |
|
436 | $msg = "owner guid cannot be used in ElggEntity::setMetadata() until entity is saved."; |
||
437 | throw new \InvalidArgumentException($msg); |
||
438 | } |
||
439 | |||
440 | // if overwrite, delete first |
||
441 | 229 | if (!$multiple) { |
|
442 | 229 | $this->temp_metadata[$name] = $value; |
|
443 | 229 | return true; |
|
444 | } |
||
445 | |||
446 | 3 | if (!isset($this->temp_metadata[$name])) { |
|
447 | $this->temp_metadata[$name] = []; |
||
448 | } |
||
449 | |||
450 | // add new md |
||
451 | 3 | $this->temp_metadata[$name] = array_merge($this->temp_metadata[$name], $value); |
|
452 | 3 | return true; |
|
453 | } |
||
454 | } |
||
455 | |||
456 | /** |
||
457 | * Deletes all metadata on this object (metadata.entity_guid = $this->guid). |
||
458 | * If you pass a name, only metadata matching that name will be deleted. |
||
459 | * |
||
460 | * @warning Calling this with no $name will clear all metadata on the entity. |
||
461 | * |
||
462 | * @param null|string $name The name of the metadata to remove. |
||
463 | * @return bool |
||
464 | * @since 1.8 |
||
465 | */ |
||
466 | 233 | public function deleteMetadata($name = null) { |
|
467 | |||
468 | 233 | if (!$this->guid) { |
|
469 | return false; |
||
470 | } |
||
471 | |||
472 | $options = [ |
||
473 | 233 | 'guid' => $this->guid, |
|
474 | 233 | 'limit' => 0 |
|
475 | ]; |
||
476 | 233 | if ($name) { |
|
477 | 47 | $options['metadata_name'] = $name; |
|
478 | } |
||
479 | |||
480 | 233 | return elgg_delete_metadata($options); |
|
481 | } |
||
482 | |||
483 | /** |
||
484 | * Get a piece of volatile (non-persisted) data on this entity. |
||
485 | * |
||
486 | * @param string $name The name of the volatile data |
||
487 | * |
||
488 | * @return mixed The value or null if not found. |
||
489 | */ |
||
490 | 9 | public function getVolatileData($name) { |
|
491 | 9 | return array_key_exists($name, $this->volatile) ? $this->volatile[$name] : null; |
|
492 | } |
||
493 | |||
494 | /** |
||
495 | * Set a piece of volatile (non-persisted) data on this entity |
||
496 | * |
||
497 | * @param string $name Name |
||
498 | * @param mixed $value Value |
||
499 | * |
||
500 | * @return void |
||
501 | */ |
||
502 | 18 | public function setVolatileData($name, $value) { |
|
503 | 18 | $this->volatile[$name] = $value; |
|
504 | 18 | } |
|
505 | |||
506 | /** |
||
507 | * Cache the entity in a persisted cache |
||
508 | * |
||
509 | * @param ElggSharedMemoryCache $cache Memcache or null cache |
||
510 | * @param int $last_action Last action time |
||
511 | * |
||
512 | * @return void |
||
513 | * @access private |
||
514 | * @internal |
||
515 | */ |
||
516 | 355 | public function storeInPersistedCache(\ElggSharedMemoryCache $cache, $last_action = 0) { |
|
517 | 355 | $tmp = $this->volatile; |
|
518 | |||
519 | // don't store volatile data |
||
520 | 355 | $this->volatile = []; |
|
521 | 355 | if ($last_action) { |
|
522 | $this->last_action = (int) $last_action; |
||
523 | } |
||
524 | 355 | $cache->save($this->guid, $this); |
|
525 | |||
526 | 355 | $this->volatile = $tmp; |
|
527 | 355 | } |
|
528 | |||
529 | /** |
||
530 | * Remove all relationships to and from this entity. |
||
531 | * If you pass a relationship name, only relationships matching that name |
||
532 | * will be deleted. |
||
533 | * |
||
534 | * @warning Calling this with no $relationship will clear all relationships |
||
535 | * for this entity. |
||
536 | * |
||
537 | * @param null|string $relationship The name of the relationship to remove. |
||
538 | * @return bool |
||
539 | * @see \ElggEntity::addRelationship() |
||
540 | * @see \ElggEntity::removeRelationship() |
||
541 | */ |
||
542 | 192 | public function deleteRelationships($relationship = null) { |
|
543 | 192 | $relationship = (string) $relationship; |
|
544 | 192 | $result = remove_entity_relationships($this->getGUID(), $relationship); |
|
545 | 192 | return $result && remove_entity_relationships($this->getGUID(), $relationship, true); |
|
546 | } |
||
547 | |||
548 | /** |
||
549 | * Add a relationship between this an another entity. |
||
550 | * |
||
551 | * @tip Read the relationship like "This entity is a $relationship of $guid_two." |
||
552 | * |
||
553 | * @param int $guid_two GUID of the target entity of the relationship. |
||
554 | * @param string $relationship The type of relationship. |
||
555 | * |
||
556 | * @return bool |
||
557 | * @see \ElggEntity::removeRelationship() |
||
558 | * @see \ElggEntity::deleteRelationships() |
||
559 | */ |
||
560 | 3 | public function addRelationship($guid_two, $relationship) { |
|
561 | 3 | return add_entity_relationship($this->getGUID(), $relationship, $guid_two); |
|
562 | } |
||
563 | |||
564 | /** |
||
565 | * Remove a relationship |
||
566 | * |
||
567 | * @param int $guid_two GUID of the target entity of the relationship. |
||
568 | * @param string $relationship The type of relationship. |
||
569 | * |
||
570 | * @return bool |
||
571 | * @see \ElggEntity::addRelationship() |
||
572 | * @see \ElggEntity::deleteRelationships() |
||
573 | */ |
||
574 | 1 | public function removeRelationship($guid_two, $relationship) { |
|
575 | 1 | return remove_entity_relationship($this->getGUID(), $relationship, $guid_two); |
|
576 | } |
||
577 | |||
578 | /** |
||
579 | * Adds a private setting to this entity. |
||
580 | * |
||
581 | * Private settings are similar to metadata but will not |
||
582 | * be searched and there are fewer helper functions for them. |
||
583 | * |
||
584 | * @param string $name Name of private setting |
||
585 | * @param mixed $value Value of private setting |
||
586 | * |
||
587 | * @return bool |
||
588 | */ |
||
589 | 33 | public function setPrivateSetting($name, $value) { |
|
590 | 33 | if ((int) $this->guid > 0) { |
|
591 | 33 | return set_private_setting($this->getGUID(), $name, $value); |
|
592 | } else { |
||
593 | 29 | $this->temp_private_settings[$name] = $value; |
|
594 | 29 | return true; |
|
595 | } |
||
596 | } |
||
597 | |||
598 | /** |
||
599 | * Returns a private setting value |
||
600 | * |
||
601 | * @param string $name Name of the private setting |
||
602 | * |
||
603 | * @return mixed Null if the setting does not exist |
||
604 | */ |
||
605 | 9 | public function getPrivateSetting($name) { |
|
606 | 9 | if ((int) ($this->guid) > 0) { |
|
607 | 9 | return get_private_setting($this->getGUID(), $name); |
|
608 | } else { |
||
609 | 5 | if (isset($this->temp_private_settings[$name])) { |
|
610 | 5 | return $this->temp_private_settings[$name]; |
|
611 | } |
||
612 | } |
||
613 | return null; |
||
614 | } |
||
615 | |||
616 | /** |
||
617 | * Removes private setting |
||
618 | * |
||
619 | * @param string $name Name of the private setting |
||
620 | * |
||
621 | * @return bool |
||
622 | */ |
||
623 | public function removePrivateSetting($name) { |
||
624 | return remove_private_setting($this->getGUID(), $name); |
||
625 | } |
||
626 | |||
627 | /** |
||
628 | * Deletes all annotations on this object (annotations.entity_guid = $this->guid). |
||
629 | * If you pass a name, only annotations matching that name will be deleted. |
||
630 | * |
||
631 | * @warning Calling this with no or empty arguments will clear all annotations on the entity. |
||
632 | * |
||
633 | * @param null|string $name The annotations name to remove. |
||
634 | * @return bool |
||
635 | * @since 1.8 |
||
636 | */ |
||
637 | 192 | View Code Duplication | public function deleteAnnotations($name = null) { |
638 | $options = [ |
||
639 | 192 | 'guid' => $this->guid, |
|
640 | 192 | 'limit' => 0 |
|
641 | ]; |
||
642 | 192 | if ($name) { |
|
643 | 1 | $options['annotation_name'] = $name; |
|
644 | } |
||
645 | |||
646 | 192 | return elgg_delete_annotations($options); |
|
647 | } |
||
648 | |||
649 | /** |
||
650 | * Deletes all annotations owned by this object (annotations.owner_guid = $this->guid). |
||
651 | * If you pass a name, only annotations matching that name will be deleted. |
||
652 | * |
||
653 | * @param null|string $name The name of annotations to delete. |
||
654 | * @return bool |
||
655 | * @since 1.8 |
||
656 | */ |
||
657 | 192 | public function deleteOwnedAnnotations($name = null) { |
|
658 | // access is turned off for this because they might |
||
659 | // no longer have access to an entity they created annotations on. |
||
660 | 192 | $ia = elgg_set_ignore_access(true); |
|
661 | $options = [ |
||
662 | 192 | 'annotation_owner_guid' => $this->guid, |
|
663 | 192 | 'limit' => 0 |
|
664 | ]; |
||
665 | 192 | if ($name) { |
|
666 | $options['annotation_name'] = $name; |
||
667 | } |
||
668 | |||
669 | 192 | $r = elgg_delete_annotations($options); |
|
670 | 192 | elgg_set_ignore_access($ia); |
|
671 | 192 | return $r; |
|
672 | } |
||
673 | |||
674 | /** |
||
675 | * Disables annotations for this entity, optionally based on name. |
||
676 | * |
||
677 | * @param string $name An options name of annotations to disable. |
||
678 | * @return bool |
||
679 | * @since 1.8 |
||
680 | */ |
||
681 | 5 | View Code Duplication | public function disableAnnotations($name = '') { |
682 | $options = [ |
||
683 | 5 | 'guid' => $this->guid, |
|
684 | 5 | 'limit' => 0 |
|
685 | ]; |
||
686 | 5 | if ($name) { |
|
687 | $options['annotation_name'] = $name; |
||
688 | } |
||
689 | |||
690 | 5 | return elgg_disable_annotations($options); |
|
691 | } |
||
692 | |||
693 | /** |
||
694 | * Enables annotations for this entity, optionally based on name. |
||
695 | * |
||
696 | * @warning Before calling this, you must use {@link access_show_hidden_entities()} |
||
697 | * |
||
698 | * @param string $name An options name of annotations to enable. |
||
699 | * @return bool |
||
700 | * @since 1.8 |
||
701 | */ |
||
702 | 3 | View Code Duplication | public function enableAnnotations($name = '') { |
703 | $options = [ |
||
704 | 3 | 'guid' => $this->guid, |
|
705 | 3 | 'limit' => 0 |
|
706 | ]; |
||
707 | 3 | if ($name) { |
|
708 | $options['annotation_name'] = $name; |
||
709 | } |
||
710 | |||
711 | 3 | return elgg_enable_annotations($options); |
|
712 | } |
||
713 | |||
714 | /** |
||
715 | * Helper function to return annotation calculation results |
||
716 | * |
||
717 | * @param string $name The annotation name. |
||
718 | * @param string $calculation A valid MySQL function to run its values through |
||
719 | * @return mixed |
||
720 | */ |
||
721 | 2 | private function getAnnotationCalculation($name, $calculation) { |
|
722 | $options = [ |
||
723 | 2 | 'guid' => $this->getGUID(), |
|
724 | 'distinct' => false, |
||
725 | 2 | 'annotation_name' => $name, |
|
726 | 2 | 'annotation_calculation' => $calculation |
|
727 | ]; |
||
728 | |||
729 | 2 | return elgg_get_annotations($options); |
|
730 | } |
||
731 | |||
732 | /** |
||
733 | * Adds an annotation to an entity. |
||
734 | * |
||
735 | * @warning By default, annotations are private. |
||
736 | * |
||
737 | * @warning Annotating an unsaved entity more than once with the same name |
||
738 | * will only save the last annotation. |
||
739 | * |
||
740 | * @param string $name Annotation name |
||
741 | * @param mixed $value Annotation value |
||
742 | * @param int $access_id Access ID |
||
743 | * @param int $owner_guid GUID of the annotation owner |
||
744 | * @param string $vartype The type of annotation value |
||
745 | * |
||
746 | * @return bool|int Returns int if an annotation is saved |
||
747 | */ |
||
748 | 123 | public function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_guid = 0, $vartype = "") { |
|
749 | 123 | if ((int) $this->guid > 0) { |
|
750 | 123 | return create_annotation($this->getGUID(), $name, $value, $vartype, $owner_guid, $access_id); |
|
751 | } else { |
||
752 | 24 | $this->temp_annotations[$name] = $value; |
|
753 | } |
||
754 | 24 | return true; |
|
755 | } |
||
756 | |||
757 | /** |
||
758 | * Gets an array of annotations. |
||
759 | * |
||
760 | * To retrieve annotations on an unsaved entity, pass array('name' => [annotation name]) |
||
761 | * as the options array. |
||
762 | * |
||
763 | * @param array $options Array of options for elgg_get_annotations() except guid. |
||
764 | * |
||
765 | * @return array |
||
766 | * @see elgg_get_annotations() |
||
767 | */ |
||
768 | 9 | public function getAnnotations(array $options = []) { |
|
769 | 9 | if ($this->guid) { |
|
770 | 9 | $options['guid'] = $this->guid; |
|
771 | |||
772 | 9 | return elgg_get_annotations($options); |
|
773 | } else { |
||
774 | $name = elgg_extract('annotation_name', $options, ''); |
||
775 | |||
776 | if (isset($this->temp_annotations[$name])) { |
||
777 | return [$this->temp_annotations[$name]]; |
||
778 | } |
||
779 | } |
||
780 | |||
781 | return []; |
||
782 | } |
||
783 | |||
784 | /** |
||
785 | * Count annotations. |
||
786 | * |
||
787 | * @param string $name The type of annotation. |
||
788 | * |
||
789 | * @return int |
||
790 | */ |
||
791 | 2 | public function countAnnotations($name = "") { |
|
792 | 2 | return $this->getAnnotationCalculation($name, 'count'); |
|
793 | } |
||
794 | |||
795 | /** |
||
796 | * Get the average of an integer type annotation. |
||
797 | * |
||
798 | * @param string $name Annotation name |
||
799 | * |
||
800 | * @return int |
||
801 | */ |
||
802 | public function getAnnotationsAvg($name) { |
||
803 | return $this->getAnnotationCalculation($name, 'avg'); |
||
804 | } |
||
805 | |||
806 | /** |
||
807 | * Get the sum of integer type annotations of a given name. |
||
808 | * |
||
809 | * @param string $name Annotation name |
||
810 | * |
||
811 | * @return int |
||
812 | */ |
||
813 | public function getAnnotationsSum($name) { |
||
814 | return $this->getAnnotationCalculation($name, 'sum'); |
||
815 | } |
||
816 | |||
817 | /** |
||
818 | * Get the minimum of integer type annotations of given name. |
||
819 | * |
||
820 | * @param string $name Annotation name |
||
821 | * |
||
822 | * @return int |
||
823 | */ |
||
824 | public function getAnnotationsMin($name) { |
||
825 | return $this->getAnnotationCalculation($name, 'min'); |
||
826 | } |
||
827 | |||
828 | /** |
||
829 | * Get the maximum of integer type annotations of a given name. |
||
830 | * |
||
831 | * @param string $name Annotation name |
||
832 | * |
||
833 | * @return int |
||
834 | */ |
||
835 | public function getAnnotationsMax($name) { |
||
836 | return $this->getAnnotationCalculation($name, 'max'); |
||
837 | } |
||
838 | |||
839 | /** |
||
840 | * Count the number of comments attached to this entity. |
||
841 | * |
||
842 | * @return int Number of comments |
||
843 | * @since 1.8.0 |
||
844 | */ |
||
845 | public function countComments() { |
||
846 | $params = ['entity' => $this]; |
||
847 | $num = _elgg_services()->hooks->trigger('comments:count', $this->getType(), $params); |
||
848 | |||
849 | if (is_int($num)) { |
||
850 | return $num; |
||
851 | } else { |
||
852 | return elgg_get_entities([ |
||
853 | 'type' => 'object', |
||
854 | 'subtype' => 'comment', |
||
855 | 'container_guid' => $this->getGUID(), |
||
856 | 'count' => true, |
||
857 | 'distinct' => false, |
||
858 | ]); |
||
859 | } |
||
860 | } |
||
861 | |||
862 | /** |
||
863 | * Gets an array of entities with a relationship to this entity. |
||
864 | * |
||
865 | * @param array $options Options array. See elgg_get_entities_from_relationship() |
||
866 | * for a list of options. 'relationship_guid' is set to |
||
867 | * this entity. |
||
868 | * |
||
869 | * @return array|false An array of entities or false on failure |
||
870 | * @see elgg_get_entities_from_relationship() |
||
871 | */ |
||
872 | public function getEntitiesFromRelationship(array $options = []) { |
||
873 | $options['relationship_guid'] = $this->guid; |
||
874 | return elgg_get_entities_from_relationship($options); |
||
875 | } |
||
876 | |||
877 | /** |
||
878 | * Gets the number of entities from a specific relationship type |
||
879 | * |
||
880 | * @param string $relationship Relationship type (eg "friends") |
||
881 | * @param bool $inverse_relationship Invert relationship |
||
882 | * |
||
883 | * @return int|false The number of entities or false on failure |
||
884 | */ |
||
885 | public function countEntitiesFromRelationship($relationship, $inverse_relationship = false) { |
||
886 | return elgg_get_entities_from_relationship([ |
||
887 | 'relationship' => $relationship, |
||
888 | 'relationship_guid' => $this->getGUID(), |
||
889 | 'inverse_relationship' => $inverse_relationship, |
||
890 | 'count' => true |
||
891 | ]); |
||
892 | } |
||
893 | |||
894 | /** |
||
895 | * Can a user edit this entity? |
||
896 | * |
||
897 | * @tip Can be overridden by registering for the permissions_check plugin hook. |
||
898 | * |
||
899 | * @param int $user_guid The user GUID, optionally (default: logged in user) |
||
900 | * |
||
901 | * @return bool Whether this entity is editable by the given user. |
||
902 | * @see elgg_set_ignore_access() |
||
903 | */ |
||
904 | 107 | public function canEdit($user_guid = 0) { |
|
905 | 107 | return _elgg_services()->userCapabilities->canEdit($this, $user_guid); |
|
906 | } |
||
907 | |||
908 | /** |
||
909 | * Can a user delete this entity? |
||
910 | * |
||
911 | * @tip Can be overridden by registering for the permissions_check:delete plugin hook. |
||
912 | * |
||
913 | * @param int $user_guid The user GUID, optionally (default: logged in user) |
||
914 | * |
||
915 | * @return bool Whether this entity is deletable by the given user. |
||
916 | * @since 1.11 |
||
917 | * @see elgg_set_ignore_access() |
||
918 | */ |
||
919 | 211 | public function canDelete($user_guid = 0) { |
|
920 | 211 | return _elgg_services()->userCapabilities->canDelete($this, $user_guid); |
|
921 | } |
||
922 | |||
923 | /** |
||
924 | * Can a user edit metadata on this entity? |
||
925 | * |
||
926 | * If no specific metadata is passed, it returns whether the user can |
||
927 | * edit any metadata on the entity. |
||
928 | * |
||
929 | * @tip Can be overridden by by registering for the permissions_check:metadata |
||
930 | * plugin hook. |
||
931 | * |
||
932 | * @param \ElggMetadata $metadata The piece of metadata to specifically check or null for any metadata |
||
933 | * @param int $user_guid The user GUID, optionally (default: logged in user) |
||
934 | * |
||
935 | * @return bool |
||
936 | * @see elgg_set_ignore_access() |
||
937 | */ |
||
938 | 175 | public function canEditMetadata($metadata = null, $user_guid = 0) { |
|
939 | 175 | return _elgg_services()->userCapabilities->canEditMetadata($this, $user_guid, $metadata); |
|
940 | } |
||
941 | |||
942 | /** |
||
943 | * Can a user add an entity to this container |
||
944 | * |
||
945 | * @param int $user_guid The GUID of the user creating the entity (0 for logged in user). |
||
946 | * @param string $type The type of entity we're looking to write |
||
947 | * @param string $subtype The subtype of the entity we're looking to write |
||
948 | * |
||
949 | * @return bool |
||
950 | * @see elgg_set_ignore_access() |
||
951 | */ |
||
952 | 177 | public function canWriteToContainer($user_guid = 0, $type = 'all', $subtype = 'all') { |
|
953 | 177 | return _elgg_services()->userCapabilities->canWriteToContainer($this, $user_guid, $type, $subtype); |
|
954 | } |
||
955 | |||
956 | /** |
||
957 | * Can a user comment on an entity? |
||
958 | * |
||
959 | * @tip Can be overridden by registering for the permissions_check:comment, |
||
960 | * <entity type> plugin hook. |
||
961 | * |
||
962 | * @param int $user_guid User guid (default is logged in user) |
||
963 | * @param bool $default Default permission |
||
964 | * @return bool |
||
965 | */ |
||
966 | 2 | public function canComment($user_guid = 0, $default = null) { |
|
967 | 2 | return _elgg_services()->userCapabilities->canComment($this, $user_guid, $default); |
|
968 | } |
||
969 | |||
970 | /** |
||
971 | * Can a user annotate an entity? |
||
972 | * |
||
973 | * @tip Can be overridden by registering for the plugin hook [permissions_check:annotate:<name>, |
||
974 | * <entity type>] or [permissions_check:annotate, <entity type>]. The hooks are called in that order. |
||
975 | * |
||
976 | * @tip If you want logged out users to annotate an object, do not call |
||
977 | * canAnnotate(). It's easier than using the plugin hook. |
||
978 | * |
||
979 | * @param int $user_guid User guid (default is logged in user) |
||
980 | * @param string $annotation_name The name of the annotation (default is unspecified) |
||
981 | * |
||
982 | * @return bool |
||
983 | */ |
||
984 | 8 | public function canAnnotate($user_guid = 0, $annotation_name = '') { |
|
985 | 8 | return _elgg_services()->userCapabilities->canAnnotate($this, $user_guid, $annotation_name); |
|
986 | } |
||
987 | |||
988 | /** |
||
989 | * Returns the access_id. |
||
990 | * |
||
991 | * @return int The access ID |
||
992 | */ |
||
993 | public function getAccessID() { |
||
994 | return $this->access_id; |
||
995 | } |
||
996 | |||
997 | /** |
||
998 | * Returns the guid. |
||
999 | * |
||
1000 | * @return int|null GUID |
||
1001 | */ |
||
1002 | 3614 | public function getGUID() { |
|
1003 | 3614 | return $this->guid; |
|
1004 | } |
||
1005 | |||
1006 | /** |
||
1007 | * Returns the entity type |
||
1008 | * |
||
1009 | * @return string The entity type |
||
1010 | */ |
||
1011 | 1 | public function getType() { |
|
1012 | // this is just for the PHPUnit mocking framework |
||
1013 | 1 | return $this->type; |
|
1014 | } |
||
1015 | |||
1016 | /** |
||
1017 | * Get the entity subtype |
||
1018 | * |
||
1019 | * @return string The entity subtype |
||
1020 | */ |
||
1021 | 265 | public function getSubtype() { |
|
1022 | // If this object hasn't been saved, then return the subtype string. |
||
1023 | 265 | if ($this->attributes['guid']) { |
|
1024 | 261 | return get_subtype_from_id($this->attributes['subtype']); |
|
1025 | } |
||
1026 | 7 | return $this->attributes['subtype']; |
|
1027 | } |
||
1028 | |||
1029 | /** |
||
1030 | * Get the guid of the entity's owner. |
||
1031 | * |
||
1032 | * @return int The owner GUID |
||
1033 | */ |
||
1034 | 68 | public function getOwnerGUID() { |
|
1035 | 68 | return (int) $this->owner_guid; |
|
1036 | } |
||
1037 | |||
1038 | /** |
||
1039 | * Gets the \ElggEntity that owns this entity. |
||
1040 | * |
||
1041 | * @return \ElggEntity The owning entity |
||
1042 | */ |
||
1043 | 172 | public function getOwnerEntity() { |
|
1044 | 172 | return get_entity($this->owner_guid); |
|
1045 | } |
||
1046 | |||
1047 | /** |
||
1048 | * Set the container for this object. |
||
1049 | * |
||
1050 | * @param int $container_guid The ID of the container. |
||
1051 | * |
||
1052 | * @return bool |
||
1053 | */ |
||
1054 | 1 | public function setContainerGUID($container_guid) { |
|
1055 | 1 | return $this->container_guid = (int) $container_guid; |
|
1056 | } |
||
1057 | |||
1058 | /** |
||
1059 | * Gets the container GUID for this entity. |
||
1060 | * |
||
1061 | * @return int |
||
1062 | */ |
||
1063 | 179 | public function getContainerGUID() { |
|
1064 | 179 | return (int) $this->container_guid; |
|
1065 | } |
||
1066 | |||
1067 | /** |
||
1068 | * Get the container entity for this object. |
||
1069 | * |
||
1070 | * @return \ElggEntity |
||
1071 | * @since 1.8.0 |
||
1072 | */ |
||
1073 | 178 | public function getContainerEntity() { |
|
1074 | 178 | return get_entity($this->getContainerGUID()); |
|
1075 | } |
||
1076 | |||
1077 | /** |
||
1078 | * Returns the UNIX epoch time that this entity was last updated |
||
1079 | * |
||
1080 | * @return int UNIX epoch time |
||
1081 | */ |
||
1082 | 1 | public function getTimeUpdated() { |
|
1083 | 1 | return $this->time_updated; |
|
1084 | } |
||
1085 | |||
1086 | /** |
||
1087 | * Gets the URL for this entity. |
||
1088 | * |
||
1089 | * Plugins can register for the 'entity:url', <type> plugin hook to |
||
1090 | * customize the url for an entity. |
||
1091 | * |
||
1092 | * @return string The URL of the entity |
||
1093 | */ |
||
1094 | 10 | public function getURL() { |
|
1095 | 10 | $url = _elgg_services()->hooks->trigger('entity:url', $this->getType(), ['entity' => $this]); |
|
1096 | |||
1097 | 10 | if ($url === null || $url === '' || $url === false) { |
|
1098 | 10 | return ''; |
|
1099 | } |
||
1100 | |||
1101 | return elgg_normalize_url($url); |
||
1102 | } |
||
1103 | |||
1104 | /** |
||
1105 | * Saves icons using an uploaded file as the source. |
||
1106 | * |
||
1107 | * @param string $input_name Form input name |
||
1108 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
1109 | * @param array $coords An array of cropping coordinates x1, y1, x2, y2 |
||
1110 | * @return bool |
||
1111 | */ |
||
1112 | public function saveIconFromUploadedFile($input_name, $type = 'icon', array $coords = []) { |
||
1113 | return _elgg_services()->iconService->saveIconFromUploadedFile($this, $input_name, $type, $coords); |
||
1114 | } |
||
1115 | |||
1116 | /** |
||
1117 | * Saves icons using a local file as the source. |
||
1118 | * |
||
1119 | * @param string $filename The full path to the local file |
||
1120 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
1121 | * @param array $coords An array of cropping coordinates x1, y1, x2, y2 |
||
1122 | * @return bool |
||
1123 | */ |
||
1124 | public function saveIconFromLocalFile($filename, $type = 'icon', array $coords = []) { |
||
1125 | return _elgg_services()->iconService->saveIconFromLocalFile($this, $filename, $type, $coords); |
||
1126 | } |
||
1127 | |||
1128 | /** |
||
1129 | * Saves icons using a file located in the data store as the source. |
||
1130 | * |
||
1131 | * @param string $file An ElggFile instance |
||
1132 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
1133 | * @param array $coords An array of cropping coordinates x1, y1, x2, y2 |
||
1134 | * @return bool |
||
1135 | */ |
||
1136 | public function saveIconFromElggFile(\ElggFile $file, $type = 'icon', array $coords = []) { |
||
1137 | return _elgg_services()->iconService->saveIconFromElggFile($this, $file, $type, $coords); |
||
1138 | } |
||
1139 | |||
1140 | /** |
||
1141 | * Returns entity icon as an ElggIcon object |
||
1142 | * The icon file may or may not exist on filestore |
||
1143 | * |
||
1144 | * @param string $size Size of the icon |
||
1145 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
1146 | * @return \ElggIcon |
||
1147 | */ |
||
1148 | 5 | public function getIcon($size, $type = 'icon') { |
|
1149 | 5 | return _elgg_services()->iconService->getIcon($this, $size, $type); |
|
1150 | } |
||
1151 | |||
1152 | /** |
||
1153 | * Removes all icon files and metadata for the passed type of icon. |
||
1154 | * |
||
1155 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
1156 | * @return bool |
||
1157 | */ |
||
1158 | public function deleteIcon($type = 'icon') { |
||
1159 | return _elgg_services()->iconService->deleteIcon($this, $type); |
||
1160 | } |
||
1161 | |||
1162 | /** |
||
1163 | * Returns the timestamp of when the icon was changed. |
||
1164 | * |
||
1165 | * @param string $size The size of the icon |
||
1166 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
1167 | * |
||
1168 | * @return int|null A unix timestamp of when the icon was last changed, or null if not set. |
||
1169 | */ |
||
1170 | public function getIconLastChange($size, $type = 'icon') { |
||
1171 | return _elgg_services()->iconService->getIconLastChange($this, $size, $type); |
||
1172 | } |
||
1173 | |||
1174 | /** |
||
1175 | * Returns if the entity has an icon of the passed type. |
||
1176 | * |
||
1177 | * @param string $size The size of the icon |
||
1178 | * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' |
||
1179 | * @return bool |
||
1180 | */ |
||
1181 | public function hasIcon($size, $type = 'icon') { |
||
1182 | return _elgg_services()->iconService->hasIcon($this, $size, $type); |
||
1183 | } |
||
1184 | |||
1185 | /** |
||
1186 | * Get the URL for this entity's icon |
||
1187 | * |
||
1188 | * Plugins can register for the 'entity:icon:url', <type> plugin hook |
||
1189 | * to customize the icon for an entity. |
||
1190 | * |
||
1191 | * @param mixed $params A string defining the size of the icon (e.g. tiny, small, medium, large) |
||
1192 | * or an array of parameters including 'size' |
||
1193 | * @return string The URL |
||
1194 | * @since 1.8.0 |
||
1195 | */ |
||
1196 | 1 | public function getIconURL($params = []) { |
|
1197 | 1 | return _elgg_services()->iconService->getIconURL($this, $params); |
|
1198 | } |
||
1199 | |||
1200 | /** |
||
1201 | * Save an entity. |
||
1202 | * |
||
1203 | * @return bool|int |
||
1204 | * @throws InvalidParameterException |
||
1205 | * @throws IOException |
||
1206 | */ |
||
1207 | 207 | public function save() { |
|
1208 | 207 | $guid = $this->guid; |
|
1209 | 207 | if ($guid > 0) { |
|
1210 | 69 | $guid = $this->update(); |
|
1211 | } else { |
||
1212 | 207 | $guid = $this->create(); |
|
1213 | 207 | if ($guid && !_elgg_services()->hooks->getEvents()->trigger('create', $this->type, $this)) { |
|
1214 | // plugins that return false to event don't need to override the access system |
||
1215 | $ia = elgg_set_ignore_access(true); |
||
1216 | $this->delete(); |
||
1217 | elgg_set_ignore_access($ia); |
||
1218 | return false; |
||
1219 | } |
||
1220 | } |
||
1221 | |||
1222 | 207 | if ($guid) { |
|
1223 | 207 | _elgg_services()->entityCache->set($this); |
|
1224 | 207 | $this->storeInPersistedCache(_elgg_get_memcache('new_entity_cache')); |
|
1225 | } |
||
1226 | |||
1227 | 207 | return $guid; |
|
1228 | } |
||
1229 | |||
1230 | /** |
||
1231 | * Create a new entry in the entities table. |
||
1232 | * |
||
1233 | * Saves the base information in the entities table for the entity. Saving |
||
1234 | * the type-specific information is handled in the calling class method. |
||
1235 | * |
||
1236 | * @warning Entities must have an entry in both the entities table and their type table |
||
1237 | * or they will throw an exception when loaded. |
||
1238 | * |
||
1239 | * @return int The new entity's GUID |
||
1240 | * @throws InvalidParameterException If the entity's type has not been set. |
||
1241 | * @throws IOException If the new row fails to write to the DB. |
||
1242 | */ |
||
1243 | 207 | protected function create() { |
|
1244 | |||
1245 | 207 | $type = $this->attributes['type']; |
|
1246 | 207 | if (!in_array($type, \Elgg\Config::getEntityTypes())) { |
|
1247 | throw new \InvalidParameterException('Entity type must be one of the allowed types: ' |
||
1248 | . implode(', ', \Elgg\Config::getEntityTypes())); |
||
1249 | } |
||
1250 | |||
1251 | 207 | $subtype = $this->attributes['subtype']; |
|
1252 | 207 | $subtype_id = add_subtype($type, $subtype); |
|
1253 | 207 | $owner_guid = (int) $this->attributes['owner_guid']; |
|
1254 | 207 | $access_id = (int) $this->attributes['access_id']; |
|
1255 | 207 | $now = $this->getCurrentTime()->getTimestamp(); |
|
1256 | 207 | $time_created = isset($this->attributes['time_created']) ? (int) $this->attributes['time_created'] : $now; |
|
1257 | |||
1258 | 207 | $container_guid = $this->attributes['container_guid']; |
|
1259 | 207 | if ($container_guid == 0) { |
|
1260 | 70 | $container_guid = $owner_guid; |
|
1261 | 70 | $this->attributes['container_guid'] = $container_guid; |
|
1262 | } |
||
1263 | 207 | $container_guid = (int) $container_guid; |
|
1264 | |||
1265 | 207 | if ($access_id == ACCESS_DEFAULT) { |
|
1266 | throw new \InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h'); |
||
1267 | } |
||
1268 | |||
1269 | 207 | $user_guid = elgg_get_logged_in_user_guid(); |
|
1270 | |||
1271 | // If given an owner, verify it can be loaded |
||
1272 | 207 | View Code Duplication | if ($owner_guid) { |
1273 | 172 | $owner = $this->getOwnerEntity(); |
|
1274 | 172 | if (!$owner) { |
|
1275 | _elgg_services()->logger->error("User $user_guid tried to create a ($type, $subtype), but the given" |
||
1276 | . " owner $owner_guid could not be loaded."); |
||
1277 | return false; |
||
1278 | } |
||
1279 | |||
1280 | // If different owner than logged in, verify can write to container. |
||
1281 | |||
1282 | 172 | if ($user_guid != $owner_guid && !$owner->canWriteToContainer($user_guid, $type, $subtype)) { |
|
1283 | _elgg_services()->logger->error("User $user_guid tried to create a ($type, $subtype) with owner" |
||
1284 | . " $owner_guid, but the user wasn't permitted to write to the owner's container."); |
||
1285 | return false; |
||
1286 | } |
||
1287 | } |
||
1288 | |||
1289 | // If given a container, verify it can be loaded and that the current user can write to it |
||
1290 | 207 | View Code Duplication | if ($container_guid) { |
1291 | 173 | $container = $this->getContainerEntity(); |
|
1292 | 173 | if (!$container) { |
|
1293 | _elgg_services()->logger->error("User $user_guid tried to create a ($type, $subtype), but the given" |
||
1294 | . " container $container_guid could not be loaded."); |
||
1295 | return false; |
||
1296 | } |
||
1297 | |||
1298 | 173 | if (!$container->canWriteToContainer($user_guid, $type, $subtype)) { |
|
1299 | _elgg_services()->logger->error("User $user_guid tried to create a ($type, $subtype), but was not" |
||
1300 | . " permitted to write to container $container_guid."); |
||
1301 | return false; |
||
1302 | } |
||
1303 | } |
||
1304 | |||
1305 | // Create primary table row |
||
1306 | 207 | $guid = _elgg_services()->entityTable->insertRow((object) [ |
|
1307 | 207 | 'type' => $type, |
|
1308 | 207 | 'subtype_id' => $subtype_id, |
|
1309 | 207 | 'owner_guid' => $owner_guid, |
|
1310 | 207 | 'container_guid' => $container_guid, |
|
1311 | 207 | 'access_id' => $access_id, |
|
1312 | 207 | 'time_created' => $time_created, |
|
1313 | 207 | 'time_updated' => $now, |
|
1314 | 207 | 'last_action' => $now, |
|
1315 | 207 | ], $this->attributes); |
|
1316 | |||
1317 | 207 | if (!$guid) { |
|
1318 | throw new \IOException("Unable to save new object's base entity information!"); |
||
1319 | } |
||
1320 | |||
1321 | // We are writing this new entity to cache to make sure subsequent calls |
||
1322 | // to get_entity() load the entity from cache and not from the DB. This |
||
1323 | // MUST come before the metadata and annotation writes below! |
||
1324 | 207 | _elgg_services()->entityCache->set($this); |
|
1325 | |||
1326 | // for BC with 1.8, ->subtype always returns ID, ->getSubtype() the string |
||
1327 | 207 | $this->attributes['subtype'] = (int) $subtype_id; |
|
1328 | 207 | $this->attributes['guid'] = (int) $guid; |
|
1329 | 207 | $this->attributes['time_created'] = (int) $time_created; |
|
1330 | 207 | $this->attributes['time_updated'] = (int) $now; |
|
1331 | 207 | $this->attributes['last_action'] = (int) $now; |
|
1332 | 207 | $this->attributes['container_guid'] = (int) $container_guid; |
|
1333 | |||
1334 | // Save any unsaved metadata |
||
1335 | 207 | if (sizeof($this->temp_metadata) > 0) { |
|
1336 | 163 | foreach ($this->temp_metadata as $name => $value) { |
|
1337 | 163 | if (count($value) == 1) { |
|
1338 | // temp metadata is always an array, but if there is only one value return just the value |
||
1339 | 163 | $this->$name = $value[0]; |
|
1340 | } else { |
||
1341 | 163 | $this->$name = $value; |
|
1342 | } |
||
1343 | } |
||
1344 | |||
1345 | 163 | $this->temp_metadata = []; |
|
1346 | } |
||
1347 | |||
1348 | // Save any unsaved annotations. |
||
1349 | 207 | if (sizeof($this->temp_annotations) > 0) { |
|
1350 | 24 | foreach ($this->temp_annotations as $name => $value) { |
|
1351 | 24 | $this->annotate($name, $value); |
|
1352 | } |
||
1353 | |||
1354 | 24 | $this->temp_annotations = []; |
|
1355 | } |
||
1356 | |||
1357 | // Save any unsaved private settings. |
||
1358 | 207 | if (sizeof($this->temp_private_settings) > 0) { |
|
1359 | 29 | foreach ($this->temp_private_settings as $name => $value) { |
|
1360 | 29 | $this->setPrivateSetting($name, $value); |
|
1361 | } |
||
1362 | |||
1363 | 29 | $this->temp_private_settings = []; |
|
1364 | } |
||
1365 | |||
1366 | 207 | return $guid; |
|
1367 | } |
||
1368 | |||
1369 | /** |
||
1370 | * Update the entity in the database. |
||
1371 | * |
||
1372 | * @return bool Whether the update was successful. |
||
1373 | * |
||
1374 | * @throws InvalidParameterException |
||
1375 | */ |
||
1376 | 69 | protected function update() { |
|
1377 | |||
1378 | 69 | _elgg_services()->boot->invalidateCache(); |
|
1379 | |||
1380 | 69 | if (!$this->canEdit()) { |
|
1381 | 1 | return false; |
|
1382 | } |
||
1383 | |||
1384 | // give old update event a chance to stop the update |
||
1385 | 69 | if (!_elgg_services()->hooks->getEvents()->trigger('update', $this->type, $this)) { |
|
1386 | return false; |
||
1387 | } |
||
1388 | |||
1389 | // See #6225. We copy these after the update event in case a handler changed one of them. |
||
1390 | 69 | $guid = (int) $this->guid; |
|
1391 | 69 | $owner_guid = (int) $this->owner_guid; |
|
1392 | 69 | $access_id = (int) $this->access_id; |
|
1393 | 69 | $container_guid = (int) $this->container_guid; |
|
1394 | 69 | $time_created = (int) $this->time_created; |
|
1395 | 69 | $time = $this->getCurrentTime()->getTimestamp(); |
|
1396 | |||
1397 | 69 | if ($access_id == ACCESS_DEFAULT) { |
|
1398 | throw new \InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.php'); |
||
1399 | } |
||
1400 | |||
1401 | // Update primary table |
||
1402 | 69 | $ret = _elgg_services()->entityTable->updateRow($guid, (object) [ |
|
1403 | 69 | 'owner_guid' => $owner_guid, |
|
1404 | 69 | 'container_guid' => $container_guid, |
|
1405 | 69 | 'access_id' => $access_id, |
|
1406 | 69 | 'time_created' => $time_created, |
|
1407 | 69 | 'time_updated' => $time, |
|
1408 | 69 | 'guid' => $guid, |
|
1409 | ]); |
||
1410 | 69 | if ($ret === false) { |
|
1411 | return false; |
||
1412 | } |
||
1413 | |||
1414 | 69 | $this->attributes['time_updated'] = $time; |
|
1415 | |||
1416 | 69 | elgg_trigger_after_event('update', $this->type, $this); |
|
1417 | |||
1418 | // TODO(evan): Move this to \ElggObject? |
||
1419 | 69 | if ($this instanceof \ElggObject) { |
|
1420 | 9 | update_river_access_by_object($guid, $access_id); |
|
1421 | } |
||
1422 | |||
1423 | 69 | $this->orig_attributes = []; |
|
1424 | |||
1425 | // Handle cases where there was no error BUT no rows were updated! |
||
1426 | 69 | return true; |
|
1427 | } |
||
1428 | |||
1429 | /** |
||
1430 | * Loads attributes from the entities table into the object. |
||
1431 | * |
||
1432 | * @param \stdClass $row Object of properties from database row(s) |
||
1433 | * |
||
1434 | * @return bool |
||
1435 | */ |
||
1436 | 3711 | protected function load(\stdClass $row) { |
|
1437 | 3711 | $type = $this->type; |
|
1438 | |||
1439 | 3711 | $attr_loader = new \Elgg\AttributeLoader(get_class($this), $type, $this->attributes); |
|
1440 | 3711 | if ($type === 'user' || $this instanceof ElggPlugin) { |
|
1441 | 504 | $attr_loader->requires_access_control = false; |
|
1442 | } |
||
1443 | |||
1444 | 3711 | $attrs = $attr_loader->getRequiredAttributes($row); |
|
1445 | 3711 | if (!$attrs) { |
|
1446 | return false; |
||
1447 | } |
||
1448 | |||
1449 | 3711 | $this->attributes = $attrs; |
|
1450 | |||
1451 | 3711 | foreach ($attr_loader->getAdditionalSelectValues() as $name => $value) { |
|
1452 | 18 | $this->setVolatileData("select:$name", $value); |
|
1453 | } |
||
1454 | |||
1455 | 3711 | _elgg_services()->entityCache->set($this); |
|
1456 | |||
1457 | 3711 | return true; |
|
1458 | } |
||
1459 | |||
1460 | /** |
||
1461 | * Load new data from database into existing entity. Overwrites data but |
||
1462 | * does not change values not included in the latest data. |
||
1463 | * |
||
1464 | * @internal This is used when the same entity is selected twice during a |
||
1465 | * request in case different select clauses were used to load different data |
||
1466 | * into volatile data. |
||
1467 | * |
||
1468 | * @param \stdClass $row DB row with new entity data |
||
1469 | * @return bool |
||
1470 | * @access private |
||
1471 | */ |
||
1472 | public function refresh(\stdClass $row) { |
||
1473 | if ($row instanceof \stdClass) { |
||
1474 | return $this->load($row); |
||
1475 | } |
||
1476 | return false; |
||
1477 | } |
||
1478 | |||
1479 | /** |
||
1480 | * Disable this entity. |
||
1481 | * |
||
1482 | * Disabled entities are not returned by getter functions. |
||
1483 | * To enable an entity, use {@link \ElggEntity::enable()}. |
||
1484 | * |
||
1485 | * Recursively disabling an entity will disable all entities |
||
1486 | * owned or contained by the parent entity. |
||
1487 | * |
||
1488 | * You can ignore the disabled field by using {@link access_show_hidden_entities()}. |
||
1489 | * |
||
1490 | * @note Internal: Disabling an entity sets the 'enabled' column to 'no'. |
||
1491 | * |
||
1492 | * @param string $reason Optional reason |
||
1493 | * @param bool $recursive Recursively disable all contained entities? |
||
1494 | * |
||
1495 | * @return bool |
||
1496 | * @see \ElggEntity::enable() |
||
1497 | */ |
||
1498 | 5 | public function disable($reason = "", $recursive = true) { |
|
1499 | 5 | if (!$this->guid) { |
|
1500 | return false; |
||
1501 | } |
||
1502 | |||
1503 | 5 | if (!_elgg_services()->hooks->getEvents()->trigger('disable', $this->type, $this)) { |
|
1504 | return false; |
||
1505 | } |
||
1506 | |||
1507 | 5 | if (!$this->canEdit()) { |
|
1508 | return false; |
||
1509 | } |
||
1510 | |||
1511 | 5 | if ($this instanceof ElggUser && !$this->isBanned()) { |
|
1512 | // temporarily ban to prevent using the site during disable |
||
1513 | 1 | $this->ban(); |
|
1514 | 1 | $unban_after = true; |
|
1515 | } else { |
||
1516 | 4 | $unban_after = false; |
|
1517 | } |
||
1518 | |||
1519 | 5 | if ($reason) { |
|
1520 | $this->disable_reason = $reason; |
||
1521 | } |
||
1522 | |||
1523 | 5 | $dbprefix = _elgg_config()->dbprefix; |
|
1524 | |||
1525 | 5 | $guid = (int) $this->guid; |
|
1526 | |||
1527 | 5 | if ($recursive) { |
|
1528 | // Only disable enabled subentities |
||
1529 | 5 | $hidden = access_get_show_hidden_status(); |
|
1530 | 5 | access_show_hidden_entities(false); |
|
1531 | |||
1532 | 5 | $ia = elgg_set_ignore_access(true); |
|
1533 | |||
1534 | $base_options = [ |
||
1535 | 5 | 'wheres' => [ |
|
1536 | 5 | "e.guid != $guid", |
|
1537 | ], |
||
1538 | 'limit' => false, |
||
1539 | ]; |
||
1540 | |||
1541 | 5 | foreach (['owner_guid', 'container_guid'] as $db_column) { |
|
1542 | 5 | $options = $base_options; |
|
1543 | 5 | $options[$db_column] = $guid; |
|
1544 | |||
1545 | 5 | $subentities = new \ElggBatch('elgg_get_entities', $options); |
|
1546 | 5 | $subentities->setIncrementOffset(false); |
|
1547 | |||
1548 | 5 | foreach ($subentities as $subentity) { |
|
1549 | /* @var $subentity \ElggEntity */ |
||
1550 | 2 | if (!$subentity->isEnabled()) { |
|
1551 | continue; |
||
1552 | } |
||
1553 | 2 | add_entity_relationship($subentity->guid, 'disabled_with', $guid); |
|
1554 | 5 | $subentity->disable($reason); |
|
1555 | } |
||
1556 | } |
||
1557 | |||
1558 | 5 | access_show_hidden_entities($hidden); |
|
1559 | 5 | elgg_set_ignore_access($ia); |
|
1560 | } |
||
1561 | |||
1562 | 5 | $this->disableAnnotations(); |
|
1563 | |||
1564 | 5 | _elgg_services()->entityCache->remove($guid); |
|
1565 | 5 | _elgg_get_memcache('new_entity_cache')->delete($guid); |
|
1566 | |||
1567 | $sql = " |
||
1568 | 5 | UPDATE {$dbprefix}entities |
|
1569 | SET enabled = 'no' |
||
1570 | WHERE guid = :guid |
||
1571 | "; |
||
1572 | $params = [ |
||
1573 | 5 | ':guid' => $guid, |
|
1574 | ]; |
||
1575 | 5 | $disabled = $this->getDatabase()->updateData($sql, false, $params); |
|
1576 | |||
1577 | 5 | if ($unban_after) { |
|
1578 | 1 | $this->unban(); |
|
1579 | } |
||
1580 | |||
1581 | 5 | View Code Duplication | if ($disabled) { |
1582 | 5 | $this->attributes['enabled'] = 'no'; |
|
1583 | 5 | _elgg_services()->hooks->getEvents()->trigger('disable:after', $this->type, $this); |
|
1584 | } |
||
1585 | |||
1586 | 5 | return (bool) $disabled; |
|
1587 | } |
||
1588 | |||
1589 | /** |
||
1590 | * Enable the entity |
||
1591 | * |
||
1592 | * @warning Disabled entities can't be loaded unless |
||
1593 | * {@link access_show_hidden_entities(true)} has been called. |
||
1594 | * |
||
1595 | * @param bool $recursive Recursively enable all entities disabled with the entity? |
||
1596 | * @see access_show_hiden_entities() |
||
1597 | * @return bool |
||
1598 | */ |
||
1599 | 3 | public function enable($recursive = true) { |
|
1600 | 3 | $guid = (int) $this->guid; |
|
1601 | 3 | if (!$guid) { |
|
1602 | return false; |
||
1603 | } |
||
1604 | |||
1605 | 3 | if (!_elgg_services()->hooks->getEvents()->trigger('enable', $this->type, $this)) { |
|
1606 | return false; |
||
1607 | } |
||
1608 | |||
1609 | 3 | if (!$this->canEdit()) { |
|
1610 | return false; |
||
1611 | } |
||
1612 | |||
1613 | // Override access only visible entities |
||
1614 | 3 | $old_access_status = access_get_show_hidden_status(); |
|
1615 | 3 | access_show_hidden_entities(true); |
|
1616 | |||
1617 | 3 | $db = $this->getDatabase(); |
|
1618 | 3 | $result = $db->updateData(" |
|
1619 | 3 | UPDATE {$db->prefix}entities |
|
1620 | SET enabled = 'yes' |
||
1621 | 3 | WHERE guid = $guid |
|
1622 | "); |
||
1623 | |||
1624 | 3 | $this->deleteMetadata('disable_reason'); |
|
1625 | 3 | $this->enableAnnotations(); |
|
1626 | |||
1627 | 3 | if ($recursive) { |
|
1628 | 3 | $disabled_with_it = elgg_get_entities_from_relationship([ |
|
1629 | 3 | 'relationship' => 'disabled_with', |
|
1630 | 3 | 'relationship_guid' => $guid, |
|
1631 | 'inverse_relationship' => true, |
||
1632 | 3 | 'limit' => 0, |
|
1633 | ]); |
||
1634 | |||
1635 | 3 | foreach ($disabled_with_it as $e) { |
|
1636 | 1 | $e->enable(); |
|
1637 | 1 | remove_entity_relationship($e->guid, 'disabled_with', $guid); |
|
1638 | } |
||
1639 | } |
||
1640 | |||
1641 | 3 | access_show_hidden_entities($old_access_status); |
|
1642 | |||
1643 | 3 | View Code Duplication | if ($result) { |
1644 | 3 | $this->attributes['enabled'] = 'yes'; |
|
1645 | 3 | _elgg_services()->hooks->getEvents()->trigger('enable:after', $this->type, $this); |
|
1646 | } |
||
1647 | |||
1648 | 3 | return $result; |
|
1649 | } |
||
1650 | |||
1651 | /** |
||
1652 | * Is this entity enabled? |
||
1653 | * |
||
1654 | * @return boolean Whether this entity is enabled. |
||
1655 | */ |
||
1656 | 2 | public function isEnabled() { |
|
1657 | 2 | return $this->enabled == 'yes'; |
|
1658 | } |
||
1659 | |||
1660 | /** |
||
1661 | * Deletes the entity. |
||
1662 | * |
||
1663 | * Removes the entity and its metadata, annotations, relationships, |
||
1664 | * river entries, and private data. |
||
1665 | * |
||
1666 | * Optionally can remove entities contained and owned by this entity. |
||
1667 | * |
||
1668 | * @warning If deleting recursively, this bypasses ownership of items contained by |
||
1669 | * the entity. That means that if the container_guid = $this->guid, the item will |
||
1670 | * be deleted regardless of who owns it. |
||
1671 | * |
||
1672 | * @param bool $recursive If true (default) then all entities which are |
||
1673 | * owned or contained by $this will also be deleted. |
||
1674 | * |
||
1675 | * @return bool |
||
1676 | */ |
||
1677 | 208 | public function delete($recursive = true) { |
|
1678 | |||
1679 | 208 | $guid = $this->guid; |
|
1680 | 208 | if (!$guid) { |
|
1681 | 1 | return false; |
|
1682 | } |
||
1683 | |||
1684 | // first check if we can delete this entity |
||
1685 | // NOTE: in Elgg <= 1.10.3 this was after the delete event, |
||
1686 | // which could potentially remove some content if the user didn't have access |
||
1687 | 208 | if (!$this->canDelete()) { |
|
1688 | 21 | return false; |
|
1689 | } |
||
1690 | |||
1691 | // now trigger an event to let others know this entity is about to be deleted |
||
1692 | // so they can prevent it or take their own actions |
||
1693 | 192 | if (!_elgg_services()->hooks->getEvents()->trigger('delete', $this->type, $this)) { |
|
1694 | return false; |
||
1695 | } |
||
1696 | |||
1697 | 192 | if ($this instanceof ElggUser) { |
|
1698 | // ban to prevent using the site during delete |
||
1699 | 62 | $this->ban(); |
|
1700 | } |
||
1701 | |||
1702 | // Delete contained owned and otherwise releated objects (depth first) |
||
1703 | 192 | if ($recursive) { |
|
1704 | // Temporarily overriding access controls |
||
1705 | 192 | $entity_disable_override = access_get_show_hidden_status(); |
|
1706 | 192 | access_show_hidden_entities(true); |
|
1707 | 192 | $ia = elgg_set_ignore_access(true); |
|
1708 | |||
1709 | // @todo there was logic in the original code that ignored |
||
1710 | // entities with owner or container guids of themselves. |
||
1711 | // this should probably be prevented in \ElggEntity instead of checked for here |
||
1712 | $base_options = [ |
||
1713 | 192 | 'wheres' => [ |
|
1714 | 192 | "e.guid != $guid", |
|
1715 | ], |
||
1716 | 'limit' => false, |
||
1717 | ]; |
||
1718 | |||
1719 | 192 | foreach (['owner_guid', 'container_guid'] as $db_column) { |
|
1720 | 192 | $options = $base_options; |
|
1721 | 192 | $options[$db_column] = $guid; |
|
1722 | |||
1723 | 192 | $batch = new \ElggBatch('elgg_get_entities', $options); |
|
1724 | 192 | $batch->setIncrementOffset(false); |
|
1725 | |||
1726 | /* @var $e \ElggEntity */ |
||
1727 | 192 | foreach ($batch as $e) { |
|
1728 | 192 | $e->delete(true); |
|
1729 | } |
||
1730 | } |
||
1731 | |||
1732 | 192 | access_show_hidden_entities($entity_disable_override); |
|
1733 | 192 | elgg_set_ignore_access($ia); |
|
1734 | } |
||
1735 | |||
1736 | 192 | $entity_disable_override = access_get_show_hidden_status(); |
|
1737 | 192 | access_show_hidden_entities(true); |
|
1738 | 192 | $ia = elgg_set_ignore_access(true); |
|
1739 | |||
1740 | // Now delete the entity itself |
||
1741 | 192 | $this->deleteMetadata(); |
|
1742 | 192 | $this->deleteAnnotations(); |
|
1743 | 192 | $this->deleteOwnedAnnotations(); |
|
1744 | 192 | $this->deleteRelationships(); |
|
1745 | 192 | $this->deleteAccessCollectionMemberships(); |
|
1746 | 192 | $this->deleteOwnedAccessCollections(); |
|
1747 | |||
1748 | 192 | access_show_hidden_entities($entity_disable_override); |
|
1749 | 192 | elgg_set_ignore_access($ia); |
|
1750 | |||
1751 | 192 | elgg_delete_river(['subject_guid' => $guid, 'limit' => false]); |
|
1752 | 192 | elgg_delete_river(['object_guid' => $guid, 'limit' => false]); |
|
1753 | 192 | elgg_delete_river(['target_guid' => $guid, 'limit' => false]); |
|
1754 | |||
1755 | 192 | remove_all_private_settings($guid); |
|
1756 | |||
1757 | 192 | _elgg_invalidate_cache_for_entity($guid); |
|
1758 | 192 | _elgg_invalidate_memcache_for_entity($guid); |
|
1759 | |||
1760 | 192 | $dbprefix = _elgg_config()->dbprefix; |
|
1761 | |||
1762 | $sql = " |
||
1763 | 192 | DELETE FROM {$dbprefix}entities |
|
1764 | WHERE guid = :guid |
||
1765 | "; |
||
1766 | $params = [ |
||
1767 | 192 | ':guid' => $guid, |
|
1768 | ]; |
||
1769 | |||
1770 | 192 | $deleted = $this->getDatabase()->deleteData($sql, $params); |
|
1771 | |||
1772 | 192 | $this->clearAllFiles(); |
|
1773 | |||
1774 | 192 | return (bool) $deleted; |
|
1775 | } |
||
1776 | |||
1777 | /** |
||
1778 | * Removes all entity files in the dataroot |
||
1779 | * |
||
1780 | * @warning This only deletes the physical files and not their entities. |
||
1781 | * |
||
1782 | * @return bool |
||
1783 | */ |
||
1784 | 192 | protected function clearAllFiles() { |
|
1785 | 192 | $dir = new \Elgg\EntityDirLocator($this->guid); |
|
1786 | 192 | $file_path = _elgg_config()->dataroot . $dir; |
|
1787 | 192 | return delete_directory($file_path); |
|
1788 | } |
||
1789 | |||
1790 | /** |
||
1791 | * {@inheritdoc} |
||
1792 | */ |
||
1793 | 1 | View Code Duplication | public function toObject() { |
1794 | 1 | $object = $this->prepareObject(new \stdClass()); |
|
1795 | 1 | $params = ['entity' => $this]; |
|
1796 | 1 | $object = _elgg_services()->hooks->trigger('to:object', 'entity', $params, $object); |
|
1797 | 1 | return $object; |
|
1798 | } |
||
1799 | |||
1800 | /** |
||
1801 | * Prepare an object copy for toObject() |
||
1802 | * |
||
1803 | * @param \stdClass $object Object representation of the entity |
||
1804 | * @return \stdClass |
||
1805 | */ |
||
1806 | 1 | protected function prepareObject($object) { |
|
1807 | 1 | $object->guid = $this->guid; |
|
1808 | 1 | $object->type = $this->getType(); |
|
1809 | 1 | $object->subtype = $this->getSubtype(); |
|
1810 | 1 | $object->owner_guid = $this->getOwnerGUID(); |
|
1811 | 1 | $object->container_guid = $this->getContainerGUID(); |
|
1812 | 1 | $object->time_created = date('c', $this->getTimeCreated()); |
|
1813 | 1 | $object->time_updated = date('c', $this->getTimeUpdated()); |
|
1814 | 1 | $object->url = $this->getURL(); |
|
1815 | 1 | $object->read_access = (int) $this->access_id; |
|
1816 | 1 | return $object; |
|
1817 | } |
||
1818 | |||
1819 | /* |
||
1820 | * LOCATABLE INTERFACE |
||
1821 | */ |
||
1822 | |||
1823 | /** |
||
1824 | * Gets the 'location' metadata for the entity |
||
1825 | * |
||
1826 | * @return string The location |
||
1827 | */ |
||
1828 | public function getLocation() { |
||
1829 | return $this->location; |
||
1830 | } |
||
1831 | |||
1832 | /** |
||
1833 | * Sets the 'location' metadata for the entity |
||
1834 | * |
||
1835 | * @param string $location String representation of the location |
||
1836 | * |
||
1837 | * @return void |
||
1838 | */ |
||
1839 | public function setLocation($location) { |
||
1840 | $this->location = $location; |
||
1841 | } |
||
1842 | |||
1843 | /** |
||
1844 | * Set latitude and longitude metadata tags for a given entity. |
||
1845 | * |
||
1846 | * @param float $lat Latitude |
||
1847 | * @param float $long Longitude |
||
1848 | * |
||
1849 | * @return void |
||
1850 | * @todo Unimplemented |
||
1851 | */ |
||
1852 | public function setLatLong($lat, $long) { |
||
1853 | $this->{"geo:lat"} = $lat; |
||
1854 | $this->{"geo:long"} = $long; |
||
1855 | } |
||
1856 | |||
1857 | /** |
||
1858 | * Return the entity's latitude. |
||
1859 | * |
||
1860 | * @return float |
||
1861 | * @todo Unimplemented |
||
1862 | */ |
||
1863 | public function getLatitude() { |
||
1864 | return (float) $this->{"geo:lat"}; |
||
1865 | } |
||
1866 | |||
1867 | /** |
||
1868 | * Return the entity's longitude |
||
1869 | * |
||
1870 | * @return float |
||
1871 | * @todo Unimplemented |
||
1872 | */ |
||
1873 | public function getLongitude() { |
||
1874 | return (float) $this->{"geo:long"}; |
||
1875 | } |
||
1876 | |||
1877 | /* |
||
1878 | * SYSTEM LOG INTERFACE |
||
1879 | */ |
||
1880 | |||
1881 | /** |
||
1882 | * Return an identification for the object for storage in the system log. |
||
1883 | * This id must be an integer. |
||
1884 | * |
||
1885 | * @return int |
||
1886 | */ |
||
1887 | 161 | public function getSystemLogID() { |
|
1888 | 161 | return $this->getGUID(); |
|
1889 | } |
||
1890 | |||
1891 | /** |
||
1892 | * For a given ID, return the object associated with it. |
||
1893 | * This is used by the system log. It can be called on any Loggable object. |
||
1894 | * |
||
1895 | * @param int $id GUID. |
||
1896 | * @return int GUID |
||
1897 | */ |
||
1898 | public function getObjectFromID($id) { |
||
1899 | return get_entity($id); |
||
1900 | } |
||
1901 | |||
1902 | /** |
||
1903 | * Returns tags for this entity. |
||
1904 | * |
||
1905 | * @warning Tags must be registered by {@link elgg_register_tag_metadata_name()}. |
||
1906 | * |
||
1907 | * @param array $tag_names Optionally restrict by tag metadata names. |
||
1908 | * |
||
1909 | * @return array |
||
1910 | */ |
||
1911 | public function getTags($tag_names = null) { |
||
1912 | if ($tag_names && !is_array($tag_names)) { |
||
1913 | $tag_names = [$tag_names]; |
||
1914 | } |
||
1915 | |||
1916 | $valid_tags = elgg_get_registered_tag_metadata_names(); |
||
1917 | $entity_tags = []; |
||
1918 | |||
1919 | foreach ($valid_tags as $tag_name) { |
||
1920 | if (is_array($tag_names) && !in_array($tag_name, $tag_names)) { |
||
1921 | continue; |
||
1922 | } |
||
1923 | |||
1924 | if ($tags = $this->$tag_name) { |
||
1925 | // if a single tag, metadata returns a string. |
||
1926 | // if multiple tags, metadata returns an array. |
||
1927 | if (is_array($tags)) { |
||
1928 | $entity_tags = array_merge($entity_tags, $tags); |
||
1929 | } else { |
||
1930 | $entity_tags[] = $tags; |
||
1931 | } |
||
1932 | } |
||
1933 | } |
||
1934 | |||
1935 | return $entity_tags; |
||
1936 | } |
||
1937 | |||
1938 | /** |
||
1939 | * Remove the membership of all access collections for this entity (if the entity is a user) |
||
1940 | * |
||
1941 | * @return bool |
||
1942 | * @since 1.11 |
||
1943 | */ |
||
1944 | 192 | public function deleteAccessCollectionMemberships() { |
|
1945 | |||
1946 | 192 | if (!$this->guid) { |
|
1947 | return false; |
||
1948 | } |
||
1949 | |||
1950 | 192 | if ($this->type !== 'user') { |
|
1951 | 167 | return true; |
|
1952 | } |
||
1953 | |||
1954 | 62 | $ac = _elgg_services()->accessCollections; |
|
1955 | |||
1956 | 62 | $collections = $ac->getCollectionsByMember($this->guid); |
|
1957 | 62 | if (empty($collections)) { |
|
1958 | 62 | return true; |
|
1959 | } |
||
1960 | |||
1961 | 3 | $result = true; |
|
1962 | 3 | foreach ($collections as $collection) { |
|
1963 | 3 | $result = $result & $ac->removeUser($this->guid, $collection->id); |
|
1964 | } |
||
1965 | |||
1966 | 3 | return $result; |
|
1 ignored issue
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
1967 | } |
||
1968 | |||
1969 | /** |
||
1970 | * Remove all access collections owned by this entity |
||
1971 | * |
||
1972 | * @return bool |
||
1973 | * @since 1.11 |
||
1974 | */ |
||
1975 | 192 | public function deleteOwnedAccessCollections() { |
|
1976 | |||
1977 | 192 | if (!$this->guid) { |
|
1978 | return false; |
||
1979 | } |
||
1980 | |||
1981 | 192 | $ac = _elgg_services()->accessCollections; |
|
1982 | |||
1983 | 192 | $collections = $ac->getEntityCollections($this->guid); |
|
1984 | 192 | if (empty($collections)) { |
|
1985 | 192 | return true; |
|
1986 | } |
||
1987 | |||
1988 | 4 | $result = true; |
|
1989 | 4 | foreach ($collections as $collection) { |
|
1990 | 4 | $result = $result & $ac->delete($collection->id); |
|
1991 | } |
||
1992 | |||
1993 | 4 | return $result; |
|
0 ignored issues
–
show
|
|||
1994 | } |
||
1995 | |||
1996 | /** |
||
1997 | * Update the last_action column in the entities table. |
||
1998 | * |
||
1999 | * @warning This is different to time_updated. Time_updated is automatically set, |
||
2000 | * while last_action is only set when explicitly called. |
||
2001 | * |
||
2002 | * @param int $posted Timestamp of last action |
||
2003 | * @return int|false |
||
2004 | * @access private |
||
2005 | */ |
||
2006 | 10 | public function updateLastAction($posted = null) { |
|
2007 | 10 | $posted = _elgg_services()->entityTable->updateLastAction($this, $posted); |
|
2008 | 10 | if ($posted) { |
|
2009 | 10 | $this->attributes['last_action'] = $posted; |
|
2010 | 10 | _elgg_services()->entityCache->set($this); |
|
2011 | 10 | $this->storeInPersistedCache(_elgg_get_memcache('new_entity_cache')); |
|
2012 | } |
||
2013 | 10 | return $posted; |
|
2014 | } |
||
2015 | } |
||
2016 |