1 | <?php |
||
2 | |||
3 | use Elgg\EntityIcon; |
||
4 | |||
5 | /** |
||
6 | * The parent class for all Elgg Entities. |
||
7 | * |
||
8 | * An \ElggEntity is one of the basic data models in Elgg. |
||
9 | * It is the primary means of storing and retrieving data from the database. |
||
10 | * An \ElggEntity represents one row of the entities table. |
||
11 | * |
||
12 | * The \ElggEntity class handles CRUD operations for the entities table. |
||
13 | * \ElggEntity should always be extended by another class to handle CRUD |
||
14 | * operations on the type-specific table. |
||
15 | * |
||
16 | * \ElggEntity uses magic methods for get and set, so any property that isn't |
||
17 | * declared will be assumed to be metadata and written to the database |
||
18 | * as metadata on the object. All children classes must declare which |
||
19 | * properties are columns of the type table or they will be assumed |
||
20 | * to be metadata. See \ElggObject::initializeAttributes() for examples. |
||
21 | * |
||
22 | * Core supports 4 types of entities: \ElggObject, \ElggUser, \ElggGroup, and \ElggSite. |
||
23 | * |
||
24 | * @tip Plugin authors will want to extend the \ElggObject class, not this class. |
||
25 | * |
||
26 | * @package Elgg.Core |
||
27 | * @subpackage DataModel.Entities |
||
28 | * |
||
29 | * @property string $type object, user, group, or site (read-only after save) |
||
30 | * @property string $subtype Further clarifies the nature of the entity (this should not be read) |
||
31 | * @property-read int $guid The unique identifier for this entity (read only) |
||
32 | * @property int $owner_guid The GUID of the owner of this entity (usually the creator) |
||
33 | * @property int $container_guid The GUID of the entity containing this entity |
||
34 | * @property int $access_id Specifies the visibility level of this entity |
||
35 | * @property int $time_created A UNIX timestamp of when the entity was created |
||
36 | * @property-read int $time_updated A UNIX timestamp of when the entity was last updated (automatically updated on save) |
||
37 | * @property-read int $last_action A UNIX timestamp of when the entity was last acted upon |
||
38 | * @property string $enabled Is this entity enabled ('yes' or 'no') |
||
39 | * |
||
40 | * Metadata (the above are attributes) |
||
41 | * @property string $location A location of the entity |
||
42 | */ |
||
43 | abstract class ElggEntity extends \ElggData implements |
||
44 | Locatable, // Geocoding interface |
||
45 | EntityIcon // Icon interface |
||
46 | { |
||
47 | |||
48 | public static $primary_attr_names = [ |
||
49 | 'guid', |
||
50 | 'type', |
||
51 | 'subtype', |
||
52 | 'owner_guid', |
||
53 | 'container_guid', |
||
54 | 'access_id', |
||
55 | 'time_created', |
||
56 | 'time_updated', |
||
57 | 'last_action', |
||
58 | 'enabled', |
||
59 | ]; |
||
60 | |||
61 | protected static $integer_attr_names = [ |
||
62 | 'guid', |
||
63 | 'owner_guid', |
||
64 | 'container_guid', |
||
65 | 'access_id', |
||
66 | 'time_created', |
||
67 | 'time_updated', |
||
68 | 'last_action', |
||
69 | ]; |
||
70 | |||
71 | /** |
||
72 | * Holds metadata until entity is saved. Once the entity is saved, |
||
73 | * metadata are written immediately to the database. |
||
74 | * @var array |
||
75 | */ |
||
76 | protected $temp_metadata = []; |
||
77 | |||
78 | /** |
||
79 | * Holds annotations until entity is saved. Once the entity is saved, |
||
80 | * annotations are written immediately to the database. |
||
81 | * @var array |
||
82 | */ |
||
83 | protected $temp_annotations = []; |
||
84 | |||
85 | /** |
||
86 | * Holds private settings until entity is saved. Once the entity is saved, |
||
87 | * private settings are written immediately to the database. |
||
88 | * @var array |
||
89 | */ |
||
90 | protected $temp_private_settings = []; |
||
91 | |||
92 | /** |
||
93 | * Volatile data structure for this object, allows for storage of data |
||
94 | * in-memory that isn't sync'd back to the metadata table. |
||
95 | * @var array |
||
96 | */ |
||
97 | protected $volatile = []; |
||
98 | |||
99 | /** |
||
100 | * Holds the original (persisted) attribute values that have been changed but not yet saved. |
||
101 | * @var array |
||
102 | */ |
||
103 | protected $orig_attributes = []; |
||
104 | |||
105 | /** |
||
106 | * @var bool |
||
107 | */ |
||
108 | protected $_is_cacheable = true; |
||
109 | |||
110 | /** |
||
111 | * Create a new entity. |
||
112 | * |
||
113 | * Plugin developers should only use the constructor to create a new entity. |
||
114 | * To retrieve entities, use get_entity() and the elgg_get_entities* functions. |
||
115 | * |
||
116 | * If no arguments are passed, it creates a new entity. |
||
117 | * If a database result is passed as a \stdClass instance, it instantiates |
||
118 | * that entity. |
||
119 | * |
||
120 | * @param stdClass $row Database row result. Default is null to create a new object. |
||
121 | * |
||
122 | * @throws IOException If cannot load remaining data from db |
||
123 | */ |
||
124 | 5322 | public function __construct(stdClass $row = null) { |
|
125 | 5322 | $this->initializeAttributes(); |
|
126 | |||
127 | 5322 | if ($row && !$this->load($row)) { |
|
128 | $msg = "Failed to load new " . get_class() . " for GUID:" . $row->guid; |
||
129 | throw new \IOException($msg); |
||
130 | } |
||
131 | 5322 | } |
|
132 | |||
133 | /** |
||
134 | * Initialize the attributes array. |
||
135 | * |
||
136 | * This is vital to distinguish between metadata and base parameters. |
||
137 | * |
||
138 | * @return void |
||
139 | */ |
||
140 | 5322 | protected function initializeAttributes() { |
|
141 | 5322 | parent::initializeAttributes(); |
|
142 | |||
143 | 5322 | $this->attributes['guid'] = null; |
|
144 | 5322 | $this->attributes['type'] = null; |
|
145 | 5322 | $this->attributes['subtype'] = null; |
|
146 | |||
147 | 5322 | $this->attributes['owner_guid'] = _elgg_services()->session->getLoggedInUserGuid(); |
|
148 | 5322 | $this->attributes['container_guid'] = _elgg_services()->session->getLoggedInUserGuid(); |
|
149 | |||
150 | 5322 | $this->attributes['access_id'] = ACCESS_PRIVATE; |
|
151 | 5322 | $this->attributes['time_updated'] = null; |
|
152 | 5322 | $this->attributes['last_action'] = null; |
|
153 | 5322 | $this->attributes['enabled'] = "yes"; |
|
154 | |||
155 | 5322 | $this->attributes['type'] = $this->getType(); |
|
156 | 5322 | } |
|
157 | |||
158 | /** |
||
159 | * Clone an entity |
||
160 | * |
||
161 | * Resets the guid so that the entity can be saved as a distinct entity from |
||
162 | * the original. Creation time will be set when this new entity is saved. |
||
163 | * The owner and container guids come from the original entity. The clone |
||
164 | * method copies metadata but does not copy annotations or private settings. |
||
165 | * |
||
166 | * @note metadata will have its owner and access id set when the entity is saved |
||
167 | * and it will be the same as that of the entity. |
||
168 | * |
||
169 | * @return void |
||
170 | */ |
||
171 | 1 | public function __clone() { |
|
172 | 1 | $orig_entity = get_entity($this->guid); |
|
173 | 1 | if (!$orig_entity) { |
|
174 | _elgg_services()->logger->error("Failed to clone entity with GUID $this->guid"); |
||
175 | return; |
||
176 | } |
||
177 | |||
178 | 1 | $metadata_array = elgg_get_metadata([ |
|
179 | 1 | 'guid' => $this->guid, |
|
180 | 1 | 'limit' => 0 |
|
181 | ]); |
||
182 | |||
183 | 1 | $this->attributes['guid'] = null; |
|
184 | 1 | $this->attributes['time_created'] = null; |
|
185 | 1 | $this->attributes['time_updated'] = null; |
|
186 | 1 | $this->attributes['last_action'] = null; |
|
187 | |||
188 | 1 | $this->attributes['subtype'] = $orig_entity->getSubtype(); |
|
189 | |||
190 | // copy metadata over to new entity - slightly convoluted due to |
||
191 | // handling of metadata arrays |
||
192 | 1 | if (is_array($metadata_array)) { |
|
193 | // create list of metadata names |
||
194 | 1 | $metadata_names = []; |
|
195 | 1 | foreach ($metadata_array as $metadata) { |
|
196 | 1 | $metadata_names[] = $metadata['name']; |
|
197 | } |
||
198 | // arrays are stored with multiple enties per name |
||
199 | 1 | $metadata_names = array_unique($metadata_names); |
|
200 | |||
201 | // move the metadata over |
||
202 | 1 | foreach ($metadata_names as $name) { |
|
203 | 1 | $this->__set($name, $orig_entity->$name); |
|
204 | } |
||
205 | } |
||
206 | 1 | } |
|
207 | |||
208 | /** |
||
209 | * Set an attribute or metadata value for this entity |
||
210 | * |
||
211 | * Anything that is not an attribute is saved as metadata. |
||
212 | * |
||
213 | * @warning Metadata set this way will inherit the entity's owner and |
||
214 | * access ID. If you want more control over metadata, use \ElggEntity::setMetadata() |
||
215 | * |
||
216 | * @param string $name Name of the attribute or metadata |
||
217 | * @param mixed $value The value to be set |
||
218 | * @return void |
||
219 | * @see \ElggEntity::setMetadata() |
||
220 | */ |
||
221 | 1092 | public function __set($name, $value) { |
|
222 | 1092 | if ($this->$name === $value) { |
|
223 | // quick return if value is not changing |
||
224 | 289 | return; |
|
225 | } |
||
226 | |||
227 | // Due to https://github.com/Elgg/Elgg/pull/5456#issuecomment-17785173, certain attributes |
||
228 | // will store empty strings as null in the DB. In the somewhat common case that we're re-setting |
||
229 | // the value to empty string, don't consider this a change. |
||
230 | 1092 | if (in_array($name, ['title', 'name', 'description']) |
|
231 | 1092 | && $this->$name === null |
|
232 | 1092 | && $value === "") { |
|
233 | return; |
||
234 | } |
||
235 | |||
236 | 1092 | if (array_key_exists($name, $this->attributes)) { |
|
237 | // if an attribute is 1 (integer) and it's set to "1" (string), don't consider that a change. |
||
238 | 589 | if (is_int($this->attributes[$name]) |
|
239 | 589 | && is_string($value) |
|
240 | 589 | && ((string) $this->attributes[$name] === $value)) { |
|
241 | 1 | return; |
|
242 | } |
||
243 | |||
244 | // keep original values |
||
245 | 589 | if ($this->guid && !array_key_exists($name, $this->orig_attributes)) { |
|
246 | 9 | $this->orig_attributes[$name] = $this->attributes[$name]; |
|
247 | } |
||
248 | |||
249 | // Certain properties should not be manually changed! |
||
250 | 589 | switch ($name) { |
|
251 | case 'guid': |
||
252 | case 'time_updated': |
||
253 | case 'last_action': |
||
254 | return; |
||
255 | break; |
||
256 | case 'access_id': |
||
257 | case 'owner_guid': |
||
258 | case 'container_guid': |
||
259 | 482 | if ($value !== null) { |
|
260 | 482 | $this->attributes[$name] = (int) $value; |
|
261 | } else { |
||
262 | $this->attributes[$name] = null; |
||
263 | } |
||
264 | 482 | break; |
|
265 | default: |
||
266 | 443 | $this->attributes[$name] = $value; |
|
267 | 443 | break; |
|
268 | } |
||
269 | 589 | return; |
|
270 | } |
||
271 | |||
272 | 1062 | $this->setMetadata($name, $value); |
|
273 | 1062 | } |
|
274 | |||
275 | /** |
||
276 | * Get the original values of attribute(s) that have been modified since the entity was persisted. |
||
277 | * |
||
278 | * @return array |
||
279 | */ |
||
280 | 59 | public function getOriginalAttributes() { |
|
281 | 59 | return $this->orig_attributes; |
|
282 | } |
||
283 | |||
284 | /** |
||
285 | * Get an attribute or metadata value |
||
286 | * |
||
287 | * If the name matches an attribute, the attribute is returned. If metadata |
||
288 | * does not exist with that name, a null is returned. |
||
289 | * |
||
290 | * This only returns an array if there are multiple values for a particular |
||
291 | * $name key. |
||
292 | * |
||
293 | * @param string $name Name of the attribute or metadata |
||
294 | * @return mixed |
||
295 | */ |
||
296 | 5387 | public function __get($name) { |
|
297 | 5387 | if (array_key_exists($name, $this->attributes)) { |
|
298 | 5386 | return $this->attributes[$name]; |
|
299 | } |
||
300 | |||
301 | 5363 | return $this->getMetadata($name); |
|
302 | } |
||
303 | |||
304 | /** |
||
305 | * Get the entity's display name |
||
306 | * |
||
307 | * @return string The title or name of this entity. |
||
308 | */ |
||
309 | 19 | public function getDisplayName() { |
|
310 | 19 | return $this->name; |
|
311 | } |
||
312 | |||
313 | /** |
||
314 | * Sets the title or name of this entity. |
||
315 | * |
||
316 | * @param string $display_name The title or name of this entity. |
||
317 | * @return void |
||
318 | */ |
||
319 | public function setDisplayName($display_name) { |
||
320 | $this->name = $display_name; |
||
321 | } |
||
322 | |||
323 | /** |
||
324 | * Return the value of a piece of metadata. |
||
325 | * |
||
326 | * @param string $name Name |
||
327 | * |
||
328 | * @return mixed The value, or null if not found. |
||
329 | */ |
||
330 | 5363 | public function getMetadata($name) { |
|
331 | 5363 | $guid = $this->guid; |
|
332 | |||
333 | 5363 | if (!$guid) { |
|
334 | 572 | if (isset($this->temp_metadata[$name])) { |
|
335 | // md is returned as an array only if more than 1 entry |
||
336 | 285 | if (count($this->temp_metadata[$name]) == 1) { |
|
337 | 285 | return $this->temp_metadata[$name][0]; |
|
338 | } else { |
||
339 | 158 | return $this->temp_metadata[$name]; |
|
340 | } |
||
341 | } else { |
||
342 | 572 | return null; |
|
343 | } |
||
344 | } |
||
345 | |||
346 | // upon first cache miss, just load/cache all the metadata and retry. |
||
347 | // if this works, the rest of this function may not be needed! |
||
348 | 5354 | $cache = _elgg_services()->metadataCache; |
|
349 | 5354 | if ($cache->isLoaded($guid)) { |
|
350 | 2238 | return $cache->getSingle($guid, $name); |
|
351 | } else { |
||
352 | 4136 | $cache->populateFromEntities([$guid]); |
|
353 | // in case ignore_access was on, we have to check again... |
||
354 | 4136 | if ($cache->isLoaded($guid)) { |
|
355 | 1082 | return $cache->getSingle($guid, $name); |
|
356 | } |
||
357 | } |
||
358 | |||
359 | 4083 | $md = elgg_get_metadata([ |
|
360 | 4083 | 'guid' => $guid, |
|
361 | 4083 | 'metadata_name' => $name, |
|
362 | 4083 | 'limit' => 0, |
|
363 | 'distinct' => false, |
||
364 | ]); |
||
365 | |||
366 | 4083 | $value = null; |
|
367 | |||
368 | 4083 | if ($md && !is_array($md)) { |
|
369 | $value = $md->value; |
||
370 | 4083 | } elseif (count($md) == 1) { |
|
371 | $value = $md[0]->value; |
||
372 | 4083 | } else if ($md && is_array($md)) { |
|
373 | $value = metadata_array_to_values($md); |
||
374 | } |
||
375 | |||
376 | 4083 | return $value; |
|
377 | } |
||
378 | |||
379 | /** |
||
380 | * Unset a property from metadata or attribute. |
||
381 | * |
||
382 | * @warning If you use this to unset an attribute, you must save the object! |
||
383 | * |
||
384 | * @param string $name The name of the attribute or metadata. |
||
385 | * |
||
386 | * @return void |
||
387 | * @todo some attributes should be set to null or other default values |
||
388 | */ |
||
389 | 49 | public function __unset($name) { |
|
390 | 49 | if (array_key_exists($name, $this->attributes)) { |
|
391 | $this->attributes[$name] = ""; |
||
392 | } else { |
||
393 | 49 | $this->deleteMetadata($name); |
|
394 | } |
||
395 | 49 | } |
|
396 | |||
397 | /** |
||
398 | * Set metadata on this entity. |
||
399 | * |
||
400 | * Plugin developers usually want to use the magic set method ($entity->name = 'value'). |
||
401 | * Use this method if you want to explicitly set the owner or access of the metadata. |
||
402 | * You cannot set the owner/access before the entity has been saved. |
||
403 | * |
||
404 | * @param string $name Name of the metadata |
||
405 | * @param mixed $value Value of the metadata (doesn't support assoc arrays) |
||
406 | * @param string $value_type 'text', 'integer', or '' for automatic detection |
||
407 | * @param bool $multiple Allow multiple values for a single name. |
||
408 | * Does not support associative arrays. |
||
409 | * |
||
410 | * @return bool |
||
411 | * @throws InvalidArgumentException |
||
412 | */ |
||
413 | 1062 | public function setMetadata($name, $value, $value_type = '', $multiple = false) { |
|
414 | |||
415 | // normalize value to an array that we will loop over |
||
416 | // remove indexes if value already an array. |
||
417 | 1062 | if (is_array($value)) { |
|
418 | 162 | $value = array_values($value); |
|
419 | } else { |
||
420 | 1061 | $value = [$value]; |
|
421 | } |
||
422 | |||
423 | // strip null values from array |
||
424 | 1062 | $value = array_filter($value, function($var) { |
|
425 | 1062 | return !is_null($var); |
|
426 | 1062 | }); |
|
427 | |||
428 | 1062 | if (empty($this->guid)) { |
|
429 | // unsaved entity. store in temp array |
||
430 | 569 | return $this->setTempMetadata($name, $value, $multiple); |
|
431 | } |
||
432 | |||
433 | // saved entity. persist md to db. |
||
434 | 1028 | if (!$multiple) { |
|
435 | 1028 | $current_metadata = $this->getMetadata($name); |
|
436 | |||
437 | 1028 | if ((is_array($current_metadata) || count($value) > 1 || $value === []) && isset($current_metadata)) { |
|
438 | // remove current metadata if needed |
||
439 | // need to remove access restrictions right now to delete |
||
440 | // because this is the expected behavior |
||
441 | 8 | $ia = elgg_set_ignore_access(true); |
|
442 | 8 | $delete_result = elgg_delete_metadata([ |
|
443 | 8 | 'guid' => $this->guid, |
|
444 | 8 | 'metadata_name' => $name, |
|
445 | 'limit' => false, |
||
446 | ]); |
||
447 | 8 | elgg_set_ignore_access($ia); |
|
448 | |||
449 | 8 | if (false === $delete_result) { |
|
450 | return false; |
||
451 | } |
||
452 | } |
||
453 | |||
454 | 1028 | if (count($value) > 1) { |
|
455 | // new value is a multiple valued metadata |
||
456 | 162 | $multiple = true; |
|
457 | } |
||
458 | } |
||
459 | |||
460 | // create new metadata |
||
461 | 1028 | foreach ($value as $value_tmp) { |
|
462 | 1028 | $metadata = new ElggMetadata(); |
|
463 | 1028 | $metadata->entity_guid = $this->guid; |
|
464 | 1028 | $metadata->name = $name; |
|
465 | 1028 | $metadata->value_type = $value_type; |
|
466 | 1028 | $metadata->value = $value_tmp; |
|
467 | 1028 | $md_id = _elgg_services()->metadataTable->create($metadata, $multiple); |
|
468 | 1028 | if (!$md_id) { |
|
469 | 1028 | return false; |
|
470 | } |
||
471 | } |
||
472 | |||
473 | 1028 | return true; |
|
474 | } |
||
475 | |||
476 | /** |
||
477 | * Set temp metadata on this entity. |
||
478 | * |
||
479 | * @param string $name Name of the metadata |
||
480 | * @param mixed $value Value of the metadata (doesn't support assoc arrays) |
||
481 | * @param bool $multiple Allow multiple values for a single name. |
||
482 | * Does not support associative arrays. |
||
483 | * |
||
484 | * @return bool |
||
485 | */ |
||
486 | 569 | protected function setTempMetadata($name, $value, $multiple = false) { |
|
487 | // if overwrite, delete first |
||
488 | 569 | if (!$multiple) { |
|
489 | 569 | unset($this->temp_metadata[$name]); |
|
490 | 569 | if (count($value)) { |
|
491 | // only save if value array contains data |
||
492 | 569 | $this->temp_metadata[$name] = $value; |
|
493 | } |
||
494 | 569 | return true; |
|
495 | } |
||
496 | |||
497 | 3 | if (!isset($this->temp_metadata[$name])) { |
|
498 | $this->temp_metadata[$name] = []; |
||
499 | } |
||
500 | |||
501 | 3 | $this->temp_metadata[$name] = array_merge($this->temp_metadata[$name], $value); |
|
502 | |||
503 | 3 | return true; |
|
504 | } |
||
505 | |||
506 | |||
507 | |||
508 | /** |
||
509 | * Deletes all metadata on this object (metadata.entity_guid = $this->guid). |
||
510 | * If you pass a name, only metadata matching that name will be deleted. |
||
511 | * |
||
512 | * @warning Calling this with no $name will clear all metadata on the entity. |
||
513 | * |
||
514 | * @param null|string $name The name of the metadata to remove. |
||
515 | * @return bool|null |
||
516 | * @since 1.8 |
||
517 | */ |
||
518 | 266 | public function deleteMetadata($name = null) { |
|
519 | |||
520 | 266 | if (!$this->guid) { |
|
521 | // remove from temp_metadata |
||
522 | 2 | if ($name) { |
|
523 | 2 | if (!isset($this->temp_metadata[$name])) { |
|
524 | return null; |
||
525 | } else { |
||
526 | 2 | unset($this->temp_metadata[$name]); |
|
527 | 2 | return true; |
|
528 | } |
||
529 | } else { |
||
530 | $this->temp_metadata = []; |
||
531 | return true; |
||
532 | } |
||
533 | } |
||
534 | |||
535 | $options = [ |
||
536 | 266 | 'guid' => $this->guid, |
|
537 | 266 | 'limit' => 0 |
|
538 | ]; |
||
539 | 266 | if ($name) { |
|
540 | 55 | $options['metadata_name'] = $name; |
|
541 | } |
||
542 | |||
543 | 266 | return elgg_delete_metadata($options); |
|
544 | } |
||
545 | |||
546 | /** |
||
547 | * Get a piece of volatile (non-persisted) data on this entity. |
||
548 | * |
||
549 | * @param string $name The name of the volatile data |
||
550 | * |
||
551 | * @return mixed The value or null if not found. |
||
552 | */ |
||
553 | 25 | public function getVolatileData($name) { |
|
554 | 25 | return array_key_exists($name, $this->volatile) ? $this->volatile[$name] : null; |
|
555 | } |
||
556 | |||
557 | /** |
||
558 | * Set a piece of volatile (non-persisted) data on this entity |
||
559 | * |
||
560 | * @param string $name Name |
||
561 | * @param mixed $value Value |
||
562 | * |
||
563 | * @return void |
||
564 | */ |
||
565 | 26 | public function setVolatileData($name, $value) { |
|
566 | 26 | $this->volatile[$name] = $value; |
|
567 | 26 | } |
|
568 | |||
569 | /** |
||
570 | * Remove all relationships to and from this entity. |
||
571 | * If you pass a relationship name, only relationships matching that name |
||
572 | * will be deleted. |
||
573 | * |
||
574 | * @warning Calling this with no $relationship will clear all relationships |
||
575 | * for this entity. |
||
576 | * |
||
577 | * @param null|string $relationship The name of the relationship to remove. |
||
578 | * @return bool |
||
579 | * @see \ElggEntity::addRelationship() |
||
580 | * @see \ElggEntity::removeRelationship() |
||
581 | */ |
||
582 | 214 | public function deleteRelationships($relationship = null) { |
|
583 | 214 | $relationship = (string) $relationship; |
|
584 | 214 | $result = remove_entity_relationships($this->getGUID(), $relationship); |
|
585 | 214 | return $result && remove_entity_relationships($this->getGUID(), $relationship, true); |
|
586 | } |
||
587 | |||
588 | /** |
||
589 | * Add a relationship between this an another entity. |
||
590 | * |
||
591 | * @tip Read the relationship like "This entity is a $relationship of $guid_two." |
||
592 | * |
||
593 | * @param int $guid_two GUID of the target entity of the relationship. |
||
594 | * @param string $relationship The type of relationship. |
||
595 | * |
||
596 | * @return bool |
||
597 | * @see \ElggEntity::removeRelationship() |
||
598 | * @see \ElggEntity::deleteRelationships() |
||
599 | */ |
||
600 | 5 | public function addRelationship($guid_two, $relationship) { |
|
601 | 5 | return add_entity_relationship($this->getGUID(), $relationship, $guid_two); |
|
602 | } |
||
603 | |||
604 | /** |
||
605 | * Remove a relationship |
||
606 | * |
||
607 | * @param int $guid_two GUID of the target entity of the relationship. |
||
608 | * @param string $relationship The type of relationship. |
||
609 | * |
||
610 | * @return bool |
||
611 | * @see \ElggEntity::addRelationship() |
||
612 | * @see \ElggEntity::deleteRelationships() |
||
613 | */ |
||
614 | 1 | public function removeRelationship($guid_two, $relationship) { |
|
615 | 1 | return remove_entity_relationship($this->getGUID(), $relationship, $guid_two); |
|
616 | } |
||
617 | |||
618 | /** |
||
619 | * Adds a private setting to this entity. |
||
620 | * |
||
621 | * Private settings are similar to metadata but will not |
||
622 | * be searched and there are fewer helper functions for them. |
||
623 | * |
||
624 | * @param string $name Name of private setting |
||
625 | * @param mixed $value Value of private setting |
||
626 | * |
||
627 | * @return bool |
||
628 | */ |
||
629 | 124 | public function setPrivateSetting($name, $value) { |
|
630 | 124 | if ((int) $this->guid > 0) { |
|
631 | 123 | return set_private_setting($this->getGUID(), $name, $value); |
|
632 | } else { |
||
633 | 45 | $this->temp_private_settings[$name] = $value; |
|
634 | 45 | return true; |
|
635 | } |
||
636 | } |
||
637 | |||
638 | /** |
||
639 | * Returns a private setting value |
||
640 | * |
||
641 | * @param string $name Name of the private setting |
||
642 | * |
||
643 | * @return mixed Null if the setting does not exist |
||
644 | */ |
||
645 | 37 | public function getPrivateSetting($name) { |
|
646 | 37 | if ((int) ($this->guid) > 0) { |
|
647 | 37 | return get_private_setting($this->getGUID(), $name); |
|
648 | } else { |
||
649 | 5 | if (isset($this->temp_private_settings[$name])) { |
|
650 | 5 | return $this->temp_private_settings[$name]; |
|
651 | } |
||
652 | } |
||
653 | 5 | return null; |
|
654 | } |
||
655 | |||
656 | /** |
||
657 | * Removes private setting |
||
658 | * |
||
659 | * @param string $name Name of the private setting |
||
660 | * |
||
661 | * @return bool |
||
662 | */ |
||
663 | 3 | public function removePrivateSetting($name) { |
|
664 | 3 | return remove_private_setting($this->getGUID(), $name); |
|
665 | } |
||
666 | |||
667 | /** |
||
668 | * Deletes all annotations on this object (annotations.entity_guid = $this->guid). |
||
669 | * If you pass a name, only annotations matching that name will be deleted. |
||
670 | * |
||
671 | * @warning Calling this with no or empty arguments will clear all annotations on the entity. |
||
672 | * |
||
673 | * @param null|string $name The annotations name to remove. |
||
674 | * @return bool |
||
675 | * @since 1.8 |
||
676 | */ |
||
677 | 214 | public function deleteAnnotations($name = null) { |
|
678 | $options = [ |
||
679 | 214 | 'guid' => $this->guid, |
|
680 | 214 | 'limit' => 0 |
|
681 | ]; |
||
682 | 214 | if ($name) { |
|
683 | 1 | $options['annotation_name'] = $name; |
|
684 | } |
||
685 | |||
686 | 214 | return elgg_delete_annotations($options); |
|
687 | } |
||
688 | |||
689 | /** |
||
690 | * Deletes all annotations owned by this object (annotations.owner_guid = $this->guid). |
||
691 | * If you pass a name, only annotations matching that name will be deleted. |
||
692 | * |
||
693 | * @param null|string $name The name of annotations to delete. |
||
694 | * @return bool |
||
695 | * @since 1.8 |
||
696 | */ |
||
697 | 214 | public function deleteOwnedAnnotations($name = null) { |
|
698 | // access is turned off for this because they might |
||
699 | // no longer have access to an entity they created annotations on. |
||
700 | 214 | $ia = elgg_set_ignore_access(true); |
|
701 | $options = [ |
||
702 | 214 | 'annotation_owner_guid' => $this->guid, |
|
703 | 214 | 'limit' => 0 |
|
704 | ]; |
||
705 | 214 | if ($name) { |
|
706 | $options['annotation_name'] = $name; |
||
707 | } |
||
708 | |||
709 | 214 | $r = elgg_delete_annotations($options); |
|
710 | 214 | elgg_set_ignore_access($ia); |
|
711 | 214 | return $r; |
|
712 | } |
||
713 | |||
714 | /** |
||
715 | * Disables annotations for this entity, optionally based on name. |
||
716 | * |
||
717 | * @param string $name An options name of annotations to disable. |
||
718 | * @return bool |
||
719 | * @since 1.8 |
||
720 | */ |
||
721 | 5 | public function disableAnnotations($name = '') { |
|
722 | $options = [ |
||
723 | 5 | 'guid' => $this->guid, |
|
724 | 5 | 'limit' => 0 |
|
725 | ]; |
||
726 | 5 | if ($name) { |
|
727 | $options['annotation_name'] = $name; |
||
728 | } |
||
729 | |||
730 | 5 | return elgg_disable_annotations($options); |
|
731 | } |
||
732 | |||
733 | /** |
||
734 | * Enables annotations for this entity, optionally based on name. |
||
735 | * |
||
736 | * @warning Before calling this, you must use {@link access_show_hidden_entities()} |
||
737 | * |
||
738 | * @param string $name An options name of annotations to enable. |
||
739 | * @return bool |
||
740 | * @since 1.8 |
||
741 | */ |
||
742 | 3 | public function enableAnnotations($name = '') { |
|
743 | $options = [ |
||
744 | 3 | 'guid' => $this->guid, |
|
745 | 3 | 'limit' => 0 |
|
746 | ]; |
||
747 | 3 | if ($name) { |
|
748 | $options['annotation_name'] = $name; |
||
749 | } |
||
750 | |||
751 | 3 | return elgg_enable_annotations($options); |
|
752 | } |
||
753 | |||
754 | /** |
||
755 | * Helper function to return annotation calculation results |
||
756 | * |
||
757 | * @param string $name The annotation name. |
||
758 | * @param string $calculation A valid MySQL function to run its values through |
||
759 | * @return mixed |
||
760 | */ |
||
761 | 2 | private function getAnnotationCalculation($name, $calculation) { |
|
762 | $options = [ |
||
763 | 2 | 'guid' => $this->getGUID(), |
|
764 | 'distinct' => false, |
||
765 | 2 | 'annotation_name' => $name, |
|
766 | 2 | 'annotation_calculation' => $calculation |
|
767 | ]; |
||
768 | |||
769 | 2 | return elgg_get_annotations($options); |
|
770 | } |
||
771 | |||
772 | /** |
||
773 | * Adds an annotation to an entity. |
||
774 | * |
||
775 | * @warning By default, annotations are private. |
||
776 | * |
||
777 | * @warning Annotating an unsaved entity more than once with the same name |
||
778 | * will only save the last annotation. |
||
779 | * |
||
780 | * @todo Update temp_annotations to store an instance of ElggAnnotation and simply call ElggAnnotation::save(), |
||
781 | * after entity is saved |
||
782 | * |
||
783 | * @param string $name Annotation name |
||
784 | * @param mixed $value Annotation value |
||
785 | * @param int $access_id Access ID |
||
786 | * @param int $owner_guid GUID of the annotation owner |
||
787 | * @param string $value_type The type of annotation value |
||
788 | * |
||
789 | * @return bool|int Returns int if an annotation is saved |
||
790 | */ |
||
791 | 93 | public function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_guid = 0, $value_type = "") { |
|
792 | 93 | if ($this->guid) { |
|
793 | 93 | if (!$owner_guid) { |
|
794 | 92 | $owner_guid = elgg_get_logged_in_user_guid(); |
|
795 | } |
||
796 | 93 | $annotation = new ElggAnnotation(); |
|
797 | 93 | $annotation->entity_guid = $this->guid; |
|
798 | 93 | $annotation->name = $name; |
|
799 | 93 | $annotation->value_type = $value_type; |
|
800 | 93 | $annotation->value = $value; |
|
801 | 93 | $annotation->owner_guid = $owner_guid; |
|
802 | 93 | $annotation->access_id = $access_id; |
|
803 | 93 | return $annotation->save(); |
|
804 | } else { |
||
805 | 17 | $this->temp_annotations[$name] = $value; |
|
806 | } |
||
807 | 17 | return true; |
|
808 | } |
||
809 | |||
810 | /** |
||
811 | * Gets an array of annotations. |
||
812 | * |
||
813 | * To retrieve annotations on an unsaved entity, pass array('name' => [annotation name]) |
||
814 | * as the options array. |
||
815 | * |
||
816 | * @param array $options Array of options for elgg_get_annotations() except guid. |
||
817 | * |
||
818 | * @return array |
||
819 | * @see elgg_get_annotations() |
||
820 | */ |
||
821 | 9 | public function getAnnotations(array $options = []) { |
|
822 | 9 | if ($this->guid) { |
|
823 | 9 | $options['guid'] = $this->guid; |
|
824 | |||
825 | 9 | return elgg_get_annotations($options); |
|
826 | } else { |
||
827 | $name = elgg_extract('annotation_name', $options, ''); |
||
828 | |||
829 | if (isset($this->temp_annotations[$name])) { |
||
830 | return [$this->temp_annotations[$name]]; |
||
831 | } |
||
832 | } |
||
833 | |||
834 | return []; |
||
835 | } |
||
836 | |||
837 | /** |
||
838 | * Count annotations. |
||
839 | * |
||
840 | * @param string $name The type of annotation. |
||
841 | * |
||
842 | * @return int |
||
843 | */ |
||
844 | 2 | public function countAnnotations($name = "") { |
|
845 | 2 | return $this->getAnnotationCalculation($name, 'count'); |
|
846 | } |
||
847 | |||
848 | /** |
||
849 | * Get the average of an integer type annotation. |
||
850 | * |
||
851 | * @param string $name Annotation name |
||
852 | * |
||
853 | * @return int |
||
854 | */ |
||
855 | public function getAnnotationsAvg($name) { |
||
856 | return $this->getAnnotationCalculation($name, 'avg'); |
||
857 | } |
||
858 | |||
859 | /** |
||
860 | * Get the sum of integer type annotations of a given name. |
||
861 | * |
||
862 | * @param string $name Annotation name |
||
863 | * |
||
864 | * @return int |
||
865 | */ |
||
866 | public function getAnnotationsSum($name) { |
||
867 | return $this->getAnnotationCalculation($name, 'sum'); |
||
868 | } |
||
869 | |||
870 | /** |
||
871 | * Get the minimum of integer type annotations of given name. |
||
872 | * |
||
873 | * @param string $name Annotation name |
||
874 | * |
||
875 | * @return int |
||
876 | */ |
||
877 | public function getAnnotationsMin($name) { |
||
878 | return $this->getAnnotationCalculation($name, 'min'); |
||
879 | } |
||
880 | |||
881 | /** |
||
882 | * Get the maximum of integer type annotations of a given name. |
||
883 | * |
||
884 | * @param string $name Annotation name |
||
885 | * |
||
886 | * @return int |
||
887 | */ |
||
888 | public function getAnnotationsMax($name) { |
||
889 | return $this->getAnnotationCalculation($name, 'max'); |
||
890 | } |
||
891 | |||
892 | /** |
||
893 | * Count the number of comments attached to this entity. |
||
894 | * |
||
895 | * @return int Number of comments |
||
896 | * @since 1.8.0 |
||
897 | */ |
||
898 | 2 | public function countComments() { |
|
899 | 2 | $params = ['entity' => $this]; |
|
900 | 2 | $num = _elgg_services()->hooks->trigger('comments:count', $this->getType(), $params); |
|
901 | |||
902 | 2 | if (is_int($num)) { |
|
903 | return $num; |
||
904 | } |
||
905 | |||
906 | 2 | return elgg_get_entities([ |
|
907 | 2 | 'type' => 'object', |
|
908 | 2 | 'subtype' => 'comment', |
|
909 | 2 | 'container_guid' => $this->getGUID(), |
|
910 | 'count' => true, |
||
911 | 'distinct' => false, |
||
912 | ]); |
||
913 | } |
||
914 | |||
915 | /** |
||
916 | * Returns the ACLs owned by the entity |
||
917 | * |
||
918 | * @param array $options additional options to get the access collections with |
||
919 | * |
||
920 | * @return \ElggAccessCollection[] |
||
921 | * |
||
922 | * @see elgg_get_access_collections() |
||
923 | * @since 3.0 |
||
924 | */ |
||
925 | 218 | public function getOwnedAccessCollections($options = []) { |
|
926 | 218 | $options['owner_guid'] = $this->guid; |
|
927 | 218 | return _elgg_services()->accessCollections->getEntityCollections($options); |
|
928 | } |
||
929 | |||
930 | /** |
||
931 | * Returns the first ACL owned by the entity with a given subtype |
||
932 | * |
||
933 | * @param string $subtype subtype of the ACL |
||
934 | * |
||
935 | * @return \ElggAccessCollection|false |
||
936 | * |
||
937 | * @since 3.0 |
||
938 | */ |
||
939 | public function getOwnedAccessCollection($subtype) { |
||
940 | if (!is_string($subtype) || $subtype === '') { |
||
941 | return false; |
||
942 | } |
||
943 | |||
944 | $acls = $this->getOwnedAccessCollections([ |
||
945 | 'subtype' => $subtype, |
||
946 | ]); |
||
947 | |||
948 | return elgg_extract(0, $acls, false); |
||
949 | } |
||
950 | |||
951 | /** |
||
952 | * Gets an array of entities with a relationship to this entity. |
||
953 | * |
||
954 | * @param array $options Options array. See elgg_get_entities_from_relationship() |
||
955 | * for a list of options. 'relationship_guid' is set to |
||
956 | * this entity. |
||
957 | * |
||
958 | * @return array|false An array of entities or false on failure |
||
959 | * @see elgg_get_entities_from_relationship() |
||
960 | */ |
||
961 | public function getEntitiesFromRelationship(array $options = []) { |
||
962 | $options['relationship_guid'] = $this->guid; |
||
963 | return elgg_get_entities($options); |
||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
964 | } |
||
965 | |||
966 | /** |
||
967 | * Gets the number of entities from a specific relationship type |
||
968 | * |
||
969 | * @param string $relationship Relationship type (eg "friends") |
||
970 | * @param bool $inverse_relationship Invert relationship |
||
971 | * |
||
972 | * @return int|false The number of entities or false on failure |
||
973 | */ |
||
974 | public function countEntitiesFromRelationship($relationship, $inverse_relationship = false) { |
||
975 | return elgg_get_entities([ |
||
976 | 'relationship' => $relationship, |
||
977 | 'relationship_guid' => $this->getGUID(), |
||
978 | 'inverse_relationship' => $inverse_relationship, |
||
979 | 'count' => true |
||
980 | ]); |
||
981 | } |
||
982 | |||
983 | /** |
||
984 | * Can a user edit this entity? |
||
985 | * |
||
986 | * @tip Can be overridden by registering for the permissions_check plugin hook. |
||
987 | * |
||
988 | * @param int $user_guid The user GUID, optionally (default: logged in user) |
||
989 | * |
||
990 | * @return bool Whether this entity is editable by the given user. |
||
991 | * @see elgg_set_ignore_access() |
||
992 | */ |
||
993 | 399 | public function canEdit($user_guid = 0) { |
|
994 | 399 | return _elgg_services()->userCapabilities->canEdit($this, $user_guid); |
|
995 | } |
||
996 | |||
997 | /** |
||
998 | * Can a user delete this entity? |
||
999 | * |
||
1000 | * @tip Can be overridden by registering for the permissions_check:delete plugin hook. |
||
1001 | * |
||
1002 | * @param int $user_guid The user GUID, optionally (default: logged in user) |
||
1003 | * |
||
1004 | * @return bool Whether this entity is deletable by the given user. |
||
1005 | * @since 1.11 |
||
1006 | * @see elgg_set_ignore_access() |
||
1007 | */ |
||
1008 | 462 | public function canDelete($user_guid = 0) { |
|
1009 | 462 | return _elgg_services()->userCapabilities->canDelete($this, $user_guid); |
|
1010 | } |
||
1011 | |||
1012 | /** |
||
1013 | * Can a user edit metadata on this entity? |
||
1014 | * |
||
1015 | * If no specific metadata is passed, it returns whether the user can |
||
1016 | * edit any metadata on the entity. |
||
1017 | * |
||
1018 | * @tip Can be overridden by by registering for the permissions_check:metadata |
||
1019 | * plugin hook. |
||
1020 | * |
||
1021 | * @param \ElggMetadata $metadata The piece of metadata to specifically check or null for any metadata |
||
1022 | * @param int $user_guid The user GUID, optionally (default: logged in user) |
||
1023 | * |
||
1024 | * @return bool |
||
1025 | * @see elgg_set_ignore_access() |
||
1026 | */ |
||
1027 | 338 | public function canEditMetadata($metadata = null, $user_guid = 0) { |
|
1028 | 338 | return _elgg_services()->userCapabilities->canEditMetadata($this, $user_guid, $metadata); |
|
1029 | } |
||
1030 | |||
1031 | /** |
||
1032 | * Can a user add an entity to this container |
||
1033 | * |
||
1034 | * @param int $user_guid The GUID of the user creating the entity (0 for logged in user). |
||
1035 | * @param string $type The type of entity we're looking to write |
||
1036 | * @param string $subtype The subtype of the entity we're looking to write |
||
1037 | * |
||
1038 | * @return bool |
||
1039 | * @see elgg_set_ignore_access() |
||
1040 | */ |
||
1041 | 407 | public function canWriteToContainer($user_guid = 0, $type = 'all', $subtype = 'all') { |
|
1042 | 407 | return _elgg_services()->userCapabilities->canWriteToContainer($this, $user_guid, $type, $subtype); |
|
1043 | } |
||
1044 | |||
1045 | /** |
||
1046 | * Can a user comment on an entity? |
||
1047 | * |
||
1048 | * @tip Can be overridden by registering for the permissions_check:comment, |
||
1049 | * <entity type> plugin hook. |
||
1050 | * |
||
1051 | * @param int $user_guid User guid (default is logged in user) |
||
1052 | * @param bool $default Default permission |
||
1053 | * @return bool |
||
1054 | */ |
||
1055 | 4 | public function canComment($user_guid = 0, $default = null) { |
|
1056 | 4 | return _elgg_services()->userCapabilities->canComment($this, $user_guid, $default); |
|
1057 | } |
||
1058 | |||
1059 | /** |
||
1060 | * Can a user annotate an entity? |
||
1061 | * |
||
1062 | * @tip Can be overridden by registering for the plugin hook [permissions_check:annotate:<name>, |
||
1063 | * <entity type>] or [permissions_check:annotate, <entity type>]. The hooks are called in that order. |
||
1064 | * |
||
1065 | * @tip If you want logged out users to annotate an object, do not call |
||
1066 | * canAnnotate(). It's easier than using the plugin hook. |
||
1067 | * |
||
1068 | * @param int $user_guid User guid (default is logged in user) |
||
1069 | * @param string $annotation_name The name of the annotation (default is unspecified) |
||
1070 | * |
||
1071 | * @return bool |
||
1072 | */ |
||
1073 | 8 | public function canAnnotate($user_guid = 0, $annotation_name = '') { |
|
1074 | 8 | return _elgg_services()->userCapabilities->canAnnotate($this, $user_guid, $annotation_name); |
|
1075 | } |
||
1076 | |||
1077 | /** |
||
1078 | * Returns the access_id. |
||
1079 | * |
||
1080 | * @return int The access ID |
||
1081 | */ |
||
1082 | public function getAccessID() { |
||
1083 | return $this->access_id; |
||
1084 | } |
||
1085 | |||
1086 | /** |
||
1087 | * Returns the guid. |
||
1088 | * |
||
1089 | * @return int|null GUID |
||
1090 | */ |
||
1091 | 548 | public function getGUID() { |
|
1092 | 548 | return $this->guid; |
|
1093 | } |
||
1094 | |||
1095 | /** |
||
1096 | * Returns the entity type |
||
1097 | * |
||
1098 | * @return string The entity type |
||
1099 | */ |
||
1100 | 1 | public function getType() { |
|
1101 | // this is just for the PHPUnit mocking framework |
||
1102 | 1 | return $this->type; |
|
1103 | } |
||
1104 | |||
1105 | /** |
||
1106 | * Get the entity subtype |
||
1107 | * |
||
1108 | * @return string The entity subtype |
||
1109 | */ |
||
1110 | 566 | public function getSubtype() { |
|
1111 | 566 | return $this->attributes['subtype']; |
|
1112 | } |
||
1113 | |||
1114 | /** |
||
1115 | * Get the guid of the entity's owner. |
||
1116 | * |
||
1117 | * @return int The owner GUID |
||
1118 | */ |
||
1119 | 95 | public function getOwnerGUID() { |
|
1120 | 95 | return (int) $this->owner_guid; |
|
1121 | } |
||
1122 | |||
1123 | /** |
||
1124 | * Gets the \ElggEntity that owns this entity. |
||
1125 | * |
||
1126 | * @return \ElggEntity The owning entity |
||
1127 | */ |
||
1128 | 414 | public function getOwnerEntity() { |
|
1129 | 414 | return get_entity($this->owner_guid); |
|
1130 | } |
||
1131 | |||
1132 | /** |
||
1133 | * Set the container for this object. |
||
1134 | * |
||
1135 | * @param int $container_guid The ID of the container. |
||
1136 | * |
||
1137 | * @return bool |
||
1138 | */ |
||
1139 | 1 | public function setContainerGUID($container_guid) { |
|
1140 | 1 | return $this->container_guid = (int) $container_guid; |
|
0 ignored issues
–
show
|
|||
1141 | } |
||
1142 | |||
1143 | /** |
||
1144 | * Gets the container GUID for this entity. |
||
1145 | * |
||
1146 | * @return int |
||
1147 | */ |
||
1148 | 425 | public function getContainerGUID() { |
|
1149 | 425 | return (int) $this->container_guid; |
|
1150 | } |
||
1151 | |||
1152 | /** |
||
1153 | * Get the container entity for this object. |
||
1154 | * |
||
1155 | * @return \ElggEntity |
||
1156 | * @since 1.8.0 |
||
1157 |