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