PageRenderTime 64ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/elgg/engine/lib/entities.php

https://bitbucket.org/rhizomatik/lorea_production/
PHP | 3760 lines | 2100 code | 556 blank | 1104 comment | 461 complexity | f5ac119ced48d6b116fc94a607e7b78d MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * Elgg entities.
  4. * Functions to manage all elgg entities (sites, groups, objects and users).
  5. *
  6. * @package Elgg
  7. * @subpackage Core
  8. */
  9. /// Cache objects in order to minimise database access.
  10. global $ENTITY_CACHE;
  11. $ENTITY_CACHE = NULL;
  12. /// Cache subtype searches
  13. global $SUBTYPE_CACHE;
  14. $SUBTYPE_CACHE = NULL;
  15. /// Require the locatable interface
  16. // @todo Move this into start.php?
  17. require_once('location.php');
  18. /**
  19. * ElggEntity The elgg entity superclass
  20. * This class holds methods for accessing the main entities table.
  21. *
  22. * @package Elgg
  23. * @subpackage Core
  24. */
  25. abstract class ElggEntity implements
  26. Notable, // Calendar interface
  27. Locatable, // Geocoding interface
  28. Exportable, // Allow export of data
  29. Importable, // Allow import of data
  30. Loggable, // Can events related to this object class be logged
  31. Iterator, // Override foreach behaviour
  32. ArrayAccess // Override for array access
  33. {
  34. /**
  35. * The main attributes of an entity.
  36. * Blank entries for all database fields should be created by the constructor.
  37. * Subclasses should add to this in their constructors.
  38. * Any field not appearing in this will be viewed as a
  39. */
  40. protected $attributes;
  41. /**
  42. * If set, overrides the value of getURL()
  43. */
  44. protected $url_override;
  45. /**
  46. * Icon override, overrides the value of getIcon().
  47. */
  48. protected $icon_override;
  49. /**
  50. * Temporary cache for metadata, permitting meta data access before a guid has obtained.
  51. */
  52. protected $temp_metadata;
  53. /**
  54. * Temporary cache for annotations, permitting meta data access before a guid has obtained.
  55. */
  56. protected $temp_annotations;
  57. /**
  58. * Volatile data structure for this object, allows for storage of data
  59. * in-memory that isn't sync'd back to the metadata table.
  60. */
  61. protected $volatile;
  62. /**
  63. * Initialise the attributes array.
  64. * This is vital to distinguish between metadata and base parameters.
  65. *
  66. * Place your base parameters here.
  67. *
  68. * @return void
  69. */
  70. protected function initialise_attributes() {
  71. initialise_entity_cache();
  72. // Create attributes array if not already created
  73. if (!is_array($this->attributes)) {
  74. $this->attributes = array();
  75. }
  76. if (!is_array($this->temp_metadata)) {
  77. $this->temp_metadata = array();
  78. }
  79. if (!is_array($this->temp_annotations)) {
  80. $this->temp_annotations = array();
  81. }
  82. if (!is_array($this->volatile)) {
  83. $this->volatile = array();
  84. }
  85. $this->attributes['guid'] = "";
  86. $this->attributes['type'] = "";
  87. $this->attributes['subtype'] = "";
  88. $this->attributes['owner_guid'] = get_loggedin_userid();
  89. $this->attributes['container_guid'] = get_loggedin_userid();
  90. $this->attributes['site_guid'] = 0;
  91. $this->attributes['access_id'] = ACCESS_PRIVATE;
  92. $this->attributes['time_created'] = "";
  93. $this->attributes['time_updated'] = "";
  94. $this->attributes['last_action'] = '';
  95. $this->attributes['enabled'] = "yes";
  96. // There now follows a bit of a hack
  97. /* Problem: To speed things up, some objects are split over several tables, this means that it requires
  98. * n number of database reads to fully populate an entity. This causes problems for caching and create events
  99. * since it is not possible to tell whether a subclassed entity is complete.
  100. * Solution: We have two counters, one 'tables_split' which tells whatever is interested how many tables
  101. * are going to need to be searched in order to fully populate this object, and 'tables_loaded' which is how
  102. * many have been loaded thus far.
  103. * If the two are the same then this object is complete.
  104. *
  105. * Use: isFullyLoaded() to check
  106. */
  107. $this->attributes['tables_split'] = 1;
  108. $this->attributes['tables_loaded'] = 0;
  109. }
  110. /**
  111. * Clone an entity
  112. *
  113. * Resets the guid so that the entity can be saved as a distinct entity from
  114. * the original. Creation time will be set when this new entity is saved.
  115. * The owner and container guids come from the original entity. The clone
  116. * method copies metadata but does not copy over annotations, or private settings.
  117. *
  118. * Note: metadata will have its owner and access id set when the entity is saved
  119. * and it will be the same as that of the entity.
  120. */
  121. public function __clone() {
  122. $orig_entity = get_entity($this->guid);
  123. if (!$orig_entity) {
  124. elgg_log("Failed to clone entity with GUID $this->guid", "ERROR");
  125. return;
  126. }
  127. $metadata_array = get_metadata_for_entity($this->guid);
  128. $this->attributes['guid'] = "";
  129. $this->attributes['subtype'] = $orig_entity->getSubtype();
  130. // copy metadata over to new entity - slightly convoluted due to
  131. // handling of metadata arrays
  132. if (is_array($metadata_array)) {
  133. // create list of metadata names
  134. $metadata_names = array();
  135. foreach ($metadata_array as $metadata) {
  136. $metadata_names[] = $metadata['name'];
  137. }
  138. // arrays are stored with multiple enties per name
  139. $metadata_names = array_unique($metadata_names);
  140. // move the metadata over
  141. foreach ($metadata_names as $name) {
  142. $this->set($name, $orig_entity->$name);
  143. }
  144. }
  145. }
  146. /**
  147. * Return the value of a given key.
  148. * If $name is a key field (as defined in $this->attributes) that value is returned, otherwise it will
  149. * then look to see if the value is in this object's metadata.
  150. *
  151. * Q: Why are we not using __get overload here?
  152. * A: Because overload operators cause problems during subclassing, so we put the code here and
  153. * create overloads in subclasses.
  154. *
  155. * subtype is returned as an id rather than the subtype string. Use getSubtype()
  156. * to get the subtype string.
  157. *
  158. * @param string $name
  159. * @return mixed Returns the value of a given value, or null.
  160. */
  161. public function get($name) {
  162. // See if its in our base attribute
  163. if (isset($this->attributes[$name])) {
  164. return $this->attributes[$name];
  165. }
  166. // No, so see if its in the meta data for this entity
  167. $meta = $this->getMetaData($name);
  168. // getMetaData returns NULL if $name is not found
  169. return $meta;
  170. }
  171. /**
  172. * Set the value of a given key, replacing it if necessary.
  173. * If $name is a base attribute (as defined in $this->attributes) that value is set, otherwise it will
  174. * set the appropriate item of metadata.
  175. *
  176. * Note: It is important that your class populates $this->attributes with keys for all base attributes, anything
  177. * not in their gets set as METADATA.
  178. *
  179. * Q: Why are we not using __set overload here?
  180. * A: Because overload operators cause problems during subclassing, so we put the code here and
  181. * create overloads in subclasses.
  182. *
  183. * @param string $name
  184. * @param mixed $value
  185. */
  186. public function set($name, $value) {
  187. if (array_key_exists($name, $this->attributes)) {
  188. // Certain properties should not be manually changed!
  189. switch ($name) {
  190. case 'guid':
  191. case 'time_created':
  192. case 'time_updated':
  193. case 'last_action':
  194. return FALSE;
  195. break;
  196. default:
  197. $this->attributes[$name] = $value;
  198. break;
  199. }
  200. } else {
  201. return $this->setMetaData($name, $value);
  202. }
  203. return TRUE;
  204. }
  205. /**
  206. * Get a given piece of metadata.
  207. *
  208. * @param string $name
  209. */
  210. public function getMetaData($name) {
  211. if ((int) ($this->guid) > 0) {
  212. $md = get_metadata_byname($this->getGUID(), $name);
  213. } else {
  214. if (isset($this->temp_metadata[$name])) {
  215. return $this->temp_metadata[$name];
  216. }
  217. }
  218. if ($md && !is_array($md)) {
  219. return $md->value;
  220. } else if ($md && is_array($md)) {
  221. return metadata_array_to_values($md);
  222. }
  223. return null;
  224. }
  225. /**
  226. * Class member get overloading
  227. *
  228. * @param string $name
  229. * @return mixed
  230. */
  231. function __get($name) {
  232. return $this->get($name);
  233. }
  234. /**
  235. * Class member set overloading
  236. *
  237. * @param string $name
  238. * @param mixed $value
  239. * @return mixed
  240. */
  241. function __set($name, $value) {
  242. return $this->set($name, $value);
  243. }
  244. /**
  245. * Supporting isset.
  246. *
  247. * @param string $name The name of the attribute or metadata.
  248. * @return bool
  249. */
  250. function __isset($name) {
  251. return $this->$name !== NULL;
  252. }
  253. /**
  254. * Supporting unsetting of magic attributes.
  255. *
  256. * @param string $name The name of the attribute or metadata.
  257. */
  258. function __unset($name) {
  259. if (array_key_exists($name, $this->attributes)) {
  260. $this->attributes[$name] = "";
  261. }
  262. else {
  263. $this->clearMetaData($name);
  264. }
  265. }
  266. /**
  267. * Set a piece of metadata.
  268. *
  269. * @param string $name Name of the metadata
  270. * @param mixed $value Value of the metadata
  271. * @param string $value_type Types supported: integer and string. Will auto-identify if not set
  272. * @param bool $multiple (does not support associative arrays)
  273. * @return bool
  274. */
  275. public function setMetaData($name, $value, $value_type = "", $multiple = false) {
  276. if (is_array($value)) {
  277. unset($this->temp_metadata[$name]);
  278. remove_metadata($this->getGUID(), $name);
  279. foreach ($value as $v) {
  280. if ((int) $this->guid > 0) {
  281. $multiple = true;
  282. if (!create_metadata($this->getGUID(), $name, $v, $value_type,
  283. $this->getOwner(), $this->getAccessID(), $multiple)) {
  284. return false;
  285. }
  286. } else {
  287. if (($multiple) && (isset($this->temp_metadata[$name]))) {
  288. if (!is_array($this->temp_metadata[$name])) {
  289. $tmp = $this->temp_metadata[$name];
  290. $this->temp_metadata[$name] = array();
  291. $this->temp_metadata[$name][] = $tmp;
  292. }
  293. $this->temp_metadata[$name][] = $value;
  294. }
  295. else {
  296. $this->temp_metadata[$name] = $value;
  297. }
  298. }
  299. }
  300. return true;
  301. } else {
  302. unset($this->temp_metadata[$name]);
  303. if ((int) $this->guid > 0) {
  304. $result = create_metadata($this->getGUID(), $name, $value, $value_type, $this->getOwner(), $this->getAccessID(), $multiple);
  305. return (bool)$result;
  306. } else {
  307. if (($multiple) && (isset($this->temp_metadata[$name]))) {
  308. if (!is_array($this->temp_metadata[$name])) {
  309. $tmp = $this->temp_metadata[$name];
  310. $this->temp_metadata[$name] = array();
  311. $this->temp_metadata[$name][] = $tmp;
  312. }
  313. $this->temp_metadata[$name][] = $value;
  314. }
  315. else {
  316. $this->temp_metadata[$name] = $value;
  317. }
  318. return true;
  319. }
  320. }
  321. }
  322. /**
  323. * Clear metadata.
  324. */
  325. public function clearMetaData($name = "") {
  326. if (empty($name)) {
  327. return clear_metadata($this->getGUID());
  328. } else {
  329. return remove_metadata($this->getGUID(),$name);
  330. }
  331. }
  332. /**
  333. * Get a piece of volatile (non-persisted) data on this entity
  334. */
  335. public function getVolatileData($name) {
  336. if (!is_array($this->volatile)) {
  337. $this->volatile = array();
  338. }
  339. if (array_key_exists($name, $this->volatile)) {
  340. return $this->volatile[$name];
  341. } else {
  342. return NULL;
  343. }
  344. }
  345. /**
  346. * Set a piece of volatile (non-persisted) data on this entity
  347. */
  348. public function setVolatileData($name, $value) {
  349. if (!is_array($this->volatile)) {
  350. $this->volatile = array();
  351. }
  352. $this->volatile[$name] = $value;
  353. }
  354. /**
  355. * Remove all entities associated with this entity
  356. *
  357. * @return true
  358. */
  359. public function clearRelationships() {
  360. remove_entity_relationships($this->getGUID());
  361. remove_entity_relationships($this->getGUID(),"",true);
  362. return true;
  363. }
  364. /**
  365. * Add a relationship.
  366. *
  367. * @param int $guid Relationship to link to.
  368. * @param string $relationship The type of relationship.
  369. * @return bool
  370. */
  371. public function addRelationship($guid, $relationship) {
  372. return add_entity_relationship($this->getGUID(), $relationship, $guid);
  373. }
  374. /**
  375. * Remove a relationship
  376. *
  377. * @param int $guid
  378. * @param str $relationship
  379. * @return bool
  380. */
  381. public function removeRelationship($guid, $relationship) {
  382. return remove_entity_relationship($this->getGUID(), $relationship, $guid);
  383. }
  384. /**
  385. * Adds a private setting to this entity.
  386. *
  387. * @param $name
  388. * @param $value
  389. * @return unknown_type
  390. */
  391. function setPrivateSetting($name, $value) {
  392. return set_private_setting($this->getGUID(), $name, $value);
  393. }
  394. /**
  395. * Gets private setting for this entity
  396. *
  397. * @param $name
  398. * @return unknown_type
  399. */
  400. function getPrivateSetting($name) {
  401. return get_private_setting($this->getGUID(), $name);
  402. }
  403. /**
  404. * Removes private setting for this entity.
  405. *
  406. * @param $name
  407. * @return unknown_type
  408. */
  409. function removePrivateSetting($name) {
  410. return remove_private_setting($this->getGUID(), $name);
  411. }
  412. /**
  413. * Adds an annotation to an entity. By default, the type is detected automatically; however,
  414. * it can also be set. Note that by default, annotations are private.
  415. *
  416. * @param string $name
  417. * @param mixed $value
  418. * @param int $access_id
  419. * @param int $owner_id
  420. * @param string $vartype
  421. */
  422. function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_id = 0, $vartype = "") {
  423. if ((int) $this->guid > 0) {
  424. return create_annotation($this->getGUID(), $name, $value, $vartype, $owner_id, $access_id);
  425. } else {
  426. $this->temp_annotations[$name] = $value;
  427. }
  428. return true;
  429. }
  430. /**
  431. * Get the annotations for an entity.
  432. *
  433. * @param string $name
  434. * @param int $limit
  435. * @param int $offset
  436. * @param string $order
  437. */
  438. function getAnnotations($name, $limit = 50, $offset = 0, $order="asc") {
  439. if ((int) ($this->guid) > 0) {
  440. return get_annotations($this->getGUID(), "", "", $name, "", 0, $limit, $offset, $order);
  441. } else {
  442. return $this->temp_annotations[$name];
  443. }
  444. }
  445. /**
  446. * Remove all annotations or all annotations for this entity.
  447. *
  448. * @param string $name
  449. */
  450. function clearAnnotations($name = "") {
  451. return clear_annotations($this->getGUID(), $name);
  452. }
  453. /**
  454. * Return the annotations for the entity.
  455. *
  456. * @param string $name The type of annotation.
  457. */
  458. function countAnnotations($name = "") {
  459. return count_annotations($this->getGUID(), "", "", $name);
  460. }
  461. /**
  462. * Get the average of an integer type annotation.
  463. *
  464. * @param string $name
  465. */
  466. function getAnnotationsAvg($name) {
  467. return get_annotations_avg($this->getGUID(), "", "", $name);
  468. }
  469. /**
  470. * Get the sum of integer type annotations of a given name.
  471. *
  472. * @param string $name
  473. */
  474. function getAnnotationsSum($name) {
  475. return get_annotations_sum($this->getGUID(), "", "", $name);
  476. }
  477. /**
  478. * Get the minimum of integer type annotations of given name.
  479. *
  480. * @param string $name
  481. */
  482. function getAnnotationsMin($name) {
  483. return get_annotations_min($this->getGUID(), "", "", $name);
  484. }
  485. /**
  486. * Get the maximum of integer type annotations of a given name.
  487. *
  488. * @param string $name
  489. */
  490. function getAnnotationsMax($name) {
  491. return get_annotations_max($this->getGUID(), "", "", $name);
  492. }
  493. /**
  494. * Gets an array of entities from a specific relationship type
  495. *
  496. * @param string $relationship Relationship type (eg "friends")
  497. * @param true|false $inverse Is this an inverse relationship?
  498. * @param int $limit Number of elements to return
  499. * @param int $offset Indexing offset
  500. * @return array|false An array of entities or false on failure
  501. */
  502. function getEntitiesFromRelationship($relationship, $inverse = false, $limit = 50, $offset = 0) {
  503. return elgg_get_entities_from_relationship(array(
  504. 'relationship' => $relationship,
  505. 'relationship_guid' => $this->getGUID(),
  506. 'inverse_relationship' => $inverse,
  507. 'limit' => $limit,
  508. 'offset' => $offset
  509. ));
  510. }
  511. /**
  512. * Gets the number of of entities from a specific relationship type
  513. *
  514. * @param string $relationship Relationship type (eg "friends")
  515. * @param bool $inverse_relationship
  516. * @return int|false The number of entities or false on failure
  517. */
  518. function countEntitiesFromRelationship($relationship, $inverse_relationship = FALSE) {
  519. return elgg_get_entities_from_relationship(array(
  520. 'relationship' => $relationship,
  521. 'relationship_guid' => $this->getGUID(),
  522. 'inverse_relationship' => $inverse_relationship,
  523. 'count' => TRUE
  524. ));
  525. }
  526. /**
  527. * Determines whether or not the specified user (by default the current one) can edit the entity
  528. *
  529. * @param int $user_guid The user GUID, optionally (defaults to the currently logged in user)
  530. * @return true|false
  531. */
  532. function canEdit($user_guid = 0) {
  533. return can_edit_entity($this->getGUID(), $user_guid);
  534. }
  535. /**
  536. * Determines whether or not the specified user (by default the current one) can edit metadata on the entity
  537. *
  538. * @param ElggMetadata $metadata The piece of metadata to specifically check
  539. * @param int $user_guid The user GUID, optionally (defaults to the currently logged in user)
  540. * @return true|false
  541. */
  542. function canEditMetadata($metadata = null, $user_guid = 0) {
  543. return can_edit_entity_metadata($this->getGUID(), $user_guid, $metadata);
  544. }
  545. /**
  546. * Returns whether the given user (or current user) has the ability to write to this container.
  547. *
  548. * @param int $user_guid The user.
  549. * @return bool
  550. */
  551. public function canWriteToContainer($user_guid = 0) {
  552. return can_write_to_container($user_guid, $this->getGUID());
  553. }
  554. /**
  555. * Obtain this entity's access ID
  556. *
  557. * @return int The access ID
  558. */
  559. public function getAccessID() {
  560. return $this->get('access_id');
  561. }
  562. /**
  563. * Obtain this entity's GUID
  564. *
  565. * @return int GUID
  566. */
  567. public function getGUID() {
  568. return $this->get('guid');
  569. }
  570. /**
  571. * Get the owner of this entity
  572. *
  573. * @return int The owner GUID
  574. */
  575. public function getOwner() {
  576. return $this->get('owner_guid');
  577. }
  578. /**
  579. * Returns the actual entity of the user who owns this entity, if any
  580. *
  581. * @return ElggEntity The owning user
  582. */
  583. public function getOwnerEntity() {
  584. return get_entity($this->get('owner_guid'));
  585. }
  586. /**
  587. * Gets the type of entity this is
  588. *
  589. * @return string Entity type
  590. */
  591. public function getType() {
  592. return $this->get('type');
  593. }
  594. /**
  595. * Returns the subtype of this entity
  596. *
  597. * @return string The entity subtype
  598. */
  599. public function getSubtype() {
  600. // If this object hasn't been saved, then return the subtype string.
  601. if (!((int) $this->guid > 0)) {
  602. return $this->get('subtype');
  603. }
  604. return get_subtype_from_id($this->get('subtype'));
  605. }
  606. /**
  607. * Gets the UNIX epoch time that this entity was created
  608. *
  609. * @return int UNIX epoch time
  610. */
  611. public function getTimeCreated() {
  612. return $this->get('time_created');
  613. }
  614. /**
  615. * Gets the UNIX epoch time that this entity was last updated
  616. *
  617. * @return int UNIX epoch time
  618. */
  619. public function getTimeUpdated() {
  620. return $this->get('time_updated');
  621. }
  622. /**
  623. * Gets the display URL for this entity
  624. *
  625. * @return string The URL
  626. */
  627. public function getURL() {
  628. if (!empty($this->url_override)) {
  629. return $this->url_override;
  630. }
  631. return get_entity_url($this->getGUID());
  632. }
  633. /**
  634. * Overrides the URL returned by getURL
  635. *
  636. * @param string $url The new item URL
  637. * @return string The URL
  638. */
  639. public function setURL($url) {
  640. $this->url_override = $url;
  641. return $url;
  642. }
  643. /**
  644. * Return a url for the entity's icon, trying multiple alternatives.
  645. *
  646. * @param string $size Either 'large','medium','small' or 'tiny'
  647. * @return string The url or false if no url could be worked out.
  648. */
  649. public function getIcon($size = 'medium') {
  650. if (isset($this->icon_override[$size])) {
  651. return $this->icon_override[$size];
  652. }
  653. return get_entity_icon_url($this, $size);
  654. }
  655. /**
  656. * Set an icon override for an icon and size.
  657. *
  658. * @param string $url The url of the icon.
  659. * @param string $size The size its for.
  660. * @return bool
  661. */
  662. public function setIcon($url, $size = 'medium') {
  663. $url = sanitise_string($url);
  664. $size = sanitise_string($size);
  665. if (!$this->icon_override) {
  666. $this->icon_override = array();
  667. }
  668. $this->icon_override[$size] = $url;
  669. return true;
  670. }
  671. /**
  672. * Tests to see whether the object has been fully loaded.
  673. *
  674. * @return bool
  675. */
  676. public function isFullyLoaded() {
  677. return ! ($this->attributes['tables_loaded'] < $this->attributes['tables_split']);
  678. }
  679. /**
  680. * Save generic attributes to the entities table.
  681. */
  682. public function save() {
  683. $guid = (int) $this->guid;
  684. if ($guid > 0) {
  685. cache_entity($this);
  686. return update_entity(
  687. $this->get('guid'),
  688. $this->get('owner_guid'),
  689. $this->get('access_id'),
  690. $this->get('container_guid')
  691. );
  692. } else {
  693. // Create a new entity (nb: using attribute array directly 'cos set function does something special!)
  694. $this->attributes['guid'] = create_entity($this->attributes['type'], $this->attributes['subtype'], $this->attributes['owner_guid'], $this->attributes['access_id'], $this->attributes['site_guid'], $this->attributes['container_guid']);
  695. if (!$this->attributes['guid']) {
  696. throw new IOException(elgg_echo('IOException:BaseEntitySaveFailed'));
  697. }
  698. // Save any unsaved metadata
  699. // @todo How to capture extra information (access id etc)
  700. if (sizeof($this->temp_metadata) > 0) {
  701. foreach($this->temp_metadata as $name => $value) {
  702. $this->$name = $value;
  703. unset($this->temp_metadata[$name]);
  704. }
  705. }
  706. // Save any unsaved annotations metadata.
  707. // @todo How to capture extra information (access id etc)
  708. if (sizeof($this->temp_annotations) > 0) {
  709. foreach($this->temp_annotations as $name => $value) {
  710. $this->annotate($name, $value);
  711. unset($this->temp_annotations[$name]);
  712. }
  713. }
  714. // set the subtype to id now rather than a string
  715. $this->attributes['subtype'] = get_subtype_id($this->attributes['type'], $this->attributes['subtype']);
  716. // Cache object handle
  717. if ($this->attributes['guid']) {
  718. cache_entity($this);
  719. }
  720. return $this->attributes['guid'];
  721. }
  722. }
  723. /**
  724. * Load the basic entity information and populate base attributes array.
  725. *
  726. * @param int $guid
  727. */
  728. protected function load($guid) {
  729. $row = get_entity_as_row($guid);
  730. if ($row) {
  731. // Create the array if necessary - all subclasses should test before creating
  732. if (!is_array($this->attributes)) {
  733. $this->attributes = array();
  734. }
  735. // Now put these into the attributes array as core values
  736. $objarray = (array) $row;
  737. foreach($objarray as $key => $value) {
  738. $this->attributes[$key] = $value;
  739. }
  740. // Increment the portion counter
  741. if (!$this->isFullyLoaded()) {
  742. $this->attributes['tables_loaded']++;
  743. }
  744. // Cache object handle
  745. if ($this->attributes['guid']) {
  746. cache_entity($this);
  747. }
  748. return true;
  749. }
  750. return false;
  751. }
  752. /**
  753. * Disable this entity.
  754. *
  755. * @param string $reason Optional reason
  756. * @param bool $recursive Recursively disable all contained entities?
  757. */
  758. public function disable($reason = "", $recursive = true) {
  759. return disable_entity($this->get('guid'), $reason, $recursive);
  760. }
  761. /**
  762. * Re-enable this entity.
  763. */
  764. public function enable() {
  765. return enable_entity($this->get('guid'));
  766. }
  767. /**
  768. * Is this entity enabled?
  769. *
  770. * @return boolean
  771. */
  772. public function isEnabled() {
  773. if ($this->enabled == 'yes') {
  774. return true;
  775. }
  776. return false;
  777. }
  778. /**
  779. * Delete this entity.
  780. */
  781. public function delete() {
  782. return delete_entity($this->get('guid'));
  783. }
  784. // LOCATABLE INTERFACE /////////////////////////////////////////////////////////////
  785. /** Interface to set the location */
  786. public function setLocation($location) {
  787. return $this->location = $location;
  788. }
  789. /**
  790. * Set latitude and longitude tags for a given entity.
  791. *
  792. * @param float $lat
  793. * @param float $long
  794. */
  795. public function setLatLong($lat, $long) {
  796. $this->set('geo:lat', $lat);
  797. $this->set('geo:long', $long);
  798. return true;
  799. }
  800. /**
  801. * Get the contents of the ->geo:lat field.
  802. *
  803. */
  804. public function getLatitude() {
  805. return $this->get('geo:lat');
  806. }
  807. /**
  808. * Get the contents of the ->geo:lat field.
  809. *
  810. */
  811. public function getLongitude() {
  812. return $this->get('geo:long');
  813. }
  814. /**
  815. * Get the ->location metadata.
  816. *
  817. */
  818. public function getLocation() {
  819. return $this->get('location');
  820. }
  821. // NOTABLE INTERFACE ///////////////////////////////////////////////////////////////
  822. /**
  823. * Calendar functionality.
  824. * This function sets the time of an object on a calendar listing.
  825. *
  826. * @param int $hour If ommitted, now is assumed.
  827. * @param int $minute If ommitted, now is assumed.
  828. * @param int $second If ommitted, now is assumed.
  829. * @param int $day If ommitted, now is assumed.
  830. * @param int $month If ommitted, now is assumed.
  831. * @param int $year If ommitted, now is assumed.
  832. * @param int $duration Duration of event, remainder of the day is assumed.
  833. */
  834. public function setCalendarTimeAndDuration($hour = NULL, $minute = NULL, $second = NULL, $day = NULL, $month = NULL, $year = NULL, $duration = NULL) {
  835. $start = mktime($hour, $minute, $second, $month, $day, $year);
  836. $end = $start + abs($duration);
  837. if (!$duration) {
  838. $end = get_day_end($day,$month,$year);
  839. }
  840. $this->calendar_start = $start;
  841. $this->calendar_end = $end;
  842. return true;
  843. }
  844. /**
  845. * Return the start timestamp.
  846. */
  847. public function getCalendarStartTime() {
  848. return (int)$this->calendar_start;
  849. }
  850. /**
  851. * Return the end timestamp.
  852. */
  853. public function getCalendarEndTime() {
  854. return (int)$this->calendar_end;
  855. }
  856. // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
  857. /**
  858. * Return an array of fields which can be exported.
  859. */
  860. public function getExportableValues() {
  861. return array(
  862. 'guid',
  863. 'type',
  864. 'subtype',
  865. 'time_created',
  866. 'time_updated',
  867. 'container_guid',
  868. 'owner_guid',
  869. 'site_guid'
  870. );
  871. }
  872. /**
  873. * Export this class into an array of ODD Elements containing all necessary fields.
  874. * Override if you wish to return more information than can be found in $this->attributes (shouldn't happen)
  875. */
  876. public function export() {
  877. $tmp = array();
  878. // Generate uuid
  879. $uuid = guid_to_uuid($this->getGUID());
  880. // Create entity
  881. $odd = new ODDEntity(
  882. $uuid,
  883. $this->attributes['type'],
  884. get_subtype_from_id($this->attributes['subtype'])
  885. );
  886. $tmp[] = $odd;
  887. $exportable_values = $this->getExportableValues();
  888. // Now add its attributes
  889. foreach ($this->attributes as $k => $v) {
  890. $meta = NULL;
  891. if (in_array( $k, $exportable_values)) {
  892. switch ($k) {
  893. case 'guid' : // Dont use guid in OpenDD
  894. case 'type' : // Type and subtype already taken care of
  895. case 'subtype' :
  896. break;
  897. case 'time_created' : // Created = published
  898. $odd->setAttribute('published', date("r", $v));
  899. break;
  900. case 'site_guid' : // Container
  901. $k = 'site_uuid';
  902. $v = guid_to_uuid($v);
  903. $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
  904. break;
  905. case 'container_guid' : // Container
  906. $k = 'container_uuid';
  907. $v = guid_to_uuid($v);
  908. $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
  909. break;
  910. case 'owner_guid' : // Convert owner guid to uuid, this will be stored in metadata
  911. $k = 'owner_uuid';
  912. $v = guid_to_uuid($v);
  913. $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
  914. break;
  915. default :
  916. $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
  917. }
  918. // set the time of any metadata created
  919. if ($meta) {
  920. $meta->setAttribute('published', date("r",$this->time_created));
  921. $tmp[] = $meta;
  922. }
  923. }
  924. }
  925. // Now we do something a bit special.
  926. /*
  927. * This provides a rendered view of the entity to foreign sites.
  928. */
  929. elgg_set_viewtype('default');
  930. $view = elgg_view_entity($this, true);
  931. elgg_set_viewtype();
  932. $tmp[] = new ODDMetaData($uuid . "volatile/renderedentity/", $uuid, 'renderedentity', $view , 'volatile');
  933. return $tmp;
  934. }
  935. // IMPORTABLE INTERFACE ////////////////////////////////////////////////////////////
  936. /**
  937. * Import data from an parsed xml data array.
  938. *
  939. * @param array $data
  940. * @return bool
  941. */
  942. public function import(ODD $data) {
  943. if (!($data instanceof ODDEntity)) {
  944. throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnexpectedODDClass'));
  945. }
  946. // Set type and subtype
  947. $this->attributes['type'] = $data->getAttribute('class');
  948. $this->attributes['subtype'] = $data->getAttribute('subclass');
  949. // Set owner
  950. $this->attributes['owner_guid'] = get_loggedin_userid(); // Import as belonging to importer.
  951. // Set time
  952. $this->attributes['time_created'] = strtotime($data->getAttribute('published'));
  953. $this->attributes['time_updated'] = time();
  954. return true;
  955. }
  956. // SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
  957. /**
  958. * Return an identification for the object for storage in the system log.
  959. * This id must be an integer.
  960. *
  961. * @return int
  962. */
  963. public function getSystemLogID() {
  964. return $this->getGUID();
  965. }
  966. /**
  967. * Return the class name of the object.
  968. */
  969. public function getClassName() {
  970. return get_class($this);
  971. }
  972. /**
  973. * For a given ID, return the object associated with it.
  974. * This is used by the river functionality primarily.
  975. * This is useful for checking access permissions etc on objects.
  976. */
  977. public function getObjectFromID($id) {
  978. return get_entity($id);
  979. }
  980. /**
  981. * Return the GUID of the owner of this object.
  982. */
  983. public function getObjectOwnerGUID() {
  984. return $this->owner_guid;
  985. }
  986. /**
  987. * Returns tags for this entity.
  988. *
  989. * @param array $tag_names Optionally restrict by tag metadata names.
  990. * @return array
  991. */
  992. public function getTags($tag_names = NULL) {
  993. global $CONFIG;
  994. if ($tag_names && !is_array($tag_names)) {
  995. $tag_names = array($tag_names);
  996. }
  997. $valid_tags = elgg_get_registered_tag_metadata_names();
  998. $entity_tags = array();
  999. foreach ($valid_tags as $tag_name) {
  1000. if (is_array($tag_names) && !in_array($tag_name, $tag_names)) {
  1001. continue;
  1002. }
  1003. if ($tags = $this->$tag_name) {
  1004. // if a single tag, metadata returns a string.
  1005. // if multiple tags, metadata returns an array.
  1006. if (is_array($tags)) {
  1007. $entity_tags = array_merge($entity_tags, $tags);
  1008. } else {
  1009. $entity_tags[] = $tags;
  1010. }
  1011. }
  1012. }
  1013. return $entity_tags;
  1014. }
  1015. // ITERATOR INTERFACE //////////////////////////////////////////////////////////////
  1016. /*
  1017. * This lets an entity's attributes be displayed using foreach as a normal array.
  1018. * Example: http://www.sitepoint.com/print/php5-standard-library
  1019. */
  1020. private $valid = FALSE;
  1021. function rewind() {
  1022. $this->valid = (FALSE !== reset($this->attributes));
  1023. }
  1024. function current() {
  1025. return current($this->attributes);
  1026. }
  1027. function key() {
  1028. return key($this->attributes);
  1029. }
  1030. function next() {
  1031. $this->valid = (FALSE !== next($this->attributes));
  1032. }
  1033. function valid() {
  1034. return $this->valid;
  1035. }
  1036. // ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
  1037. /*
  1038. * This lets an entity's attributes be accessed like an associative array.
  1039. * Example: http://www.sitepoint.com/print/php5-standard-library
  1040. */
  1041. function offsetSet($key, $value) {
  1042. if ( array_key_exists($key, $this->attributes) ) {
  1043. $this->attributes[$key] = $value;
  1044. }
  1045. }
  1046. function offsetGet($key) {
  1047. if ( array_key_exists($key, $this->attributes) ) {
  1048. return $this->attributes[$key];
  1049. }
  1050. }
  1051. function offsetUnset($key) {
  1052. if ( array_key_exists($key, $this->attributes) ) {
  1053. $this->attributes[$key] = ""; // Full unsetting is dangerious for our objects
  1054. }
  1055. }
  1056. function offsetExists($offset) {
  1057. return array_key_exists($offset, $this->attributes);
  1058. }
  1059. }
  1060. /**
  1061. * Initialise the entity cache.
  1062. */
  1063. function initialise_entity_cache() {
  1064. global $ENTITY_CACHE;
  1065. if (!$ENTITY_CACHE) {
  1066. //select_default_memcache('entity_cache'); // @todo Replace with memcache?
  1067. $ENTITY_CACHE = array();
  1068. }
  1069. }
  1070. /**
  1071. * Invalidate this class' entry in the cache.
  1072. *
  1073. * @param int $guid The guid
  1074. */
  1075. function invalidate_cache_for_entity($guid) {
  1076. global $ENTITY_CACHE;
  1077. $guid = (int)$guid;
  1078. unset($ENTITY_CACHE[$guid]);
  1079. //$ENTITY_CACHE->delete($guid);
  1080. }
  1081. /**
  1082. * Cache an entity.
  1083. *
  1084. * @param ElggEntity $entity Entity to cache
  1085. */
  1086. function cache_entity(ElggEntity $entity) {
  1087. global $ENTITY_CACHE;
  1088. $ENTITY_CACHE[$entity->guid] = $entity;
  1089. }
  1090. /**
  1091. * Retrieve a entity from the cache.
  1092. *
  1093. * @param int $guid The guid
  1094. */
  1095. function retrieve_cached_entity($guid) {
  1096. global $ENTITY_CACHE;
  1097. $guid = (int)$guid;
  1098. if (isset($ENTITY_CACHE[$guid])) {
  1099. if ($ENTITY_CACHE[$guid]->isFullyLoaded()) {
  1100. return $ENTITY_CACHE[$guid];
  1101. }
  1102. }
  1103. return false;
  1104. }
  1105. /**
  1106. * As retrieve_cached_entity, but returns the result as a stdClass (compatible with load functions that
  1107. * expect a database row.)
  1108. *
  1109. * @param int $guid The guid
  1110. */
  1111. function retrieve_cached_entity_row($guid) {
  1112. $obj = retrieve_cached_entity($guid);
  1113. if ($obj) {
  1114. $tmp = new stdClass;
  1115. foreach ($obj as $k => $v) {
  1116. $tmp->$k = $v;
  1117. }
  1118. return $tmp;
  1119. }
  1120. return false;
  1121. }
  1122. /**
  1123. * Return the integer ID for a given subtype, or false.
  1124. *
  1125. * @todo Move to a nicer place?
  1126. *
  1127. * @param string $type
  1128. * @param string $subtype
  1129. */
  1130. function get_subtype_id($type, $subtype) {
  1131. global $CONFIG, $SUBTYPE_CACHE;
  1132. $type = sanitise_string($type);
  1133. $subtype = sanitise_string($subtype);
  1134. if ($subtype=="") {
  1135. //return $subtype;
  1136. return FALSE;
  1137. }
  1138. // Todo: cache here? Or is looping less efficient that going to the db each time?
  1139. $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes
  1140. where type='$type' and subtype='$subtype'");
  1141. if ($result) {
  1142. if (!$SUBTYPE_CACHE) {
  1143. //select_default_memcache('subtype_cache');
  1144. $SUBTYPE_CACHE = array();
  1145. }
  1146. $SUBTYPE_CACHE[$result->id] = $result;
  1147. return $result->id;
  1148. }
  1149. return FALSE;
  1150. }
  1151. /**
  1152. * For a given subtype ID, return its identifier text.
  1153. *
  1154. * @todo Move to a nicer place?
  1155. *
  1156. * @param int $subtype_id
  1157. */
  1158. function get_subtype_from_id($subtype_id) {
  1159. global $CONFIG, $SUBTYPE_CACHE;
  1160. $subtype_id = (int)$subtype_id;
  1161. if (!$subtype_id) {
  1162. return false;
  1163. }
  1164. if (isset($SUBTYPE_CACHE[$subtype_id])) {
  1165. return $SUBTYPE_CACHE[$subtype_id]->subtype;
  1166. }
  1167. $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes where id=$subtype_id");
  1168. if ($result) {
  1169. if (!$SUBTYPE_CACHE) {
  1170. //select_default_memcache('subtype_cache');
  1171. $SUBTYPE_CACHE = array();
  1172. }
  1173. $SUBTYPE_CACHE[$subtype_id] = $result;
  1174. return $result->subtype;
  1175. }
  1176. return false;
  1177. }
  1178. /**
  1179. * This function tests to see if a subtype has a registered class handler.
  1180. *
  1181. * @param string $type The type
  1182. * @param string $subtype The subtype
  1183. * @return a class name or null
  1184. */
  1185. function get_subtype_class($type, $subtype) {
  1186. global $CONFIG, $SUBTYPE_CACHE;
  1187. $type = sanitise_string($type);
  1188. $subtype = sanitise_string($subtype);
  1189. // Todo: cache here? Or is looping less efficient that going to the db each time?
  1190. $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes
  1191. where type='$type' and subtype='$subtype'");
  1192. if ($result) {
  1193. if (!$SUBTYPE_CACHE) {
  1194. //select_default_memcache('subtype_cache');
  1195. $SUBTYPE_CACHE = array();
  1196. }
  1197. $SUBTYPE_CACHE[$result->id] = $result;
  1198. return $result->class;
  1199. }
  1200. return NULL;
  1201. }
  1202. /**
  1203. * This function tests to see if a subtype has a registered class handler by its id.
  1204. *
  1205. * @param int $subtype_id The subtype
  1206. * @return a class name or null
  1207. */
  1208. function get_subtype_class_from_id($subtype_id) {
  1209. global $CONFIG, $SUBTYPE_CACHE;
  1210. $subtype_id = (int)$subtype_id;
  1211. if (!$subtype_id) {
  1212. return false;
  1213. }
  1214. if (isset($SUBTYPE_CACHE[$subtype_id])) {
  1215. return $SUBTYPE_CACHE[$subtype_id]->class;
  1216. }
  1217. $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes where id=$subtype_id");
  1218. if ($result) {
  1219. if (!$SUBTYPE_CACHE) {
  1220. //select_default_memcache('subtype_cache');
  1221. $SUBTYPE_CACHE = array();
  1222. }
  1223. $SUBTYPE_CACHE[$subtype_id] = $result;
  1224. return $result->class;
  1225. }
  1226. return NULL;
  1227. }
  1228. /**
  1229. * This function will register a new subtype, returning its ID as required.
  1230. *
  1231. * @param string $type The type you're subtyping
  1232. * @param string $subtype The subtype label
  1233. * @param string $class Optional class handler (if you don't want it handled by the generic elgg handler for the type)
  1234. */
  1235. function add_subtype($type, $subtype, $class = "") {
  1236. global $CONFIG;
  1237. $type = sanitise_string($type);
  1238. $subtype = sanitise_string($subtype);
  1239. $class = sanitise_string($class);
  1240. // Short circuit if no subtype is given
  1241. if ($subtype == "") {
  1242. return 0;
  1243. }
  1244. $id = get_subtype_id($type, $subtype);
  1245. if ($id==0) {
  1246. return insert_data("insert into {$CONFIG->dbprefix}entity_subtypes (type, subtype, class) values ('$type','$subtype','$class')");
  1247. }
  1248. return $id;
  1249. }
  1250. /**
  1251. * Update an existing entity.
  1252. *
  1253. * @param int $guid
  1254. * @param int $owner_guid
  1255. * @param int $access_id
  1256. * @param int $container_guid
  1257. */
  1258. function update_entity($guid, $owner_guid, $access_id, $container_guid = null) {
  1259. global $CONFIG, $ENTITY_CACHE;
  1260. $guid = (int)$guid;
  1261. $owner_guid = (int)$owner_guid;
  1262. $access_id = (int)$access_id;
  1263. $container_guid = (int) $container_guid;
  1264. $time = time();
  1265. $entity = get_entity($guid);
  1266. if ($entity->type !== 'group' && is_null($container_guid)) {
  1267. $container_guid = $owner_guid;
  1268. }
  1269. if (!$entity) {
  1270. return FALSE;
  1271. }
  1272. if ($entity->canEdit()) {
  1273. if (trigger_elgg_event('update',$entity->type,$entity)) {
  1274. $ret = update_data("UPDATE {$CONFIG->dbprefix}entities set owner_guid='$owner_guid', access_id='$access_id', container_guid='$container_guid', time_updated='$time' WHERE guid=$guid");
  1275. if ($entity instanceof ElggObject) {
  1276. update_river_access_by_object($guid,$access_id);
  1277. }
  1278. // If memcache is available then delete this entry from the cache
  1279. static $newentity_cache;
  1280. if ((!$newentity_cache) && (is_memcache_available())) {
  1281. $newentity_cache = new ElggMemcache('new_entity_cache');
  1282. }
  1283. if ($newentity_cache) {
  1284. $new_entity = $newentity_cache->delete($guid);
  1285. }
  1286. // Handle cases where there was no error BUT no rows were updated!
  1287. if ($ret===false) {
  1288. return false;
  1289. }
  1290. return true;
  1291. }
  1292. }
  1293. }
  1294. /**
  1295. * Determine whether a given user is able to write to a given container.
  1296. *
  1297. * @param int $user_guid The user guid, or 0 for get_loggedin_userid()
  1298. * @param int $container_guid The container, or 0 for the current page owner.
  1299. */
  1300. function can_write_to_container($user_guid = 0, $container_guid = 0, $entity_type = 'all') {
  1301. global $CONFIG;
  1302. $user_guid = (int)$user_guid;
  1303. $user = get_entity($user_guid);
  1304. if (!$user) {
  1305. $user = get_loggedin_user();
  1306. }
  1307. $container_guid = (int)$container_guid;
  1308. if (!$container_guid) {
  1309. $container_guid = page_owner();
  1310. }
  1311. $return = false;
  1312. // @todo This is probably not a good idea.
  1313. if (!$container_guid) {
  1314. $return = true;
  1315. } else {
  1316. $container = get_entity($container_guid);
  1317. if ($container && $user) {
  1318. // If the user can edit the container, they can also write to it
  1319. $return = $container->canEdit($user->getGUID());
  1320. // See if the user is a member of the group.
  1321. if (!$return && $container instanceof ElggGroup) {
  1322. $return = $container->isMember($user);
  1323. }
  1324. }
  1325. }
  1326. // See if anyone else has anything to say
  1327. return trigger_plugin_hook('container_permissions_check', $entity_type,
  1328. array('container' => $container, 'user' => $user), $return);
  1329. }
  1330. /**
  1331. * Create a new entity of a given type.
  1332. *
  1333. * @param string $type The type of the entity (site, user, object).
  1334. * @param string $subtype The subtype of the entity.
  1335. * @param int $owner_guid The GUID of the object's owner.
  1336. * @param int $access_id The access control group to create the entity with.
  1337. * @param int $site_guid The site to add this entity to. Leave as 0 (default) for the current site.
  1338. * @return mixed The new entity's GUID, or false on failure
  1339. */
  1340. function create_entity($type, $subtype, $owner_guid, $access_id, $site_guid = 0, $container_guid = 0) {
  1341. global $CONFIG;
  1342. $type = sanitise_string($type);
  1343. $subtype = add_subtype($type, $subtype);
  1344. $owner_guid = (int)$owner_guid;
  1345. $access_id = (int)$access_id;
  1346. $time = time();
  1347. if ($site_guid == 0) {
  1348. $site_guid = $CONFIG->site_guid;
  1349. }
  1350. $site_guid = (int) $site_guid;
  1351. if ($container_guid == 0 && $type != 'group') {
  1352. $container_guid = $owner_guid;
  1353. }
  1354. $user = get_loggedin_user();
  1355. if (!can_write_to_container($user->guid, $owner_guid, $type)) {
  1356. return false;
  1357. }
  1358. if ($owner_guid != $container_guid) {
  1359. if (!can_write_to_container($user->guid, $container_guid, $type)) {
  1360. return false;
  1361. }
  1362. }
  1363. if ($type=="") {
  1364. throw new InvalidParameterException(elgg_echo('InvalidParameterException:EntityTypeNotSet'));
  1365. }
  1366. return insert_data("INSERT into {$CONFIG->dbprefix}entities
  1367. (type, subtype, owner_guid, site_guid, container_guid, access_id, time_created, time_updated, last_action) values
  1368. ('$type',$subtype, $owner_guid, $site_guid, $container_guid, $access_id, $time, $time, $time)");
  1369. }
  1370. /**
  1371. * Retrieve the entity details for a specific GUID, returning it as a stdClass db row.
  1372. *
  1373. * You will only get an object if a) it exists, b) you have access to it.
  1374. *
  1375. * @param int $guid The GUID of the object to extract
  1376. */
  1377. function get_entity_as_row($guid) {
  1378. global $CONFIG;
  1379. if (!$guid) {
  1380. return false;
  1381. }
  1382. $guid = (int) $guid;
  1383. $access = get_access_sql_suffix();
  1384. return get_data_row("SELECT * from {$CONFIG->dbprefix}entities where guid=$guid and $access");
  1385. }
  1386. /**
  1387. * Create an Elgg* object from a given entity row.
  1388. */
  1389. function entity_row_to_elggstar($row) {
  1390. if (!($row instanceof stdClass)) {
  1391. return $row;
  1392. }
  1393. if ((!isset($row->guid)) || (!isset($row->subtype))) {
  1394. return $row;
  1395. }
  1396. $new_entity = false;
  1397. // Create a memcache cache if we can
  1398. static $newentity_cache;
  1399. if ((!$newentity_cache) && (is_memcache_available())) {
  1400. $newentity_cache = new ElggMemcache('new_entity_cache');
  1401. }
  1402. if ($newentity_cache) {
  1403. $new_entity = $newentity_cache->load($row->guid);
  1404. }
  1405. if ($new_entity) {
  1406. return $new_entity;
  1407. }
  1408. // load class for entity if one is registered
  1409. $classname = get_subtype_class_from_id($row->subtype);
  1410. if ($classname!="") {
  1411. if (class_exists($classname)) {
  1412. $new_entity = new $classname($row);
  1413. if (!($new_entity instanceof ElggEntity)) {
  1414. throw new ClassException(sprintf(elgg_echo('ClassException:ClassnameNotClass'), $classname, 'ElggEntity'));
  1415. }
  1416. } else {
  1417. error_log(sprintf(elgg_echo('ClassNotFoundException:MissingClass'), $classname));
  1418. }
  1419. }
  1420. if (!$new_entity) {
  1421. switch ($row->type) {
  1422. case 'object' :
  1423. $new_entity = new ElggObject($row);
  1424. break;
  1425. case 'user' :
  1426. $new_entity = new ElggUser($row);
  1427. break;
  1428. case 'group' :
  1429. $new_entity = new ElggGroup($row);
  1430. break;
  1431. case 'site' :
  1432. $new_entity = new ElggSite($row);
  1433. break;
  1434. default:
  1435. throw new InstallationException(sprintf(elgg_echo('InstallationException:TypeNotSupported'), $row->type));
  1436. }
  1437. }
  1438. // Cache entity if we have a cache available
  1439. if (($newentity_cache) && ($new_entity)) {
  1440. $newentity_cache->save($new_entity->guid, $new_entity);
  1441. }
  1442. return $new_entity;
  1443. }
  1444. /**
  1445. * Return the entity for a given guid as the correct object.
  1446. * @param int $guid The GUID of the entity
  1447. * @return a child of ElggEntity appropriate for the type.
  1448. */
  1449. function get_entity($guid) {
  1450. static $newentity_cache;
  1451. $new_entity = false;
  1452. if (!is_numeric($guid)) {
  1453. return FALSE;
  1454. }
  1455. if ((!$newentity_cache) && (is_memcache_available())) {
  1456. $newentity_cache = new ElggMemcache('new_entity_cache');
  1457. }
  1458. if ($newentity_cache) {
  1459. $new_entity = $newentity_cache->load($guid);
  1460. }
  1461. if ($new_entity) {
  1462. return $new_entity;
  1463. }
  1464. return entity_row_to_elggstar(get_entity_as_row($guid));
  1465. }
  1466. /**
  1467. * Get all entities. NB: Plural arguments can be written as
  1468. * singular if only specifying a single element. (e.g., 'type' => 'object'
  1469. * vs 'types' => array('object')).
  1470. *
  1471. * @param array $options Array in format:
  1472. *
  1473. * types => NULL|STR entity type (SQL: type IN ('type1', 'type2') Joined with subtypes by AND...see below)
  1474. *
  1475. * subtypes => NULL|STR entity subtype (SQL: subtype IN ('subtype1', 'subtype2))
  1476. *
  1477. * type_subtype_pairs => NULL|ARR (array('type' => 'subtype')) (SQL: type = '$type' AND subtype = '$subtype') pairs
  1478. *
  1479. * owner_guids => NULL|INT entity guid
  1480. *
  1481. * container_guids => NULL|INT container_guid
  1482. *
  1483. * site_guids => NULL (current_site)|INT site_guid
  1484. *
  1485. * order_by => NULL (time_created desc)|STR SQL order by clause
  1486. *
  1487. * limit => NULL (10)|INT SQL limit clause
  1488. *
  1489. * offset => NULL (0)|INT SQL offset clause
  1490. *
  1491. * created_time_lower => NULL|INT Created time lower boundary in epoch time
  1492. *
  1493. * created_time_upper => NULL|INT Created time upper boundary in epoch time
  1494. *
  1495. * modified_time_lower => NULL|INT Modified time lower boundary in epoch time
  1496. *
  1497. * modified_time_upper => NULL|INT Modified time upper boundary in epoch time
  1498. *
  1499. * count => TRUE|FALSE return a count instead of entities
  1500. *
  1501. * wheres => array() Additional where clauses to AND together
  1502. *
  1503. * joins => array() Additional joins
  1504. *
  1505. * callback => string A callback function to pass each row through
  1506. *
  1507. * @return if count, int. if not count, array or false if no entities. false also on errors.
  1508. *
  1509. * @since 1.7.0
  1510. */
  1511. function elgg_get_entities(array $options = array()) {
  1512. global $CONFIG;
  1513. $defaults = array(
  1514. 'types' => ELGG_ENTITIES_ANY_VALUE,
  1515. 'subtypes' => ELGG_ENTITIES_ANY_VALUE,
  1516. 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
  1517. 'owner_guids' => ELGG_ENTITIES_ANY_VALUE,
  1518. 'container_guids' => ELGG_ENTITIES_ANY_VALUE,
  1519. 'site_guids' => $CONFIG->site_guid,
  1520. 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE,
  1521. 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE,
  1522. 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
  1523. 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
  1524. 'order_by' => 'e.time_created desc',
  1525. 'group_by' => ELGG_ENTITIES_ANY_VALUE,
  1526. 'limit' => 10,
  1527. 'offset' => 0,
  1528. 'count' => FALSE,
  1529. 'selects' => array(),
  1530. 'wheres' => array(),
  1531. 'joins' => array(),
  1532. 'callback' => 'entity_row_to_elggstar',
  1533. );
  1534. $options = array_merge($defaults, $options);
  1535. // can't use helper function with type_subtype_pair because it's already an array...just need to merge it
  1536. if (isset($options['type_subtype_pair'])) {
  1537. if (isset($options['type_subtype_pairs'])) {
  1538. $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'], $options['type_subtype_pair']);
  1539. } else {
  1540. $options['type_subtype_pairs'] = $options['type_subtype_pair'];
  1541. }
  1542. }
  1543. $singulars = array('type', 'subtype', 'owner_guid', 'container_guid', 'site_guid');
  1544. $options = elgg_normalise_plural_options_array($options, $singulars);
  1545. // evaluate where clauses
  1546. if (!is_array($options['wheres'])) {
  1547. $options['wheres'] = array($options['wheres']);
  1548. }
  1549. $wheres = $options['wheres'];
  1550. $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'], $options['subtypes'], $options['type_subtype_pairs']);
  1551. $wheres[] = elgg_get_entity_site_where_sql('e', $options['site_guids']);
  1552. $wheres[] = elgg_get_entity_owner_where_sql('e', $options['owner_guids']);
  1553. $wheres[] = elgg_get_entity_container_where_sql('e', $options['container_guids']);
  1554. $wheres[] = elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
  1555. $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
  1556. // remove identical where clauses
  1557. $wheres = array_unique($wheres);
  1558. // see if any functions failed
  1559. // remove empty strings on successful functions
  1560. foreach ($wheres as $i => $where) {
  1561. if ($where === FALSE) {
  1562. return FALSE;
  1563. } elseif (empty($where)) {
  1564. unset($wheres[$i]);
  1565. }
  1566. }
  1567. // evaluate join clauses
  1568. if (!is_array($options['joins'])) {
  1569. $options['joins'] = array($options['joins']);
  1570. }
  1571. // remove identical join clauses
  1572. $joins = array_unique($options['joins']);
  1573. foreach ($joins as $i => $join) {
  1574. if ($join === FALSE) {
  1575. return FALSE;
  1576. } elseif (empty($join)) {
  1577. unset($joins[$i]);
  1578. }
  1579. }
  1580. // evalutate selects
  1581. if ($options['selects']) {
  1582. $selects = '';
  1583. foreach ($options['selects'] as $select) {
  1584. $selects = ", $select";
  1585. }
  1586. } else {
  1587. $selects = '';
  1588. }
  1589. if (!$options['count']) {
  1590. $query = "SELECT DISTINCT e.*{$selects} FROM {$CONFIG->dbprefix}entities e ";
  1591. } else {
  1592. $query = "SELECT count(DISTINCT e.guid) as total FROM {$CONFIG->dbprefix}entities e ";
  1593. }
  1594. // add joins
  1595. foreach ($joins as $j) {
  1596. $query .= " $j ";
  1597. }
  1598. // add wheres
  1599. $query .= ' WHERE ';
  1600. foreach ($wheres as $w) {
  1601. $query .= " $w AND ";
  1602. }
  1603. // Add access controls
  1604. $query .= get_access_sql_suffix('e');
  1605. if (!$options['count']) {
  1606. if ($options['group_by'] = sanitise_string($options['group_by'])) {
  1607. $query .= " GROUP BY {$options['group_by']}";
  1608. }
  1609. if ($options['order_by'] = sanitise_string($options['order_by'])) {
  1610. $query .= " ORDER BY {$options['order_by']}";
  1611. }
  1612. if ($options['limit']) {
  1613. $limit = sanitise_int($options['limit']);
  1614. $offset = sanitise_int($options['offset'], true);
  1615. $query .= " LIMIT $offset, $limit";
  1616. }
  1617. try {
  1618. $dt = get_data($query, $options['callback']);
  1619. } catch (Exception $e) {
  1620. return array();
  1621. }
  1622. //@todo normalize this to array()
  1623. return $dt;
  1624. } else {
  1625. $total = get_data_row($query);
  1626. return (int)$total->total;
  1627. }
  1628. }
  1629. /**
  1630. * @deprecated 1.7. Use elgg_get_entities().
  1631. * @param $type
  1632. * @param $subtype
  1633. * @param $owner_guid
  1634. * @param $order_by
  1635. * @param $limit
  1636. * @param $offset
  1637. * @param $count
  1638. * @param $site_guid
  1639. * @param $container_guid
  1640. * @param $timelower
  1641. * @param $timeupper
  1642. * @return unknown_type
  1643. */
  1644. function get_entities($type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, $offset = 0,
  1645. $count = false, $site_guid = 0, $container_guid = null, $timelower = 0, $timeupper = 0) {
  1646. elgg_deprecated_notice('get_entities() was deprecated by elgg_get_entities().', 1.7);
  1647. // rewrite owner_guid to container_guid to emulate old functionality
  1648. if ($owner_guid != "") {
  1649. if (is_null($container_guid)) {
  1650. $container_guid = $owner_guid;
  1651. $owner_guid = NULL;
  1652. }
  1653. }
  1654. $options = array();
  1655. if ($type) {
  1656. if (is_array($type)) {
  1657. $options['types'] = $type;
  1658. } else {
  1659. $options['type'] = $type;
  1660. }
  1661. }
  1662. if ($subtype) {
  1663. if (is_array($subtype)) {
  1664. $options['subtypes'] = $subtype;
  1665. } else {
  1666. $options['subtype'] = $subtype;
  1667. }
  1668. }
  1669. if ($owner_guid) {
  1670. if (is_array($owner_guid)) {
  1671. $options['owner_guids'] = $owner_guid;
  1672. } else {
  1673. $options['owner_guid'] = $owner_guid;
  1674. }
  1675. }
  1676. if ($order_by) {
  1677. $options['order_by'] = $order_by;
  1678. }
  1679. // need to pass 0 for all option
  1680. $options['limit'] = $limit;
  1681. if ($offset) {
  1682. $options['offset'] = $offset;
  1683. }
  1684. if ($count) {
  1685. $options['count'] = $count;
  1686. }
  1687. if ($site_guid) {
  1688. $options['site_guids'] = $site_guid;
  1689. }
  1690. if ($container_guid) {
  1691. $options['container_guids'] = $container_guid;
  1692. }
  1693. if ($timeupper) {
  1694. $options['created_time_upper'] = $timeupper;
  1695. }
  1696. if ($timelower) {
  1697. $options['created_time_lower'] = $timelower;
  1698. }
  1699. $r = elgg_get_entities($options);
  1700. return $r;
  1701. }
  1702. /**
  1703. * Returns type and subtype SQL appropriate for inclusion in an IN clause.
  1704. *
  1705. * @param string $table entity table prefix.
  1706. * @param NULL|$types
  1707. * @param NULL|array $subtypes
  1708. * @param NULL|array $pairs
  1709. * @return FALSE|string
  1710. * @since 1.7.0
  1711. */
  1712. function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pairs) {
  1713. // subtype depends upon type.
  1714. if ($subtypes && !$types) {
  1715. elgg_log("Cannot set subtypes without type.", 'WARNING');
  1716. return FALSE;
  1717. }
  1718. // short circuit if nothing is requested
  1719. if (!$types && !$subtypes && !$pairs) {
  1720. return '';
  1721. }
  1722. // these are the only valid types for entities in elgg as defined in the DB.
  1723. $valid_types = array('object', 'user', 'group', 'site');
  1724. // pairs override
  1725. $wheres = array();
  1726. if (!is_array($pairs)) {
  1727. if (!is_array($types)) {
  1728. $types = array($types);
  1729. }
  1730. if ($subtypes && !is_array($subtypes)) {
  1731. $subtypes = array($subtypes);
  1732. }
  1733. // decrementer for valid types. Return FALSE if no valid types
  1734. $valid_types_count = count($types);
  1735. $valid_subtypes_count = 0;
  1736. // remove invalid types to get an accurate count of
  1737. // valid types for the invalid subtype detection to use
  1738. // below.
  1739. // also grab the count of ALL subtypes on valid types to decrement later on
  1740. // and check against.
  1741. //
  1742. // yes this is duplicating a foreach on $types.
  1743. foreach ($types as $type) {
  1744. if (!in_array($type, $valid_types)) {
  1745. $valid_types_count--;
  1746. unset ($types[array_search($type, $types)]);
  1747. } else {
  1748. // do the checking (and decrementing) in the subtype section.
  1749. $valid_subtypes_count += count($subtypes);
  1750. }
  1751. }
  1752. // return false if nothing is valid.
  1753. if (!$valid_types_count) {
  1754. return FALSE;
  1755. }
  1756. // subtypes are based upon types, so we need to look at each
  1757. // type individually to get the right subtype id.
  1758. foreach ($types as $type) {
  1759. $subtype_ids = array();
  1760. if ($subtypes) {
  1761. foreach ($subtypes as $subtype) {
  1762. // check that the subtype is valid (with ELGG_ENTITIES_NO_VALUE being a valid subtype)
  1763. if (ELGG_ENTITIES_NO_VALUE === $subtype || $subtype_id = get_subtype_id($type, $subtype)) {
  1764. $subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $subtype) ? ELGG_ENTITIES_NO_VALUE : $subtype_id;
  1765. } else {
  1766. $valid_subtypes_count--;
  1767. elgg_log("Type-subtype $type:$subtype' does not exist!", 'WARNING');
  1768. continue;
  1769. }
  1770. }
  1771. // return false if we're all invalid subtypes in the only valid type
  1772. if ($valid_subtypes_count <= 0) {
  1773. return FALSE;
  1774. }
  1775. }
  1776. if (is_array($subtype_ids) && count($subtype_ids)) {
  1777. $subtype_ids_str = implode(',', $subtype_ids);
  1778. $wheres[] = "({$table}.type = '$type' AND {$table}.subtype IN ($subtype_ids_str))";
  1779. } else {
  1780. $wheres[] = "({$table}.type = '$type')";
  1781. }
  1782. }
  1783. } else {
  1784. // using type/subtype pairs
  1785. $valid_pairs_count = count($pairs);
  1786. $valid_pairs_subtypes_count = 0;
  1787. // same deal as above--we need to know how many valid types
  1788. // and subtypes we have before hitting the subtype section.
  1789. // also normalize the subtypes into arrays here.
  1790. foreach ($pairs as $paired_type => $paired_subtypes) {
  1791. if (!in_array($paired_type, $valid_types)) {
  1792. $valid_pairs_count--;
  1793. unset ($pairs[array_search($paired_type, $pairs)]);
  1794. } else {
  1795. if ($paired_subtypes && !is_array($paired_subtypes)) {
  1796. $pairs[$paired_type] = array($paired_subtypes);
  1797. }
  1798. $valid_pairs_subtypes_count += count($paired_subtypes);
  1799. }
  1800. }
  1801. if ($valid_pairs_count <= 0) {
  1802. return FALSE;
  1803. }
  1804. foreach ($pairs as $paired_type => $paired_subtypes) {
  1805. // this will always be an array because of line 2027, right?
  1806. // no...some overly clever person can say pair => array('object' => null)
  1807. if (is_array($paired_subtypes)) {
  1808. $paired_subtype_ids = array();
  1809. foreach ($paired_subtypes as $paired_subtype) {
  1810. if (ELGG_ENTITIES_NO_VALUE === $paired_subtype || ($paired_subtype_id = get_subtype_id($paired_type, $paired_subtype))) {
  1811. $paired_subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $paired_subtype) ? ELGG_ENTITIES_NO_VALUE : $paired_subtype_id;
  1812. } else {
  1813. $valid_pairs_subtypes_count--;
  1814. elgg_log("Type-subtype $paired_type:$paired_subtype' does not exist!", 'WARNING');
  1815. // return false if we're all invalid subtypes in the only valid type
  1816. continue;
  1817. }
  1818. }
  1819. // return false if there are no valid subtypes.
  1820. if ($valid_pairs_subtypes_count <= 0) {
  1821. return FALSE;
  1822. }
  1823. if ($paired_subtype_ids_str = implode(',', $paired_subtype_ids)) {
  1824. $wheres[] = "({$table}.type = '$paired_type' AND {$table}.subtype IN ($paired_subtype_ids_str))";
  1825. }
  1826. } else {
  1827. $wheres[] = "({$table}.type = '$paired_type')";
  1828. }
  1829. }
  1830. }
  1831. // pairs override the above. return false if they don't exist.
  1832. if (is_array($wheres) && count($wheres)) {
  1833. $where = implode(' OR ', $wheres);
  1834. return "($where)";
  1835. }
  1836. return '';
  1837. }
  1838. /**
  1839. * Returns SQL for owner and containers.
  1840. *
  1841. * @todo Probably DRY up once things are settled.
  1842. * @param str $table
  1843. * @param NULL|array $owner_guids
  1844. * @return FALSE|str
  1845. * @since 1.7.0
  1846. */
  1847. function elgg_get_entity_owner_where_sql($table, $owner_guids) {
  1848. // short circuit if nothing requested
  1849. // 0 is a valid owner_guid.
  1850. if (!$owner_guids && $owner_guids !== 0) {
  1851. return '';
  1852. }
  1853. // normalize and sanitise owners
  1854. if (!is_array($owner_guids)) {
  1855. $owner_guids = array($owner_guids);
  1856. }
  1857. $owner_guids_sanitised = array();
  1858. foreach ($owner_guids as $owner_guid) {
  1859. if ($owner_guid !== ELGG_ENTITIES_NO_VALUE) {
  1860. $owner_guid = sanitise_int($owner_guid);
  1861. if (!$owner_guid) {
  1862. return false;
  1863. }
  1864. }
  1865. $owner_guids_sanitised[] = $owner_guid;
  1866. }
  1867. $where = '';
  1868. // implode(',', 0) returns 0.
  1869. if (($owner_str = implode(',', $owner_guids_sanitised)) && ($owner_str !== FALSE) && ($owner_str !== '')) {
  1870. $where = "({$table}.owner_guid IN ($owner_str))";
  1871. }
  1872. return $where;
  1873. }
  1874. /**
  1875. * Returns SQL for containers.
  1876. *
  1877. * @param string $table entity table prefix
  1878. * @param NULL|array $container_guids
  1879. * @return FALSE|string
  1880. * @since 1.7.0
  1881. */
  1882. function elgg_get_entity_container_where_sql($table, $container_guids) {
  1883. // short circuit if nothing is requested.
  1884. // 0 is a valid container_guid.
  1885. if (!$container_guids && $container_guids !== 0) {
  1886. return '';
  1887. }
  1888. // normalize and sanitise containers
  1889. if (!is_array($container_guids)) {
  1890. $container_guids = array($container_guids);
  1891. }
  1892. $container_guids_sanitised = array();
  1893. foreach ($container_guids as $container_guid) {
  1894. if ($container_guid !== ELGG_ENTITIES_NO_VALUE) {
  1895. $container_guid = sanitise_int($container_guid);
  1896. if (!$container_guid) {
  1897. return false;
  1898. }
  1899. }
  1900. $container_guids_sanitised[] = $container_guid;
  1901. }
  1902. $where = '';
  1903. // implode(',', 0) returns 0.
  1904. if (FALSE !== $container_str = implode(',', $container_guids_sanitised)) {
  1905. $where = "({$table}.container_guid IN ($container_str))";
  1906. }
  1907. return $where;
  1908. }
  1909. /**
  1910. * Returns SQL where clause for entity time limits.
  1911. *
  1912. * @param string $table Prefix for entity table name.
  1913. * @param NULL|int $time_created_upper
  1914. * @param NULL|int $time_created_lower
  1915. * @param NULL|int $time_updated_upper
  1916. * @param NULL|int $time_updated_lower
  1917. *
  1918. * @return FALSE|str FALSE on fail, string on success.
  1919. * @since 1.7.0
  1920. */
  1921. function elgg_get_entity_time_where_sql($table, $time_created_upper = NULL, $time_created_lower = NULL,
  1922. $time_updated_upper = NULL, $time_updated_lower = NULL) {
  1923. $wheres = array();
  1924. // exploit PHP's loose typing (quack) to check that they are INTs and not str cast to 0
  1925. if ($time_created_upper && $time_created_upper == sanitise_int($time_created_upper)) {
  1926. $wheres[] = "{$table}.time_created <= $time_created_upper";
  1927. }
  1928. if ($time_created_lower && $time_created_lower == sanitise_int($time_created_lower)) {
  1929. $wheres[] = "{$table}.time_created >= $time_created_lower";
  1930. }
  1931. if ($time_updated_upper && $time_updated_upper == sanitise_int($time_updated_upper)) {
  1932. $wheres[] = "{$table}.time_updated <= $time_updated_upper";
  1933. }
  1934. if ($time_updated_lower && $time_updated_lower == sanitise_int($time_updated_lower)) {
  1935. $wheres[] = "{$table}.time_updated >= $time_updated_lower";
  1936. }
  1937. if (is_array($wheres) && count($wheres) > 0) {
  1938. $where_str = implode(' AND ', $wheres);
  1939. return "($where_str)";
  1940. }
  1941. return '';
  1942. }
  1943. /**
  1944. * Gets SQL for site entities
  1945. *
  1946. * @param string $table entity table name
  1947. * @param NULL|array $site_guids
  1948. * @return FALSE|string
  1949. * @since 1.7.0
  1950. */
  1951. function elgg_get_entity_site_where_sql($table, $site_guids) {
  1952. // short circuit if nothing requested
  1953. if (!$site_guids) {
  1954. return '';
  1955. }
  1956. if (!is_array($site_guids)) {
  1957. $site_guids = array($site_guids);
  1958. }
  1959. $site_guids_sanitised = array();
  1960. foreach ($site_guids as $site_guid) {
  1961. if (!$site_guid || ($site_guid != sanitise_int($site_guid))) {
  1962. return FALSE;
  1963. }
  1964. $site_guids_sanitised[] = $site_guid;
  1965. }
  1966. if ($site_guids_str = implode(',', $site_guids_sanitised)) {
  1967. return "({$table}.site_guid IN ($site_guids_str))";
  1968. }
  1969. return '';
  1970. }
  1971. /**
  1972. * Returns a viewable list of entities
  1973. *
  1974. * @see elgg_view_entity_list
  1975. *
  1976. * @param array $options Any elgg_get_entity() options plus:
  1977. *
  1978. * full_view => BOOL Display full view entities
  1979. *
  1980. * view_type_toggle => BOOL Display gallery / list switch
  1981. *
  1982. * pagination => BOOL Display pagination links
  1983. *
  1984. * @return str
  1985. * @since 1.7.0
  1986. */
  1987. function elgg_list_entities($options) {
  1988. $defaults = array(
  1989. 'offset' => (int) max(get_input('offset', 0), 0),
  1990. 'limit' => (int) max(get_input('limit', 10), 0),
  1991. 'full_view' => TRUE,
  1992. 'view_type_toggle' => FALSE,
  1993. 'pagination' => TRUE
  1994. );
  1995. $options = array_merge($defaults, $options);
  1996. if (isset($options['count'])) {
  1997. unset ($options['count']);
  1998. }
  1999. $count = elgg_get_entities(array_merge($options, array('count' => TRUE)));
  2000. $entities = elgg_get_entities($options);
  2001. return elgg_view_entity_list($entities, $count, $options['offset'],
  2002. $options['limit'], $options['full_view'], $options['view_type_toggle'], $options['pagination']);
  2003. }
  2004. /**
  2005. * @deprecated 1.7. Use elgg_list_entities().
  2006. * @param $type
  2007. * @param $subtype
  2008. * @param $owner_guid
  2009. * @param $limit
  2010. * @param $fullview
  2011. * @param $viewtypetoggle
  2012. * @param $pagination
  2013. * @return unknown_type
  2014. */
  2015. function list_entities($type= "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = false, $pagination = true) {
  2016. elgg_deprecated_notice('list_entities() was deprecated by elgg_list_entities()!', 1.7);
  2017. $options = array();
  2018. // rewrite owner_guid to container_guid to emulate old functionality
  2019. if ($owner_guid) {
  2020. $options['container_guids'] = $owner_guid;
  2021. }
  2022. if ($type) {
  2023. $options['types'] = $type;
  2024. }
  2025. if ($subtype) {
  2026. $options['subtypes'] = $subtype;
  2027. }
  2028. if ($limit) {
  2029. $options['limit'] = $limit;
  2030. }
  2031. if ($offset = sanitise_int(get_input('offset', null))) {
  2032. $options['offset'] = $offset;
  2033. }
  2034. $options['full_view'] = $fullview;
  2035. $options['view_type_toggle'] = $viewtypetoggle;
  2036. $options['pagination'] = $pagination;
  2037. return elgg_list_entities($options);
  2038. }
  2039. /**
  2040. * Returns a viewable list of entities contained in a number of groups.
  2041. *
  2042. * @param string $subtype The arbitrary subtype of the entity
  2043. * @param int $owner_guid The GUID of the owning user
  2044. * @param int $container_guid The GUID of the containing group
  2045. * @param int $limit The number of entities to display per page (default: 10)
  2046. * @param true|false $fullview Whether or not to display the full view (default: true)
  2047. * @param true|false $viewtypetoggle Whether or not to allow gallery view (default: true)
  2048. * @param true|false $pagination Whether to display pagination (default: true)
  2049. * @return string A viewable list of entities
  2050. */
  2051. function list_entities_groups($subtype = "", $owner_guid = 0, $container_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = true, $pagination = true) {
  2052. $offset = (int) get_input('offset');
  2053. $count = get_objects_in_group($container_guid, $subtype, $owner_guid, 0, "", $limit, $offset, true);
  2054. $entities = get_objects_in_group($container_guid, $subtype, $owner_guid, 0, "", $limit, $offset);
  2055. return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
  2056. }
  2057. /**
  2058. * Returns a list of months containing content specified by the parameters
  2059. *
  2060. * @param string $type The type of entity
  2061. * @param string $subtype The subtype of entity
  2062. * @param int $container_guid The container GUID that the entinties belong to
  2063. * @param int $site_guid The site GUID
  2064. * @param str order_by SQL order by clause
  2065. * @return array|false Either an array of timestamps, or false on failure
  2066. */
  2067. function get_entity_dates($type = '', $subtype = '', $container_guid = 0, $site_guid = 0, $order_by = 'time_created') {
  2068. global $CONFIG;
  2069. $site_guid = (int) $site_guid;
  2070. if ($site_guid == 0) {
  2071. $site_guid = $CONFIG->site_guid;
  2072. }
  2073. $where = array();
  2074. if ($type != "") {
  2075. $type = sanitise_string($type);
  2076. $where[] = "type='$type'";
  2077. }
  2078. if (is_array($subtype)) {
  2079. $tempwhere = "";
  2080. if (sizeof($subtype)) {
  2081. foreach($subtype as $typekey => $subtypearray) {
  2082. foreach($subtypearray as $subtypeval) {
  2083. $typekey = sanitise_string($typekey);
  2084. if (!empty($subtypeval)) {
  2085. if (!$subtypeval = (int) get_subtype_id($typekey, $subtypeval))
  2086. return false;
  2087. } else {
  2088. $subtypeval = 0;
  2089. }
  2090. if (!empty($tempwhere)) $tempwhere .= " or ";
  2091. $tempwhere .= "(type = '{$typekey}' and subtype = {$subtypeval})";
  2092. }
  2093. }
  2094. }
  2095. if (!empty($tempwhere)) {
  2096. $where[] = "({$tempwhere})";
  2097. }
  2098. } else {
  2099. if ($subtype) {
  2100. if (!$subtype_id = get_subtype_id($type, $subtype)) {
  2101. return FALSE;
  2102. } else {
  2103. $where[] = "subtype=$subtype_id";
  2104. }
  2105. }
  2106. }
  2107. if ($container_guid !== 0) {
  2108. if (is_array($container_guid)) {
  2109. foreach($container_guid as $key => $val) {
  2110. $container_guid[$key] = (int) $val;
  2111. }
  2112. $where[] = "container_guid in (" . implode(",",$container_guid) . ")";
  2113. } else {
  2114. $container_guid = (int) $container_guid;
  2115. $where[] = "container_guid = {$container_guid}";
  2116. }
  2117. }
  2118. if ($site_guid > 0) {
  2119. $where[] = "site_guid = {$site_guid}";
  2120. }
  2121. $where[] = get_access_sql_suffix();
  2122. $sql = "SELECT DISTINCT EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME(time_created)) AS yearmonth
  2123. FROM {$CONFIG->dbprefix}entities where ";
  2124. foreach ($where as $w) {
  2125. $sql .= " $w and ";
  2126. }
  2127. $sql .= "1=1 ORDER BY $order_by";
  2128. if ($result = get_data($sql)) {
  2129. $endresult = array();
  2130. foreach($result as $res) {
  2131. $endresult[] = $res->yearmonth;
  2132. }
  2133. return $endresult;
  2134. }
  2135. return false;
  2136. }
  2137. /**
  2138. * Disable an entity but not delete it.
  2139. *
  2140. * @param int $guid The guid
  2141. * @param string $reason Optional reason
  2142. */
  2143. function disable_entity($guid, $reason = "", $recursive = true) {
  2144. global $CONFIG;
  2145. $guid = (int)$guid;
  2146. $reason = sanitise_string($reason);
  2147. if ($entity = get_entity($guid)) {
  2148. if (trigger_elgg_event('disable',$entity->type,$entity)) {
  2149. if ($entity->canEdit()) {
  2150. if ($reason) {
  2151. create_metadata($guid, 'disable_reason', $reason, '', 0, ACCESS_PUBLIC);
  2152. }
  2153. if ($recursive) {
  2154. // Temporary token overriding access controls
  2155. // @todo Do this better.
  2156. static $__RECURSIVE_DELETE_TOKEN;
  2157. // Make it slightly harder to guess
  2158. $__RECURSIVE_DELETE_TOKEN = md5(get_loggedin_userid());
  2159. $sub_entities = get_data("SELECT * from {$CONFIG->dbprefix}entities
  2160. WHERE container_guid=$guid
  2161. or owner_guid=$guid
  2162. or site_guid=$guid", 'entity_row_to_elggstar');
  2163. if ($sub_entities) {
  2164. foreach ($sub_entities as $e) {
  2165. $e->disable($reason);
  2166. }
  2167. }
  2168. $__RECURSIVE_DELETE_TOKEN = null;
  2169. }
  2170. $res = update_data("UPDATE {$CONFIG->dbprefix}entities
  2171. set enabled='no'
  2172. where guid={$guid}");
  2173. return $res;
  2174. }
  2175. }
  2176. }
  2177. return false;
  2178. }
  2179. /**
  2180. * Enable an entity again.
  2181. *
  2182. * @param int $guid
  2183. */
  2184. function enable_entity($guid) {
  2185. global $CONFIG;
  2186. $guid = (int)$guid;
  2187. // Override access only visible entities
  2188. $access_status = access_get_show_hidden_status();
  2189. access_show_hidden_entities(true);
  2190. if ($entity = get_entity($guid)) {
  2191. if (trigger_elgg_event('enable',$entity->type,$entity)) {
  2192. if ($entity->canEdit()) {
  2193. access_show_hidden_entities($access_status);
  2194. $result = update_data("UPDATE {$CONFIG->dbprefix}entities
  2195. set enabled='yes'
  2196. where guid={$guid}");
  2197. $entity->clearMetaData('disable_reason');
  2198. return $result;
  2199. }
  2200. }
  2201. }
  2202. access_show_hidden_entities($access_status);
  2203. return false;
  2204. }
  2205. /**
  2206. * Delete a given entity.
  2207. *
  2208. * @param int $guid
  2209. * @param bool $recursive If true (default) then all entities which are owned or contained by $guid will also be deleted.
  2210. * Note: this bypasses ownership of sub items.
  2211. */
  2212. function delete_entity($guid, $recursive = true) {
  2213. global $CONFIG, $ENTITY_CACHE;
  2214. $guid = (int)$guid;
  2215. if ($entity = get_entity($guid)) {
  2216. if (trigger_elgg_event('delete', $entity->type, $entity)) {
  2217. if ($entity->canEdit()) {
  2218. // delete cache
  2219. if (isset($ENTITY_CACHE[$guid])) {
  2220. invalidate_cache_for_entity($guid);
  2221. }
  2222. // Delete contained owned and otherwise releated objects (depth first)
  2223. if ($recursive) {
  2224. // Temporary token overriding access controls
  2225. // @todo Do this better.
  2226. static $__RECURSIVE_DELETE_TOKEN;
  2227. // Make it slightly harder to guess
  2228. $__RECURSIVE_DELETE_TOKEN = md5(get_loggedin_userid());
  2229. $entity_disable_override = access_get_show_hidden_status();
  2230. access_show_hidden_entities(true);
  2231. $ia = elgg_set_ignore_access(true);
  2232. $sub_entities = get_data("SELECT * from {$CONFIG->dbprefix}entities
  2233. WHERE container_guid=$guid
  2234. or owner_guid=$guid
  2235. or site_guid=$guid", 'entity_row_to_elggstar');
  2236. if ($sub_entities) {
  2237. foreach ($sub_entities as $e) {
  2238. $e->delete();
  2239. }
  2240. }
  2241. access_show_hidden_entities($entity_disable_override);
  2242. $__RECURSIVE_DELETE_TOKEN = null;
  2243. elgg_set_ignore_access($ia);
  2244. }
  2245. // Now delete the entity itself
  2246. $entity->clearMetadata();
  2247. $entity->clearAnnotations();
  2248. $entity->clearRelationships();
  2249. remove_from_river_by_subject($guid);
  2250. remove_from_river_by_object($guid);
  2251. remove_all_private_settings($guid);
  2252. $res = delete_data("DELETE from {$CONFIG->dbprefix}entities where guid={$guid}");
  2253. if ($res) {
  2254. $sub_table = "";
  2255. // Where appropriate delete the sub table
  2256. switch ($entity->type) {
  2257. case 'object' :
  2258. $sub_table = $CONFIG->dbprefix . 'objects_entity';
  2259. break;
  2260. case 'user' :
  2261. $sub_table = $CONFIG->dbprefix . 'users_entity';
  2262. break;
  2263. case 'group' :
  2264. $sub_table = $CONFIG->dbprefix . 'groups_entity';
  2265. break;
  2266. case 'site' :
  2267. $sub_table = $CONFIG->dbprefix . 'sites_entity';
  2268. break;
  2269. }
  2270. if ($sub_table) {
  2271. delete_data("DELETE from $sub_table where guid={$guid}");
  2272. }
  2273. }
  2274. return $res;
  2275. }
  2276. }
  2277. }
  2278. return false;
  2279. }
  2280. /**
  2281. * Delete multiple entities that match a given query.
  2282. * This function itterates through and calls delete_entity on each one, this is somewhat inefficient but lets
  2283. * the 'delete' even be called for each entity.
  2284. *
  2285. * @deprecated 1.7. This is a dangerous function as it defaults to deleting everything.
  2286. * @param string $type The type of entity (eg "user", "object" etc)
  2287. * @param string $subtype The arbitrary subtype of the entity
  2288. * @param int $owner_guid The GUID of the owning user
  2289. */
  2290. function delete_entities($type = "", $subtype = "", $owner_guid = 0) {
  2291. elgg_deprecated_notice('delete_entities() was deprecated because no one should use it.', 1.7);
  2292. return false;
  2293. }
  2294. /**
  2295. * A plugin hook to get certain volitile (generated on the fly) attributes about an entity in order to export them.
  2296. *
  2297. * @param unknown_type $hook
  2298. * @param unknown_type $entity_type
  2299. * @param unknown_type $returnvalue
  2300. * @param unknown_type $params The parameters, passed 'guid' and 'varname'
  2301. * @return unknown
  2302. */
  2303. function volatile_data_export_plugin_hook($hook, $entity_type, $returnvalue, $params) {
  2304. $guid = (int)$params['guid'];
  2305. $variable_name = sanitise_string($params['varname']);
  2306. if (($hook == 'volatile') && ($entity_type == 'metadata')) {
  2307. if (($guid) && ($variable_name)) {
  2308. switch ($variable_name) {
  2309. case 'renderedentity' :
  2310. elgg_set_viewtype('default');
  2311. $view = elgg_view_entity(get_entity($guid));
  2312. elgg_set_viewtype();
  2313. $tmp = new ElggMetadata();
  2314. $tmp->type = 'volatile';
  2315. $tmp->name = 'renderedentity';
  2316. $tmp->value = $view;
  2317. $tmp->entity_guid = $guid;
  2318. return $tmp;
  2319. break;
  2320. }
  2321. }
  2322. }
  2323. }
  2324. /**
  2325. * Handler called by trigger_plugin_hook on the "export" event.
  2326. */
  2327. function export_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
  2328. // Sanity check values
  2329. if ((!is_array($params)) && (!isset($params['guid']))) {
  2330. throw new InvalidParameterException(elgg_echo('InvalidParameterException:GUIDNotForExport'));
  2331. }
  2332. if (!is_array($returnvalue)) {
  2333. throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonArrayReturnValue'));
  2334. }
  2335. $guid = (int)$params['guid'];
  2336. // Get the entity
  2337. $entity = get_entity($guid);
  2338. if (!($entity instanceof ElggEntity)) {
  2339. throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, get_class()));
  2340. }
  2341. $export = $entity->export();
  2342. if (is_array($export)) {
  2343. foreach ($export as $e) {
  2344. $returnvalue[] = $e;
  2345. }
  2346. } else {
  2347. $returnvalue[] = $export;
  2348. }
  2349. return $returnvalue;
  2350. }
  2351. /**
  2352. * Utility function used by import_entity_plugin_hook() to process an ODDEntity into an unsaved ElggEntity.
  2353. *
  2354. * @param ODDEntity $element The OpenDD element
  2355. * @return ElggEntity the unsaved entity which should be populated by items.
  2356. */
  2357. function oddentity_to_elggentity(ODDEntity $element) {
  2358. $class = $element->getAttribute('class');
  2359. $subclass = $element->getAttribute('subclass');
  2360. // See if we already have imported this uuid
  2361. $tmp = get_entity_from_uuid($element->getAttribute('uuid'));
  2362. if (!$tmp) {
  2363. // Construct new class with owner from session
  2364. $classname = get_subtype_class($class, $subclass);
  2365. if ($classname!="") {
  2366. if (class_exists($classname)) {
  2367. $tmp = new $classname();
  2368. if (!($tmp instanceof ElggEntity)) {
  2369. throw new ClassException(sprintf(elgg_echo('ClassException:ClassnameNotClass', $classname, get_class())));
  2370. }
  2371. }
  2372. else
  2373. error_log(sprintf(elgg_echo('ClassNotFoundException:MissingClass'), $classname));
  2374. }
  2375. else {
  2376. switch ($class) {
  2377. case 'object' :
  2378. $tmp = new ElggObject($row);
  2379. break;
  2380. case 'user' :
  2381. $tmp = new ElggUser($row);
  2382. break;
  2383. case 'group' :
  2384. $tmp = new ElggGroup($row);
  2385. break;
  2386. case 'site' :
  2387. $tmp = new ElggSite($row);
  2388. break;
  2389. default:
  2390. throw new InstallationException(sprintf(elgg_echo('InstallationException:TypeNotSupported'), $class));
  2391. }
  2392. }
  2393. }
  2394. if ($tmp) {
  2395. if (!$tmp->import($element)) {
  2396. throw new ImportException(sprintf(elgg_echo('ImportException:ImportFailed'), $element->getAttribute('uuid')));
  2397. }
  2398. return $tmp;
  2399. }
  2400. return NULL;
  2401. }
  2402. /**
  2403. * Import an entity.
  2404. * This function checks the passed XML doc (as array) to see if it is a user, if so it constructs a new
  2405. * elgg user and returns "true" to inform the importer that it's been handled.
  2406. */
  2407. function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
  2408. $element = $params['element'];
  2409. $tmp = NULL;
  2410. if ($element instanceof ODDEntity) {
  2411. $tmp = oddentity_to_elggentity($element);
  2412. if ($tmp) {
  2413. // Make sure its saved
  2414. if (!$tmp->save()) {
  2415. throw new ImportException(sprintf(elgg_echo('ImportException:ProblemSaving'), $element->getAttribute('uuid')));
  2416. }
  2417. // Belts and braces
  2418. if (!$tmp->guid) {
  2419. throw new ImportException(elgg_echo('ImportException:NoGUID'));
  2420. }
  2421. // We have saved, so now tag
  2422. add_uuid_to_guid($tmp->guid, $element->getAttribute('uuid'));
  2423. return $tmp;
  2424. }
  2425. }
  2426. }
  2427. /**
  2428. * Determines whether or not the specified user can edit the specified entity.
  2429. *
  2430. * This is extendible by registering a plugin hook taking in the parameters 'entity' and 'user',
  2431. * which are the entity and user entities respectively
  2432. *
  2433. * @see register_plugin_hook
  2434. *
  2435. * @param int $entity_guid The GUID of the entity
  2436. * @param int $user_guid The GUID of the user
  2437. * @return true|false Whether the specified user can edit the specified entity.
  2438. */
  2439. function can_edit_entity($entity_guid, $user_guid = 0) {
  2440. global $CONFIG;
  2441. $user_guid = (int)$user_guid;
  2442. $user = get_entity($user_guid);
  2443. if (elgg_get_ignore_access())
  2444. return true;
  2445. if (!$user) {
  2446. $user = get_loggedin_user();
  2447. }
  2448. if ($entity = get_entity($entity_guid)) {
  2449. $return = false;
  2450. if (isadminloggedin())
  2451. $return = true;
  2452. // Test user if possible - should default to false unless a plugin hook says otherwise
  2453. if ($user) {
  2454. if ($entity->getOwner() == $user->getGUID()) {
  2455. $return = true;
  2456. }
  2457. if ($entity->container_guid == $user->getGUID()) {
  2458. $return = true;
  2459. }
  2460. if ($entity->type == "user" && $entity->getGUID() == $user->getGUID()) {
  2461. $return = true;
  2462. }
  2463. if ($container_entity = get_entity($entity->container_guid)) {
  2464. if ($container_entity->canEdit($user->getGUID())) {
  2465. $return = true;
  2466. }
  2467. }
  2468. }
  2469. return trigger_plugin_hook('permissions_check', $entity->type,
  2470. array('entity' => $entity, 'user' => $user), $return);
  2471. } else {
  2472. return false;
  2473. }
  2474. }
  2475. /**
  2476. * Determines whether or not the specified user can edit metadata on the specified entity.
  2477. *
  2478. * This is extendible by registering a plugin hook taking in the parameters 'entity' and 'user',
  2479. * which are the entity and user entities respectively
  2480. *
  2481. * @see register_plugin_hook
  2482. *
  2483. * @param int $entity_guid The GUID of the entity
  2484. * @param int $user_guid The GUID of the user
  2485. * @param ElggMetadata $metadata The metadata to specifically check (if any; default null)
  2486. * @return true|false Whether the specified user can edit the specified entity.
  2487. */
  2488. function can_edit_entity_metadata($entity_guid, $user_guid = 0, $metadata = null) {
  2489. if (elgg_get_ignore_access())
  2490. return true;
  2491. if ($entity = get_entity($entity_guid)) {
  2492. $return = null;
  2493. if ($metadata->owner_guid == 0) {
  2494. $return = true;
  2495. }
  2496. if (is_null($return)) {
  2497. $return = can_edit_entity($entity_guid, $user_guid);
  2498. }
  2499. $user = get_entity($user_guid);
  2500. $return = trigger_plugin_hook('permissions_check:metadata',$entity->type,array('entity' => $entity, 'user' => $user, 'metadata' => $metadata),$return);
  2501. return $return;
  2502. } else {
  2503. return false;
  2504. }
  2505. }
  2506. /**
  2507. * Get the icon for an entity
  2508. *
  2509. * @param ElggEntity $entity The entity (passed an entity rather than a guid to handle non-created entities)
  2510. * @param string $size
  2511. */
  2512. function get_entity_icon_url(ElggEntity $entity, $size = 'medium') {
  2513. global $CONFIG;
  2514. $size = sanitise_string($size);
  2515. switch (strtolower($size)) {
  2516. case 'master':
  2517. $size = 'master';
  2518. break;
  2519. case 'large' :
  2520. $size = 'large';
  2521. break;
  2522. case 'topbar' :
  2523. $size = 'topbar';
  2524. break;
  2525. case 'tiny' :
  2526. $size = 'tiny';
  2527. break;
  2528. case 'small' :
  2529. $size = 'small';
  2530. break;
  2531. case 'medium' :
  2532. default:
  2533. $size = 'medium';
  2534. }
  2535. $url = false;
  2536. $viewtype = elgg_get_viewtype();
  2537. // Step one, see if anyone knows how to render this in the current view
  2538. $url = trigger_plugin_hook('entity:icon:url', $entity->getType(), array('entity' => $entity, 'viewtype' => $viewtype, 'size' => $size), $url);
  2539. // Fail, so use default
  2540. if (!$url) {
  2541. $type = $entity->getType();
  2542. $subtype = $entity->getSubtype();
  2543. if (!empty($subtype)) {
  2544. $overrideurl = elgg_view("icon/{$type}/{$subtype}/{$size}",array('entity' => $entity));
  2545. if (!empty($overrideurl)) {
  2546. return $overrideurl;
  2547. }
  2548. }
  2549. $overrideurl = elgg_view("icon/{$type}/default/{$size}",array('entity' => $entity));
  2550. if (!empty($overrideurl)) {
  2551. return $overrideurl;
  2552. }
  2553. $url = $CONFIG->url . "_graphics/icons/default/$size.png";
  2554. }
  2555. return $url;
  2556. }
  2557. /**
  2558. * Gets the URL for an entity, given a particular GUID
  2559. *
  2560. * @param int $entity_guid The GUID of the entity
  2561. * @return string The URL of the entity
  2562. */
  2563. function get_entity_url($entity_guid) {
  2564. global $CONFIG;
  2565. if ($entity = get_entity($entity_guid)) {
  2566. $url = "";
  2567. if (isset($CONFIG->entity_url_handler[$entity->getType()][$entity->getSubType()])) {
  2568. $function = $CONFIG->entity_url_handler[$entity->getType()][$entity->getSubType()];
  2569. if (is_callable($function)) {
  2570. $url = $function($entity);
  2571. }
  2572. } elseif (isset($CONFIG->entity_url_handler[$entity->getType()]['all'])) {
  2573. $function = $CONFIG->entity_url_handler[$entity->getType()]['all'];
  2574. if (is_callable($function)) {
  2575. $url = $function($entity);
  2576. }
  2577. } elseif (isset($CONFIG->entity_url_handler['all']['all'])) {
  2578. $function = $CONFIG->entity_url_handler['all']['all'];
  2579. if (is_callable($function)) {
  2580. $url = $function($entity);
  2581. }
  2582. }
  2583. if ($url == "") {
  2584. $url = $CONFIG->url . "pg/view/" . $entity_guid;
  2585. }
  2586. return $url;
  2587. }
  2588. return false;
  2589. }
  2590. /**
  2591. * Sets the URL handler for a particular entity type and subtype
  2592. *
  2593. * @param string $function_name The function to register
  2594. * @param string $entity_type The entity type
  2595. * @param string $entity_subtype The entity subtype
  2596. * @return true|false Depending on success
  2597. */
  2598. function register_entity_url_handler($function_name, $entity_type = "all", $entity_subtype = "all") {
  2599. global $CONFIG;
  2600. if (!is_callable($function_name)) {
  2601. return false;
  2602. }
  2603. if (!isset($CONFIG->entity_url_handler)) {
  2604. $CONFIG->entity_url_handler = array();
  2605. }
  2606. if (!isset($CONFIG->entity_url_handler[$entity_type])) {
  2607. $CONFIG->entity_url_handler[$entity_type] = array();
  2608. }
  2609. $CONFIG->entity_url_handler[$entity_type][$entity_subtype] = $function_name;
  2610. return true;
  2611. }
  2612. /**
  2613. * Default Icon URL handler for entities.
  2614. * This will attempt to find a default entity for the current view and return a url. This is registered at
  2615. * a low priority so that other handlers will pick it up first.
  2616. *
  2617. * @param unknown_type $hook
  2618. * @param unknown_type $entity_type
  2619. * @param unknown_type $returnvalue
  2620. * @param unknown_type $params
  2621. */
  2622. function default_entity_icon_hook($hook, $entity_type, $returnvalue, $params) {
  2623. global $CONFIG;
  2624. if ((!$returnvalue) && ($hook == 'entity:icon:url')) {
  2625. $entity = $params['entity'];
  2626. $type = $entity->type;
  2627. $subtype = get_subtype_from_id($entity->subtype);
  2628. $viewtype = $params['viewtype'];
  2629. $size = $params['size'];
  2630. $url = "views/$viewtype/graphics/icons/$type/$subtype/$size.png";
  2631. if (!@file_exists($CONFIG->path . $url)) {
  2632. $url = "views/$viewtype/graphics/icons/$type/default/$size.png";
  2633. }
  2634. if(!@file_exists($CONFIG->path . $url)) {
  2635. $url = "views/$viewtype/graphics/icons/default/$size.png";
  2636. }
  2637. if (@file_exists($CONFIG->path . $url)) {
  2638. return $CONFIG->url . $url;
  2639. }
  2640. }
  2641. }
  2642. /**
  2643. * Registers and entity type and subtype to return in search and other places.
  2644. * A description in the elgg_echo languages file of the form item:type:subtype
  2645. * is also expected.
  2646. *
  2647. * @param string $type The type of entity (object, site, user, group)
  2648. * @param string $subtype The subtype to register (may be blank)
  2649. * @return true|false Depending on success
  2650. */
  2651. function register_entity_type($type, $subtype=null) {
  2652. global $CONFIG;
  2653. $type = strtolower($type);
  2654. if (!in_array($type, array('object','site','group','user'))) {
  2655. return false;
  2656. }
  2657. if (!isset($CONFIG->registered_entities)) {
  2658. $CONFIG->registered_entities = array();
  2659. }
  2660. if (!isset($CONFIG->registered_entities[$type])) {
  2661. $CONFIG->registered_entities[$type] = array();
  2662. }
  2663. if ($subtype) {
  2664. $CONFIG->registered_entities[$type][] = $subtype;
  2665. }
  2666. return true;
  2667. }
  2668. /**
  2669. * Returns registered entity types and subtypes
  2670. *
  2671. * @see register_entity_type
  2672. *
  2673. * @param string $type The type of entity (object, site, user, group) or blank for all
  2674. * @return array|false Depending on whether entities have been registered
  2675. */
  2676. function get_registered_entity_types($type = null) {
  2677. global $CONFIG;
  2678. if (!isset($CONFIG->registered_entities)) {
  2679. return false;
  2680. }
  2681. if ($type) {
  2682. $type = strtolower($type);
  2683. }
  2684. if (!empty($type) && empty($CONFIG->registered_entities[$type])) {
  2685. return false;
  2686. }
  2687. if (empty($type)) {
  2688. return $CONFIG->registered_entities;
  2689. }
  2690. return $CONFIG->registered_entities[$type];
  2691. }
  2692. /**
  2693. * Determines whether or not the specified entity type and subtype have been registered in the system
  2694. *
  2695. * @param string $type The type of entity (object, site, user, group)
  2696. * @param string $subtype The subtype (may be blank)
  2697. * @return true|false Depending on whether or not the type has been registered
  2698. */
  2699. function is_registered_entity_type($type, $subtype=null) {
  2700. global $CONFIG;
  2701. if (!isset($CONFIG->registered_entities)) {
  2702. return false;
  2703. }
  2704. $type = strtolower($type);
  2705. // @todo registering a subtype implicitly registers the type.
  2706. // see #2684
  2707. if (!isset($CONFIG->registered_entities[$type])) {
  2708. return false;
  2709. }
  2710. if ($subtype && !in_array($subtype, $CONFIG->registered_entities[$type])) {
  2711. return false;
  2712. }
  2713. return true;
  2714. }
  2715. /**
  2716. * Page handler for generic entities view system
  2717. *
  2718. * @param array $page Page elements from pain page handler
  2719. */
  2720. function entities_page_handler($page) {
  2721. if (isset($page[0])) {
  2722. global $CONFIG;
  2723. set_input('guid',$page[0]);
  2724. include($CONFIG->path . "entities/index.php");
  2725. }
  2726. }
  2727. /**
  2728. * @deprecated 1.7. Use elgg_list_registered_entities().
  2729. * @param $owner_guid
  2730. * @param $limit
  2731. * @param $fullview
  2732. * @param $viewtypetoggle
  2733. * @param $allowedtypes
  2734. * @return unknown_type
  2735. */
  2736. function list_registered_entities($owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = false, $allowedtypes = true) {
  2737. elgg_deprecated_notice('list_registered_entities() was deprecated by elgg_list_registered_entities().', 1.7);
  2738. $options = array();
  2739. // don't want to send anything if not being used.
  2740. if ($owner_guid) {
  2741. $options['owner_guid'] = $owner_guid;
  2742. }
  2743. if ($limit) {
  2744. $options['limit'] = $limit;
  2745. }
  2746. if ($allowedtypes) {
  2747. $options['allowed_types'] = $allowedtypes;
  2748. }
  2749. // need to send because might be BOOL
  2750. $options['full_view'] = $fullview;
  2751. $options['view_type_toggle'] = $viewtypetoggle;
  2752. $options['offset'] = get_input('offset', 0);
  2753. return elgg_list_registered_entities($options);
  2754. }
  2755. /**
  2756. * Returns a viewable list of entities based on the registered types.
  2757. *
  2758. * @see elgg_view_entity_list
  2759. *
  2760. * @param array $options Any elgg_get_entity() options plus:
  2761. *
  2762. * full_view => BOOL Display full view entities
  2763. *
  2764. * view_type_toggle => BOOL Display gallery / list switch
  2765. *
  2766. * allowed_types => TRUE|ARRAY True to show all types or an array of valid types.
  2767. *
  2768. * pagination => BOOL Display pagination links
  2769. *
  2770. * @return string A viewable list of entities
  2771. * @since 1.7.0
  2772. */
  2773. function elgg_list_registered_entities($options) {
  2774. $defaults = array(
  2775. 'full_view' => TRUE,
  2776. 'allowed_types' => TRUE,
  2777. 'view_type_toggle' => FALSE,
  2778. 'pagination' => TRUE,
  2779. 'offset' => 0
  2780. );
  2781. $options = array_merge($defaults, $options);
  2782. $typearray = array();
  2783. if ($object_types = get_registered_entity_types()) {
  2784. foreach($object_types as $object_type => $subtype_array) {
  2785. if (in_array($object_type, $options['allowed_types']) || $options['allowed_types'] === TRUE) {
  2786. $typearray[$object_type] = array();
  2787. if (is_array($subtype_array) && count($subtype_array)) {
  2788. foreach ($subtype_array as $subtype) {
  2789. $typearray[$object_type][] = $subtype;
  2790. }
  2791. }
  2792. }
  2793. }
  2794. }
  2795. $options['type_subtype_pairs'] = $typearray;
  2796. $count = elgg_get_entities(array_merge(array('count' => TRUE), $options));
  2797. $entities = elgg_get_entities($options);
  2798. return elgg_view_entity_list($entities, $count, $options['offset'],
  2799. $options['limit'], $options['full_view'], $options['view_type_toggle'], $options['pagination']);
  2800. }
  2801. /**
  2802. * Get entities based on their private data, in a similar way to metadata.
  2803. *
  2804. * @param string $name The name of the setting
  2805. * @param string $value The value of the setting
  2806. * @param string $type The type of entity (eg "user", "object" etc)
  2807. * @param string $subtype The arbitrary subtype of the entity
  2808. * @param int $owner_guid The GUID of the owning user
  2809. * @param string $order_by The field to order by; by default, time_created desc
  2810. * @param int $limit The number of entities to return; 10 by default
  2811. * @param int $offset The indexing offset, 0 by default
  2812. * @param boolean $count Set to true to get a count rather than the entities themselves (limits and offsets don't apply in this context). Defaults to false.
  2813. * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
  2814. * @param int|array $container_guid The container or containers to get entities from (default: all containers).
  2815. * @return array A list of entities.
  2816. */
  2817. function get_entities_from_private_setting($name = "", $value = "", $type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = null) {
  2818. global $CONFIG;
  2819. if ($subtype === false || $subtype === null || $subtype === 0) {
  2820. return false;
  2821. }
  2822. $name = sanitise_string($name);
  2823. $value = sanitise_string($value);
  2824. if ($order_by == "") {
  2825. $order_by = "e.time_created desc";
  2826. }
  2827. $order_by = sanitise_string($order_by);
  2828. $limit = (int)$limit;
  2829. $offset = (int)$offset;
  2830. $site_guid = (int) $site_guid;
  2831. if ($site_guid == 0) {
  2832. $site_guid = $CONFIG->site_guid;
  2833. }
  2834. $where = array();
  2835. if (is_array($type)) {
  2836. $tempwhere = "";
  2837. if (sizeof($type)) {
  2838. foreach($type as $typekey => $subtypearray) {
  2839. foreach($subtypearray as $subtypeval) {
  2840. $typekey = sanitise_string($typekey);
  2841. if (!empty($subtypeval)) {
  2842. if (!$subtypeval = (int) get_subtype_id($typekey, $subtypeval)) {
  2843. return false;
  2844. }
  2845. } else {
  2846. $subtypeval = 0;
  2847. }
  2848. if (!empty($tempwhere)) $tempwhere .= " or ";
  2849. $tempwhere .= "(e.type = '{$typekey}' and e.subtype = {$subtypeval})";
  2850. }
  2851. }
  2852. }
  2853. if (!empty($tempwhere)) {
  2854. $where[] = "({$tempwhere})";
  2855. }
  2856. } else {
  2857. $type = sanitise_string($type);
  2858. if ($subtype AND !$subtype = get_subtype_id($type, $subtype)) {
  2859. return false;
  2860. }
  2861. if ($type != "") {
  2862. $where[] = "e.type='$type'";
  2863. }
  2864. if ($subtype!=="") {
  2865. $where[] = "e.subtype=$subtype";
  2866. }
  2867. }
  2868. if ($owner_guid != "") {
  2869. if (!is_array($owner_guid)) {
  2870. $owner_array = array($owner_guid);
  2871. $owner_guid = (int) $owner_guid;
  2872. // $where[] = "owner_guid = '$owner_guid'";
  2873. } else if (sizeof($owner_guid) > 0) {
  2874. $owner_array = array_map('sanitise_int', $owner_guid);
  2875. // Cast every element to the owner_guid array to int
  2876. // $owner_guid = array_map("sanitise_int", $owner_guid);
  2877. // $owner_guid = implode(",",$owner_guid);
  2878. // $where[] = "owner_guid in ({$owner_guid})";
  2879. }
  2880. if (is_null($container_guid)) {
  2881. $container_guid = $owner_array;
  2882. }
  2883. }
  2884. if ($site_guid > 0) {
  2885. $where[] = "e.site_guid = {$site_guid}";
  2886. }
  2887. if (!is_null($container_guid)) {
  2888. if (is_array($container_guid)) {
  2889. foreach($container_guid as $key => $val) $container_guid[$key] = (int) $val;
  2890. $where[] = "e.container_guid in (" . implode(",",$container_guid) . ")";
  2891. } else {
  2892. $container_guid = (int) $container_guid;
  2893. $where[] = "e.container_guid = {$container_guid}";
  2894. }
  2895. }
  2896. if ($name!="") {
  2897. $where[] = "s.name = '$name'";
  2898. }
  2899. if ($value!="") {
  2900. $where[] = "s.value='$value'";
  2901. }
  2902. if (!$count) {
  2903. $query = "SELECT distinct e.*
  2904. from {$CONFIG->dbprefix}entities e
  2905. JOIN {$CONFIG->dbprefix}private_settings s ON e.guid=s.entity_guid where ";
  2906. } else {
  2907. $query = "SELECT count(distinct e.guid) as total
  2908. from {$CONFIG->dbprefix}entities e JOIN {$CONFIG->dbprefix}private_settings s
  2909. ON e.guid=s.entity_guid where ";
  2910. }
  2911. foreach ($where as $w) {
  2912. $query .= " $w and ";
  2913. }
  2914. // Add access controls
  2915. $query .= get_access_sql_suffix('e');
  2916. if (!$count) {
  2917. $query .= " order by $order_by";
  2918. if ($limit) {
  2919. // Add order and limit
  2920. $query .= " limit $offset, $limit";
  2921. }
  2922. $dt = get_data($query, "entity_row_to_elggstar");
  2923. return $dt;
  2924. } else {
  2925. $total = get_data_row($query);
  2926. return $total->total;
  2927. }
  2928. }
  2929. /**
  2930. * Get entities based on their private data by multiple keys, in a similar way to metadata.
  2931. *
  2932. * @param string $name The name of the setting
  2933. * @param string $value The value of the setting
  2934. * @param string|array $type The type of entity (eg "user", "object" etc) or array(type1 => array('subtype1', ...'subtypeN'), ...)
  2935. * @param string $subtype The arbitrary subtype of the entity
  2936. * @param int $owner_guid The GUID of the owning user
  2937. * @param string $order_by The field to order by; by default, time_created desc
  2938. * @param int $limit The number of entities to return; 10 by default
  2939. * @param int $offset The indexing offset, 0 by default
  2940. * @param boolean $count Set to true to get a count rather than the entities themselves (limits and offsets don't apply in this context). Defaults to false.
  2941. * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
  2942. * @param int|array $container_guid The container or containers to get entities from (default: all containers).
  2943. * @return array A list of entities.
  2944. */
  2945. function get_entities_from_private_setting_multi(array $name, $type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = null) {
  2946. global $CONFIG;
  2947. if ($subtype === false || $subtype === null || $subtype === 0) {
  2948. return false;
  2949. }
  2950. if ($order_by == "") {
  2951. $order_by = "e.time_created desc";
  2952. }
  2953. $order_by = sanitise_string($order_by);
  2954. $limit = (int)$limit;
  2955. $offset = (int)$offset;
  2956. $site_guid = (int) $site_guid;
  2957. if ($site_guid == 0) {
  2958. $site_guid = $CONFIG->site_guid;
  2959. }
  2960. $where = array();
  2961. if (is_array($type)) {
  2962. $tempwhere = "";
  2963. if (sizeof($type)) {
  2964. foreach($type as $typekey => $subtypearray) {
  2965. foreach($subtypearray as $subtypeval) {
  2966. $typekey = sanitise_string($typekey);
  2967. if (!empty($subtypeval)) {
  2968. if (!$subtypeval = (int) get_subtype_id($typekey, $subtypeval)) {
  2969. return false;
  2970. }
  2971. } else {
  2972. $subtypeval = 0;
  2973. }
  2974. if (!empty($tempwhere)) $tempwhere .= " or ";
  2975. $tempwhere .= "(e.type = '{$typekey}' and e.subtype = {$subtypeval})";
  2976. }
  2977. }
  2978. }
  2979. if (!empty($tempwhere)) {
  2980. $where[] = "({$tempwhere})";
  2981. }
  2982. } else {
  2983. $type = sanitise_string($type);
  2984. if ($subtype AND !$subtype = get_subtype_id($type, $subtype)) {
  2985. return false;
  2986. }
  2987. if ($type != "") {
  2988. $where[] = "e.type='$type'";
  2989. }
  2990. if ($subtype!=="") {
  2991. $where[] = "e.subtype=$subtype";
  2992. }
  2993. }
  2994. if ($owner_guid != "") {
  2995. if (!is_array($owner_guid)) {
  2996. $owner_array = array($owner_guid);
  2997. $owner_guid = (int) $owner_guid;
  2998. // $where[] = "owner_guid = '$owner_guid'";
  2999. } else if (sizeof($owner_guid) > 0) {
  3000. $owner_array = array_map('sanitise_int', $owner_guid);
  3001. // Cast every element to the owner_guid array to int
  3002. // $owner_guid = array_map("sanitise_int", $owner_guid);
  3003. // $owner_guid = implode(",",$owner_guid);
  3004. // $where[] = "owner_guid in ({$owner_guid})";
  3005. }
  3006. if (is_null($container_guid)) {
  3007. $container_guid = $owner_array;
  3008. }
  3009. }
  3010. if ($site_guid > 0) {
  3011. $where[] = "e.site_guid = {$site_guid}";
  3012. }
  3013. if (!is_null($container_guid)) {
  3014. if (is_array($container_guid)) {
  3015. foreach($container_guid as $key => $val) $container_guid[$key] = (int) $val;
  3016. $where[] = "e.container_guid in (" . implode(",",$container_guid) . ")";
  3017. } else {
  3018. $container_guid = (int) $container_guid;
  3019. $where[] = "e.container_guid = {$container_guid}";
  3020. }
  3021. }
  3022. if ($name) {
  3023. $s_join = "";
  3024. $i = 1;
  3025. foreach ($name as $k => $n) {
  3026. $k = sanitise_string($k);
  3027. $n = sanitise_string($n);
  3028. $s_join .= " JOIN {$CONFIG->dbprefix}private_settings s$i ON e.guid=s$i.entity_guid";
  3029. $where[] = "s$i.name = '$k'";
  3030. $where[] = "s$i.value = '$n'";
  3031. $i++;
  3032. }
  3033. }
  3034. if (!$count) {
  3035. $query = "SELECT distinct e.* from {$CONFIG->dbprefix}entities e $s_join where ";
  3036. } else {
  3037. $query = "SELECT count(distinct e.guid) as total
  3038. from {$CONFIG->dbprefix}entities e $s_join where ";
  3039. }
  3040. foreach ($where as $w) {
  3041. $query .= " $w and ";
  3042. }
  3043. // Add access controls
  3044. $query .= get_access_sql_suffix('e');
  3045. if (!$count) {
  3046. $query .= " order by $order_by";
  3047. // Add order and limit
  3048. if ($limit) {
  3049. $query .= " limit $offset, $limit";
  3050. }
  3051. $dt = get_data($query, "entity_row_to_elggstar");
  3052. return $dt;
  3053. } else {
  3054. $total = get_data_row($query);
  3055. return $total->total;
  3056. }
  3057. }
  3058. /**
  3059. * Gets a private setting for an entity.
  3060. *
  3061. * @param int $entity_guid The entity GUID
  3062. * @param string $name The name of the setting
  3063. * @return mixed The setting value, or false on failure
  3064. */
  3065. function get_private_setting($entity_guid, $name) {
  3066. global $CONFIG;
  3067. $entity_guid = (int) $entity_guid;
  3068. $name = sanitise_string($name);
  3069. if ($setting = get_data_row("SELECT value from {$CONFIG->dbprefix}private_settings where name = '{$name}' and entity_guid = {$entity_guid}")) {
  3070. return $setting->value;
  3071. }
  3072. return false;
  3073. }
  3074. /**
  3075. * Return an array of all private settings for a given
  3076. *
  3077. * @param int $entity_guid The entity GUID
  3078. */
  3079. function get_all_private_settings($entity_guid) {
  3080. global $CONFIG;
  3081. $entity_guid = (int) $entity_guid;
  3082. $result = get_data("SELECT * from {$CONFIG->dbprefix}private_settings where entity_guid = {$entity_guid}");
  3083. if ($result) {
  3084. $return = array();
  3085. foreach ($result as $r) {
  3086. $return[$r->name] = $r->value;
  3087. }
  3088. return $return;
  3089. }
  3090. return false;
  3091. }
  3092. /**
  3093. * Sets a private setting for an entity.
  3094. *
  3095. * @param int $entity_guid The entity GUID
  3096. * @param string $name The name of the setting
  3097. * @param string $value The value of the setting
  3098. * @return mixed The setting ID, or false on failure
  3099. */
  3100. function set_private_setting($entity_guid, $name, $value) {
  3101. global $CONFIG;
  3102. $entity_guid = (int) $entity_guid;
  3103. $name = sanitise_string($name);
  3104. $value = sanitise_string($value);
  3105. $result = insert_data("INSERT into {$CONFIG->dbprefix}private_settings
  3106. (entity_guid, name, value) VALUES
  3107. ($entity_guid, '{$name}', '{$value}')
  3108. ON DUPLICATE KEY UPDATE value='$value'");
  3109. if ($result === 0) {
  3110. return true;
  3111. }
  3112. return $result;
  3113. }
  3114. /**
  3115. * Deletes a private setting for an entity.
  3116. *
  3117. * @param int $entity_guid The Entity GUID
  3118. * @param string $name The name of the setting
  3119. * @return true|false depending on success
  3120. *
  3121. */
  3122. function remove_private_setting($entity_guid, $name) {
  3123. global $CONFIG;
  3124. $entity_guid = (int) $entity_guid;
  3125. $name = sanitise_string($name);
  3126. return delete_data("DELETE from {$CONFIG->dbprefix}private_settings
  3127. where name = '{$name}'
  3128. and entity_guid = {$entity_guid}");
  3129. }
  3130. /**
  3131. * Deletes all private settings for an entity.
  3132. *
  3133. * @param int $entity_guid The Entity GUID
  3134. * @return true|false depending on success
  3135. *
  3136. */
  3137. function remove_all_private_settings($entity_guid) {
  3138. global $CONFIG;
  3139. $entity_guid = (int) $entity_guid;
  3140. return delete_data("DELETE from {$CONFIG->dbprefix}private_settings
  3141. where entity_guid = {$entity_guid}");
  3142. }
  3143. /*
  3144. * Check the recurisve delete permissions token.
  3145. *
  3146. * @return bool
  3147. */
  3148. function recursive_delete_permissions_check($hook, $entity_type, $returnvalue, $params) {
  3149. static $__RECURSIVE_DELETE_TOKEN;
  3150. $entity = $params['entity'];
  3151. if ((isloggedin()) && ($__RECURSIVE_DELETE_TOKEN) && (strcmp($__RECURSIVE_DELETE_TOKEN, md5(get_loggedin_userid())))) {
  3152. return true;
  3153. }
  3154. // consult next function
  3155. return NULL;
  3156. }
  3157. /**
  3158. * Garbage collect stub and fragments from any broken delete/create calls
  3159. *
  3160. * @param unknown_type $hook
  3161. * @param unknown_type $user
  3162. * @param unknown_type $returnvalue
  3163. * @param unknown_type $tag
  3164. */
  3165. function entities_gc($hook, $user, $returnvalue, $tag) {
  3166. global $CONFIG;
  3167. $tables = array ('sites_entity', 'objects_entity', 'groups_entity', 'users_entity');
  3168. foreach ($tables as $table) {
  3169. delete_data("DELETE from {$CONFIG->dbprefix}{$table}
  3170. where guid NOT IN (SELECT guid from {$CONFIG->dbprefix}entities)");
  3171. }
  3172. }
  3173. /**
  3174. * Runs unit tests for the entities object.
  3175. */
  3176. function entities_test($hook, $type, $value, $params) {
  3177. global $CONFIG;
  3178. $value[] = $CONFIG->path . 'engine/tests/objects/entities.php';
  3179. return $value;
  3180. }
  3181. /**
  3182. * Entities init function; establishes the page handler
  3183. *
  3184. */
  3185. function entities_init() {
  3186. register_page_handler('view','entities_page_handler');
  3187. register_plugin_hook('unit_test', 'system', 'entities_test');
  3188. // Allow a permission override for recursive entity deletion
  3189. // @todo Can this be done better?
  3190. register_plugin_hook('permissions_check','all','recursive_delete_permissions_check');
  3191. register_plugin_hook('permissions_check:metadata','all','recursive_delete_permissions_check');
  3192. register_plugin_hook('gc','system','entities_gc');
  3193. }
  3194. /** Register the import hook */
  3195. register_plugin_hook("import", "all", "import_entity_plugin_hook", 0);
  3196. /** Register the hook, ensuring entities are serialised first */
  3197. register_plugin_hook("export", "all", "export_entity_plugin_hook", 0);
  3198. /** Hook to get certain named bits of volatile data about an entity */
  3199. register_plugin_hook('volatile', 'metadata', 'volatile_data_export_plugin_hook');
  3200. /** Hook for rendering a default icon for entities */
  3201. register_plugin_hook('entity:icon:url', 'all', 'default_entity_icon_hook', 1000);
  3202. /** Register init system event **/
  3203. register_elgg_event_handler('init','system','entities_init');