PageRenderTime 124ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/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

Large files files are truncated, but you can click here to view the full file

  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[

Large files files are truncated, but you can click here to view the full file