Checks whether a method/function call has too many arguments.
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)) { |
|
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); |
|
0 ignored issues
–
show
|
|||
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) { |
|
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 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.