aboutsummaryrefslogtreecommitdiff
path: root/engine/classes/ElggEntity.php
diff options
context:
space:
mode:
Diffstat (limited to 'engine/classes/ElggEntity.php')
-rw-r--r--engine/classes/ElggEntity.php2980
1 files changed, 1770 insertions, 1210 deletions
diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php
index f0d58d1bd..a563f6fad 100644
--- a/engine/classes/ElggEntity.php
+++ b/engine/classes/ElggEntity.php
@@ -1,1210 +1,1770 @@
-<?php
-/**
- * ElggEntity The elgg entity superclass
- * This class holds methods for accessing the main entities table.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Core
- */
-abstract class ElggEntity implements
- Notable, // Calendar interface
- Locatable, // Geocoding interface
- Exportable, // Allow export of data
- Importable, // Allow import of data
- Loggable, // Can events related to this object class be logged
- Iterator, // Override foreach behaviour
- ArrayAccess // Override for array access
-{
- /**
- * The main attributes of an entity.
- * Blank entries for all database fields should be created by the constructor.
- * Subclasses should add to this in their constructors.
- * Any field not appearing in this will be viewed as a
- */
- protected $attributes;
-
- /**
- * If set, overrides the value of getURL()
- */
- protected $url_override;
-
- /**
- * Icon override, overrides the value of getIcon().
- */
- protected $icon_override;
-
- /**
- * Temporary cache for metadata, permitting meta data access before a guid has obtained.
- */
- protected $temp_metadata;
-
- /**
- * Temporary cache for annotations, permitting meta data access before a guid has obtained.
- */
- protected $temp_annotations;
-
-
- /**
- * Volatile data structure for this object, allows for storage of data
- * in-memory that isn't sync'd back to the metadata table.
- */
- protected $volatile;
-
- /**
- * Initialise the attributes array.
- * This is vital to distinguish between metadata and base parameters.
- *
- * Place your base parameters here.
- *
- * @return void
- */
- protected function initialise_attributes() {
- initialise_entity_cache();
-
- // Create attributes array if not already created
- if (!is_array($this->attributes)) {
- $this->attributes = array();
- }
- if (!is_array($this->temp_metadata)) {
- $this->temp_metadata = array();
- }
- if (!is_array($this->temp_annotations)) {
- $this->temp_annotations = array();
- }
- if (!is_array($this->volatile)) {
- $this->volatile = array();
- }
-
- $this->attributes['guid'] = "";
- $this->attributes['type'] = "";
- $this->attributes['subtype'] = "";
-
- $this->attributes['owner_guid'] = get_loggedin_userid();
- $this->attributes['container_guid'] = get_loggedin_userid();
-
- $this->attributes['site_guid'] = 0;
- $this->attributes['access_id'] = ACCESS_PRIVATE;
- $this->attributes['time_created'] = "";
- $this->attributes['time_updated'] = "";
- $this->attributes['last_action'] = '';
- $this->attributes['enabled'] = "yes";
-
- // There now follows a bit of a hack
- /* Problem: To speed things up, some objects are split over several tables, this means that it requires
- * n number of database reads to fully populate an entity. This causes problems for caching and create events
- * since it is not possible to tell whether a subclassed entity is complete.
- * Solution: We have two counters, one 'tables_split' which tells whatever is interested how many tables
- * are going to need to be searched in order to fully populate this object, and 'tables_loaded' which is how
- * many have been loaded thus far.
- * If the two are the same then this object is complete.
- *
- * Use: isFullyLoaded() to check
- */
- $this->attributes['tables_split'] = 1;
- $this->attributes['tables_loaded'] = 0;
- }
-
- /**
- * Clone an entity
- *
- * Resets the guid so that the entity can be saved as a distinct entity from
- * the original. Creation time will be set when this new entity is saved.
- * The owner and container guids come from the original entity. The clone
- * method copies metadata but does not copy over annotations, or private settings.
- *
- * Note: metadata will have its owner and access id set when the entity is saved
- * and it will be the same as that of the entity.
- */
- public function __clone() {
-
- $orig_entity = get_entity($this->guid);
- if (!$orig_entity) {
- elgg_log("Failed to clone entity with GUID $this->guid", "ERROR");
- return;
- }
-
- $metadata_array = get_metadata_for_entity($this->guid);
-
- $this->attributes['guid'] = "";
-
- $this->attributes['subtype'] = $orig_entity->getSubtype();
-
- // copy metadata over to new entity - slightly convoluted due to
- // handling of metadata arrays
- if (is_array($metadata_array)) {
- // create list of metadata names
- $metadata_names = array();
- foreach ($metadata_array as $metadata) {
- $metadata_names[] = $metadata['name'];
- }
- // arrays are stored with multiple enties per name
- $metadata_names = array_unique($metadata_names);
-
- // move the metadata over
- foreach ($metadata_names as $name) {
- $this->set($name, $orig_entity->$name);
- }
- }
- }
-
- /**
- * Return the value of a given key.
- * If $name is a key field (as defined in $this->attributes) that value is returned, otherwise it will
- * then look to see if the value is in this object's metadata.
- *
- * Q: Why are we not using __get overload here?
- * A: Because overload operators cause problems during subclassing, so we put the code here and
- * create overloads in subclasses.
- *
- * subtype is returned as an id rather than the subtype string. Use getSubtype()
- * to get the subtype string.
- *
- * @param string $name
- * @return mixed Returns the value of a given value, or null.
- */
- public function get($name) {
- // See if its in our base attribute
- if (isset($this->attributes[$name])) {
- return $this->attributes[$name];
- }
-
- // No, so see if its in the meta data for this entity
- $meta = $this->getMetaData($name);
-
- // getMetaData returns NULL if $name is not found
- return $meta;
- }
-
- /**
- * Set the value of a given key, replacing it if necessary.
- * If $name is a base attribute (as defined in $this->attributes) that value is set, otherwise it will
- * set the appropriate item of metadata.
- *
- * Note: It is important that your class populates $this->attributes with keys for all base attributes, anything
- * not in their gets set as METADATA.
- *
- * Q: Why are we not using __set overload here?
- * A: Because overload operators cause problems during subclassing, so we put the code here and
- * create overloads in subclasses.
- *
- * @param string $name
- * @param mixed $value
- */
- public function set($name, $value) {
- if (array_key_exists($name, $this->attributes)) {
- // Certain properties should not be manually changed!
- switch ($name) {
- case 'guid':
- case 'time_created':
- case 'time_updated':
- case 'last_action':
- return FALSE;
- break;
- default:
- $this->attributes[$name] = $value;
- break;
- }
- } else {
- return $this->setMetaData($name, $value);
- }
-
- return TRUE;
- }
-
- /**
- * Get a given piece of metadata.
- *
- * @param string $name
- */
- public function getMetaData($name) {
- if ((int) ($this->guid) > 0) {
- $md = get_metadata_byname($this->getGUID(), $name);
- } else {
- if (isset($this->temp_metadata[$name])) {
- return $this->temp_metadata[$name];
- }
- }
-
- if ($md && !is_array($md)) {
- return $md->value;
- } else if ($md && is_array($md)) {
- return metadata_array_to_values($md);
- }
-
- return null;
- }
-
- /**
- * Class member get overloading
- *
- * @param string $name
- * @return mixed
- */
- function __get($name) {
- return $this->get($name);
- }
-
- /**
- * Class member set overloading
- *
- * @param string $name
- * @param mixed $value
- * @return mixed
- */
- function __set($name, $value) {
- return $this->set($name, $value);
- }
-
- /**
- * Supporting isset.
- *
- * @param string $name The name of the attribute or metadata.
- * @return bool
- */
- function __isset($name) {
- return $this->$name !== NULL;
- }
-
- /**
- * Supporting unsetting of magic attributes.
- *
- * @param string $name The name of the attribute or metadata.
- */
- function __unset($name) {
- if (array_key_exists($name, $this->attributes)) {
- $this->attributes[$name] = "";
- }
- else {
- $this->clearMetaData($name);
- }
- }
-
- /**
- * Set a piece of metadata.
- *
- * @param string $name Name of the metadata
- * @param mixed $value Value of the metadata
- * @param string $value_type Types supported: integer and string. Will auto-identify if not set
- * @param bool $multiple (does not support associative arrays)
- * @return bool
- */
- public function setMetaData($name, $value, $value_type = "", $multiple = false) {
- if (is_array($value)) {
- unset($this->temp_metadata[$name]);
- remove_metadata($this->getGUID(), $name);
- foreach ($value as $v) {
- if ((int) $this->guid > 0) {
- $multiple = true;
- if (!create_metadata($this->getGUID(), $name, $v, $value_type,
- $this->getOwner(), $this->getAccessID(), $multiple)) {
- return false;
- }
- } else {
- if (($multiple) && (isset($this->temp_metadata[$name]))) {
- if (!is_array($this->temp_metadata[$name])) {
- $tmp = $this->temp_metadata[$name];
- $this->temp_metadata[$name] = array();
- $this->temp_metadata[$name][] = $tmp;
- }
-
- $this->temp_metadata[$name][] = $value;
- }
- else {
- $this->temp_metadata[$name] = $value;
- }
- }
- }
-
- return true;
- } else {
- unset($this->temp_metadata[$name]);
- if ((int) $this->guid > 0) {
- $result = create_metadata($this->getGUID(), $name, $value, $value_type, $this->getOwner(), $this->getAccessID(), $multiple);
- return (bool)$result;
- } else {
- if (($multiple) && (isset($this->temp_metadata[$name]))) {
- if (!is_array($this->temp_metadata[$name])) {
- $tmp = $this->temp_metadata[$name];
- $this->temp_metadata[$name] = array();
- $this->temp_metadata[$name][] = $tmp;
- }
-
- $this->temp_metadata[$name][] = $value;
- }
- else {
- $this->temp_metadata[$name] = $value;
- }
-
- return true;
- }
- }
- }
-
- /**
- * Clear metadata.
- */
- public function clearMetaData($name = "") {
- if (empty($name)) {
- return clear_metadata($this->getGUID());
- } else {
- return remove_metadata($this->getGUID(),$name);
- }
- }
-
-
- /**
- * Get a piece of volatile (non-persisted) data on this entity
- */
- public function getVolatileData($name) {
- if (!is_array($this->volatile)) {
- $this->volatile = array();
- }
-
- if (array_key_exists($name, $this->volatile)) {
- return $this->volatile[$name];
- } else {
- return NULL;
- }
- }
-
-
- /**
- * Set a piece of volatile (non-persisted) data on this entity
- */
- public function setVolatileData($name, $value) {
- if (!is_array($this->volatile)) {
- $this->volatile = array();
- }
-
- $this->volatile[$name] = $value;
- }
-
-
- /**
- * Remove all entities associated with this entity
- *
- * @return true
- */
- public function clearRelationships() {
- remove_entity_relationships($this->getGUID());
- remove_entity_relationships($this->getGUID(),"",true);
- return true;
- }
-
- /**
- * Add a relationship.
- *
- * @param int $guid Relationship to link to.
- * @param string $relationship The type of relationship.
- * @return bool
- */
- public function addRelationship($guid, $relationship) {
- return add_entity_relationship($this->getGUID(), $relationship, $guid);
- }
-
- /**
- * Remove a relationship
- *
- * @param int $guid
- * @param str $relationship
- * @return bool
- */
- public function removeRelationship($guid, $relationship) {
- return remove_entity_relationship($this->getGUID(), $relationship, $guid);
- }
-
- /**
- * Adds a private setting to this entity.
- *
- * @param $name
- * @param $value
- * @return unknown_type
- */
- function setPrivateSetting($name, $value) {
- return set_private_setting($this->getGUID(), $name, $value);
- }
-
- /**
- * Gets private setting for this entity
- *
- * @param $name
- * @return unknown_type
- */
- function getPrivateSetting($name) {
- return get_private_setting($this->getGUID(), $name);
- }
-
- /**
- * Removes private setting for this entity.
- *
- * @param $name
- * @return unknown_type
- */
- function removePrivateSetting($name) {
- return remove_private_setting($this->getGUID(), $name);
- }
-
- /**
- * Adds an annotation to an entity. By default, the type is detected automatically; however,
- * it can also be set. Note that by default, annotations are private.
- *
- * @param string $name
- * @param mixed $value
- * @param int $access_id
- * @param int $owner_id
- * @param string $vartype
- */
- function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_id = 0, $vartype = "") {
- if ((int) $this->guid > 0) {
- return create_annotation($this->getGUID(), $name, $value, $vartype, $owner_id, $access_id);
- } else {
- $this->temp_annotations[$name] = $value;
- }
- return true;
- }
-
- /**
- * Get the annotations for an entity.
- *
- * @param string $name
- * @param int $limit
- * @param int $offset
- * @param string $order
- */
- function getAnnotations($name, $limit = 50, $offset = 0, $order="asc") {
- if ((int) ($this->guid) > 0) {
- return get_annotations($this->getGUID(), "", "", $name, "", 0, $limit, $offset, $order);
- } else {
- return $this->temp_annotations[$name];
- }
- }
-
- /**
- * Remove all annotations or all annotations for this entity.
- *
- * @param string $name
- */
- function clearAnnotations($name = "") {
- return clear_annotations($this->getGUID(), $name);
- }
-
- /**
- * Return the annotations for the entity.
- *
- * @param string $name The type of annotation.
- */
- function countAnnotations($name = "") {
- return count_annotations($this->getGUID(), "", "", $name);
- }
-
- /**
- * Get the average of an integer type annotation.
- *
- * @param string $name
- */
- function getAnnotationsAvg($name) {
- return get_annotations_avg($this->getGUID(), "", "", $name);
- }
-
- /**
- * Get the sum of integer type annotations of a given name.
- *
- * @param string $name
- */
- function getAnnotationsSum($name) {
- return get_annotations_sum($this->getGUID(), "", "", $name);
- }
-
- /**
- * Get the minimum of integer type annotations of given name.
- *
- * @param string $name
- */
- function getAnnotationsMin($name) {
- return get_annotations_min($this->getGUID(), "", "", $name);
- }
-
- /**
- * Get the maximum of integer type annotations of a given name.
- *
- * @param string $name
- */
- function getAnnotationsMax($name) {
- return get_annotations_max($this->getGUID(), "", "", $name);
- }
-
- /**
- * Gets an array of entities from a specific relationship type
- *
- * @param string $relationship Relationship type (eg "friends")
- * @param true|false $inverse Is this an inverse relationship?
- * @param int $limit Number of elements to return
- * @param int $offset Indexing offset
- * @return array|false An array of entities or false on failure
- */
- function getEntitiesFromRelationship($relationship, $inverse = false, $limit = 50, $offset = 0) {
- return elgg_get_entities_from_relationship(array(
- 'relationship' => $relationship,
- 'relationship_guid' => $this->getGUID(),
- 'inverse_relationship' => $inverse,
- 'limit' => $limit,
- 'offset' => $offset
- ));
- }
-
- /**
- * Gets the number of of entities from a specific relationship type
- *
- * @param string $relationship Relationship type (eg "friends")
- * @param bool $inverse_relationship
- * @return int|false The number of entities or false on failure
- */
- function countEntitiesFromRelationship($relationship, $inverse_relationship = FALSE) {
- return elgg_get_entities_from_relationship(array(
- 'relationship' => $relationship,
- 'relationship_guid' => $this->getGUID(),
- 'inverse_relationship' => $inverse_relationship,
- 'count' => TRUE
- ));
- }
-
- /**
- * Determines whether or not the specified user (by default the current one) can edit the entity
- *
- * @param int $user_guid The user GUID, optionally (defaults to the currently logged in user)
- * @return true|false
- */
- function canEdit($user_guid = 0) {
- return can_edit_entity($this->getGUID(), $user_guid);
- }
-
- /**
- * Determines whether or not the specified user (by default the current one) can edit metadata on the entity
- *
- * @param ElggMetadata $metadata The piece of metadata to specifically check
- * @param int $user_guid The user GUID, optionally (defaults to the currently logged in user)
- * @return true|false
- */
- function canEditMetadata($metadata = null, $user_guid = 0) {
- return can_edit_entity_metadata($this->getGUID(), $user_guid, $metadata);
- }
-
- /**
- * Returns whether the given user (or current user) has the ability to write to this container.
- *
- * @param int $user_guid The user.
- * @return bool
- */
- public function canWriteToContainer($user_guid = 0) {
- return can_write_to_container($user_guid, $this->getGUID());
- }
-
- /**
- * Obtain this entity's access ID
- *
- * @return int The access ID
- */
- public function getAccessID() {
- return $this->get('access_id');
- }
-
- /**
- * Obtain this entity's GUID
- *
- * @return int GUID
- */
- public function getGUID() {
- return $this->get('guid');
- }
-
- /**
- * Get the owner of this entity
- *
- * @return int The owner GUID
- */
- public function getOwner() {
- return $this->get('owner_guid');
- }
-
- /**
- * Returns the actual entity of the user who owns this entity, if any
- *
- * @return ElggEntity The owning user
- */
- public function getOwnerEntity() {
- return get_entity($this->get('owner_guid'));
- }
-
- /**
- * Gets the type of entity this is
- *
- * @return string Entity type
- */
- public function getType() {
- return $this->get('type');
- }
-
- /**
- * Returns the subtype of this entity
- *
- * @return string The entity subtype
- */
- public function getSubtype() {
- // If this object hasn't been saved, then return the subtype string.
- if (!((int) $this->guid > 0)) {
- return $this->get('subtype');
- }
-
- return get_subtype_from_id($this->get('subtype'));
- }
-
- /**
- * Gets the UNIX epoch time that this entity was created
- *
- * @return int UNIX epoch time
- */
- public function getTimeCreated() {
- return $this->get('time_created');
- }
-
- /**
- * Gets the UNIX epoch time that this entity was last updated
- *
- * @return int UNIX epoch time
- */
- public function getTimeUpdated() {
- return $this->get('time_updated');
- }
-
- /**
- * Gets the display URL for this entity
- *
- * @return string The URL
- */
- public function getURL() {
- if (!empty($this->url_override)) {
- return $this->url_override;
- }
- return get_entity_url($this->getGUID());
- }
-
- /**
- * Overrides the URL returned by getURL
- *
- * @param string $url The new item URL
- * @return string The URL
- */
- public function setURL($url) {
- $this->url_override = $url;
- return $url;
- }
-
- /**
- * Return a url for the entity's icon, trying multiple alternatives.
- *
- * @param string $size Either 'large','medium','small' or 'tiny'
- * @return string The url or false if no url could be worked out.
- */
- public function getIcon($size = 'medium') {
- if (isset($this->icon_override[$size])) {
- return $this->icon_override[$size];
- }
- return get_entity_icon_url($this, $size);
- }
-
- /**
- * Set an icon override for an icon and size.
- *
- * @param string $url The url of the icon.
- * @param string $size The size its for.
- * @return bool
- */
- public function setIcon($url, $size = 'medium') {
- $url = sanitise_string($url);
- $size = sanitise_string($size);
-
- if (!$this->icon_override) {
- $this->icon_override = array();
- }
- $this->icon_override[$size] = $url;
-
- return true;
- }
-
- /**
- * Tests to see whether the object has been fully loaded.
- *
- * @return bool
- */
- public function isFullyLoaded() {
- return ! ($this->attributes['tables_loaded'] < $this->attributes['tables_split']);
- }
-
- /**
- * Save generic attributes to the entities table.
- */
- public function save() {
- $guid = (int) $this->guid;
- if ($guid > 0) {
- cache_entity($this);
-
- return update_entity(
- $this->get('guid'),
- $this->get('owner_guid'),
- $this->get('access_id'),
- $this->get('container_guid')
- );
- } else {
- // Create a new entity (nb: using attribute array directly 'cos set function does something special!)
- $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']);
- if (!$this->attributes['guid']) {
- throw new IOException(elgg_echo('IOException:BaseEntitySaveFailed'));
- }
-
- // Save any unsaved metadata
- // @todo How to capture extra information (access id etc)
- if (sizeof($this->temp_metadata) > 0) {
- foreach($this->temp_metadata as $name => $value) {
- $this->$name = $value;
- unset($this->temp_metadata[$name]);
- }
- }
-
- // Save any unsaved annotations metadata.
- // @todo How to capture extra information (access id etc)
- if (sizeof($this->temp_annotations) > 0) {
- foreach($this->temp_annotations as $name => $value) {
- $this->annotate($name, $value);
- unset($this->temp_annotations[$name]);
- }
- }
-
- // set the subtype to id now rather than a string
- $this->attributes['subtype'] = get_subtype_id($this->attributes['type'], $this->attributes['subtype']);
-
- // Cache object handle
- if ($this->attributes['guid']) {
- cache_entity($this);
- }
-
- return $this->attributes['guid'];
- }
- }
-
- /**
- * Load the basic entity information and populate base attributes array.
- *
- * @param int $guid
- */
- protected function load($guid) {
- $row = get_entity_as_row($guid);
-
- if ($row) {
- // Create the array if necessary - all subclasses should test before creating
- if (!is_array($this->attributes)) {
- $this->attributes = array();
- }
-
- // Now put these into the attributes array as core values
- $objarray = (array) $row;
- foreach($objarray as $key => $value) {
- $this->attributes[$key] = $value;
- }
-
- // Increment the portion counter
- if (!$this->isFullyLoaded()) {
- $this->attributes['tables_loaded']++;
- }
-
- // Cache object handle
- if ($this->attributes['guid']) {
- cache_entity($this);
- }
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Disable this entity.
- *
- * @param string $reason Optional reason
- * @param bool $recursive Recursively disable all contained entities?
- */
- public function disable($reason = "", $recursive = true) {
- return disable_entity($this->get('guid'), $reason, $recursive);
- }
-
- /**
- * Re-enable this entity.
- */
- public function enable() {
- return enable_entity($this->get('guid'));
- }
-
- /**
- * Is this entity enabled?
- *
- * @return boolean
- */
- public function isEnabled() {
- if ($this->enabled == 'yes') {
- return true;
- }
-
- return false;
- }
-
- /**
- * Delete this entity.
- */
- public function delete() {
- return delete_entity($this->get('guid'));
- }
-
- // LOCATABLE INTERFACE /////////////////////////////////////////////////////////////
-
- /** Interface to set the location */
- public function setLocation($location) {
- $location = sanitise_string($location);
-
- $this->location = $location;
-
- return true;
- }
-
- /**
- * Set latitude and longitude tags for a given entity.
- *
- * @param float $lat
- * @param float $long
- */
- public function setLatLong($lat, $long) {
- $lat = sanitise_string($lat);
- $long = sanitise_string($long);
-
- $this->set('geo:lat', $lat);
- $this->set('geo:long', $long);
-
- return true;
- }
-
- /**
- * Get the contents of the ->geo:lat field.
- *
- */
- public function getLatitude() {
- return $this->get('geo:lat');
- }
-
- /**
- * Get the contents of the ->geo:lat field.
- *
- */
- public function getLongitude() {
- return $this->get('geo:long');
- }
-
- /**
- * Get the ->location metadata.
- *
- */
- public function getLocation() {
- return $this->get('location');
- }
-
- // NOTABLE INTERFACE ///////////////////////////////////////////////////////////////
-
- /**
- * Calendar functionality.
- * This function sets the time of an object on a calendar listing.
- *
- * @param int $hour If ommitted, now is assumed.
- * @param int $minute If ommitted, now is assumed.
- * @param int $second If ommitted, now is assumed.
- * @param int $day If ommitted, now is assumed.
- * @param int $month If ommitted, now is assumed.
- * @param int $year If ommitted, now is assumed.
- * @param int $duration Duration of event, remainder of the day is assumed.
- */
- public function setCalendarTimeAndDuration($hour = NULL, $minute = NULL, $second = NULL, $day = NULL, $month = NULL, $year = NULL, $duration = NULL) {
- $start = mktime($hour, $minute, $second, $month, $day, $year);
- $end = $start + abs($duration);
- if (!$duration) {
- $end = get_day_end($day,$month,$year);
- }
-
- $this->calendar_start = $start;
- $this->calendar_end = $end;
-
- return true;
- }
-
- /**
- * Return the start timestamp.
- */
- public function getCalendarStartTime() {
- return (int)$this->calendar_start;
- }
-
- /**
- * Return the end timestamp.
- */
- public function getCalendarEndTime() {
- return (int)$this->calendar_end;
- }
-
- // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an array of fields which can be exported.
- */
- public function getExportableValues() {
- return array(
- 'guid',
- 'type',
- 'subtype',
- 'time_created',
- 'time_updated',
- 'container_guid',
- 'owner_guid',
- 'site_guid'
- );
- }
-
- /**
- * Export this class into an array of ODD Elements containing all necessary fields.
- * Override if you wish to return more information than can be found in $this->attributes (shouldn't happen)
- */
- public function export() {
- $tmp = array();
-
- // Generate uuid
- $uuid = guid_to_uuid($this->getGUID());
-
- // Create entity
- $odd = new ODDEntity(
- $uuid,
- $this->attributes['type'],
- get_subtype_from_id($this->attributes['subtype'])
- );
-
- $tmp[] = $odd;
-
- $exportable_values = $this->getExportableValues();
-
- // Now add its attributes
- foreach ($this->attributes as $k => $v) {
- $meta = NULL;
-
- if (in_array( $k, $exportable_values)) {
- switch ($k) {
- case 'guid' : // Dont use guid in OpenDD
- case 'type' : // Type and subtype already taken care of
- case 'subtype' :
- break;
-
- case 'time_created' : // Created = published
- $odd->setAttribute('published', date("r", $v));
- break;
-
- case 'site_guid' : // Container
- $k = 'site_uuid';
- $v = guid_to_uuid($v);
- $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
- break;
-
- case 'container_guid' : // Container
- $k = 'container_uuid';
- $v = guid_to_uuid($v);
- $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
- break;
-
- case 'owner_guid' : // Convert owner guid to uuid, this will be stored in metadata
- $k = 'owner_uuid';
- $v = guid_to_uuid($v);
- $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
- break;
-
- default :
- $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
- }
-
- // set the time of any metadata created
- if ($meta) {
- $meta->setAttribute('published', date("r",$this->time_created));
- $tmp[] = $meta;
- }
- }
- }
-
- // Now we do something a bit special.
- /*
- * This provides a rendered view of the entity to foreign sites.
- */
-
- elgg_set_viewtype('default');
- $view = elgg_view_entity($this, true);
- elgg_set_viewtype();
-
- $tmp[] = new ODDMetaData($uuid . "volatile/renderedentity/", $uuid, 'renderedentity', $view , 'volatile');
-
- return $tmp;
- }
-
- // IMPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Import data from an parsed xml data array.
- *
- * @param array $data
- * @param int $version
- */
- public function import(ODD $data) {
- if (!($data instanceof ODDEntity)) {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnexpectedODDClass'));
- }
-
- // Set type and subtype
- $this->attributes['type'] = $data->getAttribute('class');
- $this->attributes['subtype'] = $data->getAttribute('subclass');
-
- // Set owner
- $this->attributes['owner_guid'] = get_loggedin_userid(); // Import as belonging to importer.
-
- // Set time
- $this->attributes['time_created'] = strtotime($data->getAttribute('published'));
- $this->attributes['time_updated'] = time();
-
- return true;
- }
-
- // SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an identification for the object for storage in the system log.
- * This id must be an integer.
- *
- * @return int
- */
- public function getSystemLogID() {
- return $this->getGUID();
- }
-
- /**
- * Return the class name of the object.
- */
- public function getClassName() {
- return get_class($this);
- }
-
- /**
- * For a given ID, return the object associated with it.
- * This is used by the river functionality primarily.
- * This is useful for checking access permissions etc on objects.
- */
- public function getObjectFromID($id) {
- return get_entity($id);
- }
-
- /**
- * Return the GUID of the owner of this object.
- */
- public function getObjectOwnerGUID() {
- return $this->owner_guid;
- }
-
- /**
- * Returns tags for this entity.
- *
- * @param array $tag_names Optionally restrict by tag metadata names.
- * @return array
- */
- public function getTags($tag_names = NULL) {
- global $CONFIG;
-
- if ($tag_names && !is_array($tag_names)) {
- $tag_names = array($tag_names);
- }
-
- $valid_tags = elgg_get_registered_tag_metadata_names();
- $entity_tags = array();
-
- foreach ($valid_tags as $tag_name) {
- if (is_array($tag_names) && !in_array($tag_name, $tag_names)) {
- continue;
- }
-
- if ($tags = $this->$tag_name) {
- // if a single tag, metadata returns a string.
- // if multiple tags, metadata returns an array.
- if (is_array($tags)) {
- $entity_tags = array_merge($entity_tags, $tags);
- } else {
- $entity_tags[] = $tags;
- }
- }
- }
-
- return $entity_tags;
- }
-
- // ITERATOR INTERFACE //////////////////////////////////////////////////////////////
- /*
- * This lets an entity's attributes be displayed using foreach as a normal array.
- * Example: http://www.sitepoint.com/print/php5-standard-library
- */
-
- private $valid = FALSE;
-
- function rewind() {
- $this->valid = (FALSE !== reset($this->attributes));
- }
-
- function current() {
- return current($this->attributes);
- }
-
- function key() {
- return key($this->attributes);
- }
-
- function next() {
- $this->valid = (FALSE !== next($this->attributes));
- }
-
- function valid() {
- return $this->valid;
- }
-
- // ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
- /*
- * This lets an entity's attributes be accessed like an associative array.
- * Example: http://www.sitepoint.com/print/php5-standard-library
- */
-
- function offsetSet($key, $value) {
- if ( array_key_exists($key, $this->attributes) ) {
- $this->attributes[$key] = $value;
- }
- }
-
- function offsetGet($key) {
- if ( array_key_exists($key, $this->attributes) ) {
- return $this->attributes[$key];
- }
- }
-
- function offsetUnset($key) {
- if ( array_key_exists($key, $this->attributes) ) {
- $this->attributes[$key] = ""; // Full unsetting is dangerious for our objects
- }
- }
-
- function offsetExists($offset) {
- return array_key_exists($offset, $this->attributes);
- }
-} \ No newline at end of file
+<?php
+/**
+ * The parent class for all Elgg Entities.
+ *
+ * An ElggEntity is one of the basic data models in Elgg. It is the primary
+ * means of storing and retrieving data from the database. An ElggEntity
+ * represents one row of the entities table.
+ *
+ * The ElggEntity class handles CRUD operations for the entities table.
+ * ElggEntity should always be extended by another class to handle CRUD
+ * operations on the type-specific table.
+ *
+ * ElggEntity uses magic methods for get and set, so any property that isn't
+ * declared will be assumed to be metadata and written to the database
+ * as metadata on the object. All children classes must declare which
+ * properties are columns of the type table or they will be assumed
+ * to be metadata. See ElggObject::initialise_entities() for examples.
+ *
+ * Core supports 4 types of entities: ElggObject, ElggUser, ElggGroup, and
+ * ElggSite.
+ *
+ * @tip Most plugin authors will want to extend the ElggObject class
+ * instead of this class.
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel.Entities
+ *
+ * @property string $type object, user, group, or site (read-only after save)
+ * @property string $subtype Further clarifies the nature of the entity (read-only after save)
+ * @property int $guid The unique identifier for this entity (read only)
+ * @property int $owner_guid The GUID of the creator of this entity
+ * @property int $container_guid The GUID of the entity containing this entity
+ * @property int $site_guid The GUID of the website this entity is associated with
+ * @property int $access_id Specifies the visibility level of this entity
+ * @property int $time_created A UNIX timestamp of when the entity was created (read-only, set on first save)
+ * @property int $time_updated A UNIX timestamp of when the entity was last updated (automatically updated on save)
+ * @property-read string $enabled
+ */
+abstract class ElggEntity extends ElggData implements
+ Notable, // Calendar interface
+ Locatable, // Geocoding interface
+ Importable // Allow import of data
+{
+
+ /**
+ * If set, overrides the value of getURL()
+ */
+ protected $url_override;
+
+ /**
+ * Icon override, overrides the value of getIcon().
+ */
+ protected $icon_override;
+
+ /**
+ * Holds metadata until entity is saved. Once the entity is saved,
+ * metadata are written immediately to the database.
+ */
+ protected $temp_metadata = array();
+
+ /**
+ * Holds annotations until entity is saved. Once the entity is saved,
+ * annotations are written immediately to the database.
+ */
+ protected $temp_annotations = array();
+
+ /**
+ * Holds private settings until entity is saved. Once the entity is saved,
+ * private settings are written immediately to the database.
+ */
+ protected $temp_private_settings = array();
+
+ /**
+ * Volatile data structure for this object, allows for storage of data
+ * in-memory that isn't sync'd back to the metadata table.
+ */
+ protected $volatile = array();
+
+ /**
+ * Initialize the attributes array.
+ *
+ * This is vital to distinguish between metadata and base parameters.
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['guid'] = NULL;
+ $this->attributes['type'] = NULL;
+ $this->attributes['subtype'] = NULL;
+
+ $this->attributes['owner_guid'] = elgg_get_logged_in_user_guid();
+ $this->attributes['container_guid'] = elgg_get_logged_in_user_guid();
+
+ $this->attributes['site_guid'] = NULL;
+ $this->attributes['access_id'] = ACCESS_PRIVATE;
+ $this->attributes['time_created'] = NULL;
+ $this->attributes['time_updated'] = NULL;
+ $this->attributes['last_action'] = NULL;
+ $this->attributes['enabled'] = "yes";
+
+ // There now follows a bit of a hack
+ /* Problem: To speed things up, some objects are split over several tables,
+ * this means that it requires n number of database reads to fully populate
+ * an entity. This causes problems for caching and create events
+ * since it is not possible to tell whether a subclassed entity is complete.
+ *
+ * Solution: We have two counters, one 'tables_split' which tells whatever is
+ * interested how many tables are going to need to be searched in order to fully
+ * populate this object, and 'tables_loaded' which is how many have been
+ * loaded thus far.
+ *
+ * If the two are the same then this object is complete.
+ *
+ * Use: isFullyLoaded() to check
+ */
+ $this->attributes['tables_split'] = 1;
+ $this->attributes['tables_loaded'] = 0;
+ }
+
+ /**
+ * Clone an entity
+ *
+ * Resets the guid so that the entity can be saved as a distinct entity from
+ * the original. Creation time will be set when this new entity is saved.
+ * The owner and container guids come from the original entity. The clone
+ * method copies metadata but does not copy annotations or private settings.
+ *
+ * @note metadata will have its owner and access id set when the entity is saved
+ * and it will be the same as that of the entity.
+ *
+ * @return void
+ */
+ public function __clone() {
+ $orig_entity = get_entity($this->guid);
+ if (!$orig_entity) {
+ elgg_log("Failed to clone entity with GUID $this->guid", "ERROR");
+ return;
+ }
+
+ $metadata_array = elgg_get_metadata(array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ ));
+
+ $this->attributes['guid'] = "";
+
+ $this->attributes['subtype'] = $orig_entity->getSubtype();
+
+ // copy metadata over to new entity - slightly convoluted due to
+ // handling of metadata arrays
+ if (is_array($metadata_array)) {
+ // create list of metadata names
+ $metadata_names = array();
+ foreach ($metadata_array as $metadata) {
+ $metadata_names[] = $metadata['name'];
+ }
+ // arrays are stored with multiple enties per name
+ $metadata_names = array_unique($metadata_names);
+
+ // move the metadata over
+ foreach ($metadata_names as $name) {
+ $this->set($name, $orig_entity->$name);
+ }
+ }
+ }
+
+ /**
+ * Return the value of a property.
+ *
+ * If $name is defined in $this->attributes that value is returned, otherwise it will
+ * pull from the entity's metadata.
+ *
+ * Q: Why are we not using __get overload here?
+ * A: Because overload operators cause problems during subclassing, so we put the code here and
+ * create overloads in subclasses.
+ *
+ * @todo What problems are these?
+ *
+ * @warning Subtype is returned as an id rather than the subtype string. Use getSubtype()
+ * to get the subtype string.
+ *
+ * @param string $name Name
+ *
+ * @return mixed Returns the value of a given value, or null.
+ */
+ public function get($name) {
+ // See if its in our base attributes
+ if (array_key_exists($name, $this->attributes)) {
+ return $this->attributes[$name];
+ }
+
+ // No, so see if its in the meta data for this entity
+ $meta = $this->getMetaData($name);
+
+ // getMetaData returns NULL if $name is not found
+ return $meta;
+ }
+
+ /**
+ * Sets the value of a property.
+ *
+ * If $name is defined in $this->attributes that value is set, otherwise it is
+ * saved as metadata.
+ *
+ * @warning Metadata set this way will inherit the entity's owner and access ID. If you want
+ * to set metadata with a different owner, use create_metadata().
+ *
+ * @warning It is important that your class populates $this->attributes with keys
+ * for all base attributes, anything not in their gets set as METADATA.
+ *
+ * Q: Why are we not using __set overload here?
+ * A: Because overload operators cause problems during subclassing, so we put the code here and
+ * create overloads in subclasses.
+ *
+ * @todo What problems?
+ *
+ * @param string $name Name
+ * @param mixed $value Value
+ *
+ * @return bool
+ */
+ public function set($name, $value) {
+ if (array_key_exists($name, $this->attributes)) {
+ // Certain properties should not be manually changed!
+ switch ($name) {
+ case 'guid':
+ case 'time_updated':
+ case 'last_action':
+ return FALSE;
+ break;
+ default:
+ $this->attributes[$name] = $value;
+ break;
+ }
+ } else {
+ return $this->setMetaData($name, $value);
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Return the value of a piece of metadata.
+ *
+ * @param string $name Name
+ *
+ * @return mixed The value, or NULL if not found.
+ */
+ public function getMetaData($name) {
+ $guid = $this->getGUID();
+
+ if (! $guid) {
+ if (isset($this->temp_metadata[$name])) {
+ // md is returned as an array only if more than 1 entry
+ if (count($this->temp_metadata[$name]) == 1) {
+ return $this->temp_metadata[$name][0];
+ } else {
+ return $this->temp_metadata[$name];
+ }
+ } else {
+ return null;
+ }
+ }
+
+ // upon first cache miss, just load/cache all the metadata and retry.
+ // if this works, the rest of this function may not be needed!
+ $cache = elgg_get_metadata_cache();
+ if ($cache->isKnown($guid, $name)) {
+ return $cache->load($guid, $name);
+ } else {
+ $cache->populateFromEntities(array($guid));
+ // in case ignore_access was on, we have to check again...
+ if ($cache->isKnown($guid, $name)) {
+ return $cache->load($guid, $name);
+ }
+ }
+
+ $md = elgg_get_metadata(array(
+ 'guid' => $guid,
+ 'metadata_name' => $name,
+ 'limit' => 0,
+ ));
+
+ $value = null;
+
+ if ($md && !is_array($md)) {
+ $value = $md->value;
+ } elseif (count($md) == 1) {
+ $value = $md[0]->value;
+ } else if ($md && is_array($md)) {
+ $value = metadata_array_to_values($md);
+ }
+
+ $cache->save($guid, $name, $value);
+
+ return $value;
+ }
+
+ /**
+ * Unset a property from metadata or attribute.
+ *
+ * @warning If you use this to unset an attribute, you must save the object!
+ *
+ * @param string $name The name of the attribute or metadata.
+ *
+ * @return void
+ */
+ function __unset($name) {
+ if (array_key_exists($name, $this->attributes)) {
+ $this->attributes[$name] = "";
+ } else {
+ $this->deleteMetadata($name);
+ }
+ }
+
+ /**
+ * Set a piece of metadata.
+ *
+ * Plugin authors should use the magic methods or create_metadata().
+ *
+ * @warning The metadata will inherit the parent entity's owner and access ID.
+ * If you want to write metadata with a different owner, use create_metadata().
+ *
+ * @access private
+ *
+ * @param string $name Name of the metadata
+ * @param mixed $value Value of the metadata (doesn't support assoc arrays)
+ * @param string $value_type Types supported: integer and string. Will auto-identify if not set
+ * @param bool $multiple Allow multiple values for a single name (doesn't support assoc arrays)
+ *
+ * @return bool
+ */
+ public function setMetaData($name, $value, $value_type = null, $multiple = false) {
+
+ // normalize value to an array that we will loop over
+ // remove indexes if value already an array.
+ if (is_array($value)) {
+ $value = array_values($value);
+ } else {
+ $value = array($value);
+ }
+
+ // saved entity. persist md to db.
+ if ($this->guid) {
+ // if overwriting, delete first.
+ if (!$multiple) {
+ $options = array(
+ 'guid' => $this->getGUID(),
+ 'metadata_name' => $name,
+ 'limit' => 0
+ );
+ // @todo in 1.9 make this return false if can't add metadata
+ // https://github.com/elgg/elgg/issues/4520
+ //
+ // need to remove access restrictions right now to delete
+ // because this is the expected behavior
+ $ia = elgg_set_ignore_access(true);
+ if (false === elgg_delete_metadata($options)) {
+ return false;
+ }
+ elgg_set_ignore_access($ia);
+ }
+
+ // add new md
+ $result = true;
+ foreach ($value as $value_tmp) {
+ // at this point $value should be appended because it was cleared above if needed.
+ $md_id = create_metadata($this->getGUID(), $name, $value_tmp, $value_type,
+ $this->getOwnerGUID(), $this->getAccessId(), true);
+ if (!$md_id) {
+ return false;
+ }
+ }
+
+ return $result;
+ } else {
+ // unsaved entity. store in temp array
+ // returning single entries instead of an array of 1 element is decided in
+ // getMetaData(), just like pulling from the db.
+ //
+ // if overwrite, delete first
+ if (!$multiple || !isset($this->temp_metadata[$name])) {
+ $this->temp_metadata[$name] = array();
+ }
+
+ // add new md
+ $this->temp_metadata[$name] = array_merge($this->temp_metadata[$name], $value);
+ return true;
+ }
+ }
+
+ /**
+ * Deletes all metadata on this object (metadata.entity_guid = $this->guid).
+ * If you pass a name, only metadata matching that name will be deleted.
+ *
+ * @warning Calling this with no $name will clear all metadata on the entity.
+ *
+ * @param null|string $name The name of the metadata to remove.
+ * @return bool
+ * @since 1.8
+ */
+ public function deleteMetadata($name = null) {
+
+ if (!$this->guid) {
+ return false;
+ }
+
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['metadata_name'] = $name;
+ }
+
+ return elgg_delete_metadata($options);
+ }
+
+ /**
+ * Deletes all metadata owned by this object (metadata.owner_guid = $this->guid).
+ * If you pass a name, only metadata matching that name will be deleted.
+ *
+ * @param null|string $name The name of metadata to delete.
+ * @return bool
+ * @since 1.8
+ */
+ public function deleteOwnedMetadata($name = null) {
+ // access is turned off for this because they might
+ // no longer have access to an entity they created metadata on.
+ $ia = elgg_set_ignore_access(true);
+ $options = array(
+ 'metadata_owner_guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['metadata_name'] = $name;
+ }
+
+ $r = elgg_delete_metadata($options);
+ elgg_set_ignore_access($ia);
+ return $r;
+ }
+
+ /**
+ * Remove metadata
+ *
+ * @warning Calling this with no or empty arguments will clear all metadata on the entity.
+ *
+ * @param string $name The name of the metadata to clear
+ * @return mixed bool
+ * @deprecated 1.8 Use deleteMetadata()
+ */
+ public function clearMetaData($name = '') {
+ elgg_deprecated_notice('ElggEntity->clearMetadata() is deprecated by ->deleteMetadata()', 1.8);
+ return $this->deleteMetadata($name);
+ }
+
+ /**
+ * Disables metadata for this entity, optionally based on name.
+ *
+ * @param string $name An options name of metadata to disable.
+ * @return bool
+ * @since 1.8
+ */
+ public function disableMetadata($name = '') {
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['metadata_name'] = $name;
+ }
+
+ return elgg_disable_metadata($options);
+ }
+
+ /**
+ * Enables metadata for this entity, optionally based on name.
+ *
+ * @warning Before calling this, you must use {@link access_show_hidden_entities()}
+ *
+ * @param string $name An options name of metadata to enable.
+ * @return bool
+ * @since 1.8
+ */
+ public function enableMetadata($name = '') {
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['metadata_name'] = $name;
+ }
+
+ return elgg_enable_metadata($options);
+ }
+
+ /**
+ * Get a piece of volatile (non-persisted) data on this entity.
+ *
+ * @param string $name The name of the volatile data
+ *
+ * @return mixed The value or NULL if not found.
+ */
+ public function getVolatileData($name) {
+ if (!is_array($this->volatile)) {
+ $this->volatile = array();
+ }
+
+ if (array_key_exists($name, $this->volatile)) {
+ return $this->volatile[$name];
+ } else {
+ return NULL;
+ }
+ }
+
+ /**
+ * Set a piece of volatile (non-persisted) data on this entity
+ *
+ * @param string $name Name
+ * @param mixed $value Value
+ *
+ * @return void
+ */
+ public function setVolatileData($name, $value) {
+ if (!is_array($this->volatile)) {
+ $this->volatile = array();
+ }
+
+ $this->volatile[$name] = $value;
+ }
+
+ /**
+ * Remove all relationships to and from this entity.
+ *
+ * @return true
+ * @todo This should actually return if it worked.
+ * @see ElggEntity::addRelationship()
+ * @see ElggEntity::removeRelationship()
+ */
+ public function deleteRelationships() {
+ remove_entity_relationships($this->getGUID());
+ remove_entity_relationships($this->getGUID(), "", true);
+ return true;
+ }
+
+ /**
+ * Remove all relationships to and from this entity.
+ *
+ * @return bool
+ * @see ElggEntity::addRelationship()
+ * @see ElggEntity::removeRelationship()
+ * @deprecated 1.8 Use ->deleteRelationship()
+ */
+ public function clearRelationships() {
+ elgg_deprecated_notice('ElggEntity->clearRelationships() is deprecated by ->deleteRelationships()', 1.8);
+ return $this->deleteRelationships();
+ }
+
+ /**
+ * Add a relationship between this an another entity.
+ *
+ * @tip Read the relationship like "$guid is a $relationship of this entity."
+ *
+ * @param int $guid Entity to link to.
+ * @param string $relationship The type of relationship.
+ *
+ * @return bool
+ * @see ElggEntity::removeRelationship()
+ * @see ElggEntity::clearRelationships()
+ */
+ public function addRelationship($guid, $relationship) {
+ return add_entity_relationship($this->getGUID(), $relationship, $guid);
+ }
+
+ /**
+ * Remove a relationship
+ *
+ * @param int $guid GUID of the entity to make a relationship with
+ * @param str $relationship Name of relationship
+ *
+ * @return bool
+ * @see ElggEntity::addRelationship()
+ * @see ElggEntity::clearRelationships()
+ */
+ public function removeRelationship($guid, $relationship) {
+ return remove_entity_relationship($this->getGUID(), $relationship, $guid);
+ }
+
+ /**
+ * Adds a private setting to this entity.
+ *
+ * Private settings are similar to metadata but will not
+ * be searched and there are fewer helper functions for them.
+ *
+ * @param string $name Name of private setting
+ * @param mixed $value Value of private setting
+ *
+ * @return bool
+ */
+ function setPrivateSetting($name, $value) {
+ if ((int) $this->guid > 0) {
+ return set_private_setting($this->getGUID(), $name, $value);
+ } else {
+ $this->temp_private_settings[$name] = $value;
+ return true;
+ }
+ }
+
+ /**
+ * Returns a private setting value
+ *
+ * @param string $name Name of the private setting
+ *
+ * @return mixed
+ */
+ function getPrivateSetting($name) {
+ if ((int) ($this->guid) > 0) {
+ return get_private_setting($this->getGUID(), $name);
+ } else {
+ if (isset($this->temp_private_settings[$name])) {
+ return $this->temp_private_settings[$name];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes private setting
+ *
+ * @param string $name Name of the private setting
+ *
+ * @return bool
+ */
+ function removePrivateSetting($name) {
+ return remove_private_setting($this->getGUID(), $name);
+ }
+
+ /**
+ * Deletes all annotations on this object (annotations.entity_guid = $this->guid).
+ * If you pass a name, only annotations matching that name will be deleted.
+ *
+ * @warning Calling this with no or empty arguments will clear all annotations on the entity.
+ *
+ * @param null|string $name The annotations name to remove.
+ * @return bool
+ * @since 1.8
+ */
+ public function deleteAnnotations($name = null) {
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ return elgg_delete_annotations($options);
+ }
+
+ /**
+ * Deletes all annotations owned by this object (annotations.owner_guid = $this->guid).
+ * If you pass a name, only annotations matching that name will be deleted.
+ *
+ * @param null|string $name The name of annotations to delete.
+ * @return bool
+ * @since 1.8
+ */
+ public function deleteOwnedAnnotations($name = null) {
+ // access is turned off for this because they might
+ // no longer have access to an entity they created annotations on.
+ $ia = elgg_set_ignore_access(true);
+ $options = array(
+ 'annotation_owner_guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ $r = elgg_delete_annotations($options);
+ elgg_set_ignore_access($ia);
+ return $r;
+ }
+
+ /**
+ * Disables annotations for this entity, optionally based on name.
+ *
+ * @param string $name An options name of annotations to disable.
+ * @return bool
+ * @since 1.8
+ */
+ public function disableAnnotations($name = '') {
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ return elgg_disable_annotations($options);
+ }
+
+ /**
+ * Enables annotations for this entity, optionally based on name.
+ *
+ * @warning Before calling this, you must use {@link access_show_hidden_entities()}
+ *
+ * @param string $name An options name of annotations to enable.
+ * @return bool
+ * @since 1.8
+ */
+ public function enableAnnotations($name = '') {
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ return elgg_enable_annotations($options);
+ }
+
+ /**
+ * Helper function to return annotation calculation results
+ *
+ * @param string $name The annotation name.
+ * @param string $calculation A valid MySQL function to run its values through
+ * @return mixed
+ */
+ private function getAnnotationCalculation($name, $calculation) {
+ $options = array(
+ 'guid' => $this->getGUID(),
+ 'annotation_name' => $name,
+ 'annotation_calculation' => $calculation
+ );
+
+ return elgg_get_annotations($options);
+ }
+
+ /**
+ * Adds an annotation to an entity.
+ *
+ * @warning By default, annotations are private.
+ *
+ * @warning Annotating an unsaved entity more than once with the same name
+ * will only save the last annotation.
+ *
+ * @param string $name Annotation name
+ * @param mixed $value Annotation value
+ * @param int $access_id Access ID
+ * @param int $owner_id GUID of the annotation owner
+ * @param string $vartype The type of annotation value
+ *
+ * @return bool
+ */
+ function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_id = 0, $vartype = "") {
+ if ((int) $this->guid > 0) {
+ return create_annotation($this->getGUID(), $name, $value, $vartype, $owner_id, $access_id);
+ } else {
+ $this->temp_annotations[$name] = $value;
+ }
+ return true;
+ }
+
+ /**
+ * Returns an array of annotations.
+ *
+ * @param string $name Annotation name
+ * @param int $limit Limit
+ * @param int $offset Offset
+ * @param string $order Order by time: asc or desc
+ *
+ * @return array
+ */
+ function getAnnotations($name, $limit = 50, $offset = 0, $order = "asc") {
+ if ((int) ($this->guid) > 0) {
+
+ $options = array(
+ 'guid' => $this->guid,
+ 'annotation_name' => $name,
+ 'limit' => $limit,
+ 'offset' => $offset,
+ );
+
+ if ($order != 'asc') {
+ $options['reverse_order_by'] = true;
+ }
+
+ return elgg_get_annotations($options);
+ } else if (isset($this->temp_annotations[$name])) {
+ return array($this->temp_annotations[$name]);
+ } else {
+ return array();
+ }
+ }
+
+ /**
+ * Remove an annotation or all annotations for this entity.
+ *
+ * @warning Calling this method with no or an empty argument will remove
+ * all annotations on the entity.
+ *
+ * @param string $name Annotation name
+ * @return bool
+ * @deprecated 1.8 Use ->deleteAnnotations()
+ */
+ function clearAnnotations($name = "") {
+ elgg_deprecated_notice('ElggEntity->clearAnnotations() is deprecated by ->deleteAnnotations()', 1.8);
+ return $this->deleteAnnotations($name);
+ }
+
+ /**
+ * Count annotations.
+ *
+ * @param string $name The type of annotation.
+ *
+ * @return int
+ */
+ function countAnnotations($name = "") {
+ return $this->getAnnotationCalculation($name, 'count');
+ }
+
+ /**
+ * Get the average of an integer type annotation.
+ *
+ * @param string $name Annotation name
+ *
+ * @return int
+ */
+ function getAnnotationsAvg($name) {
+ return $this->getAnnotationCalculation($name, 'avg');
+ }
+
+ /**
+ * Get the sum of integer type annotations of a given name.
+ *
+ * @param string $name Annotation name
+ *
+ * @return int
+ */
+ function getAnnotationsSum($name) {
+ return $this->getAnnotationCalculation($name, 'sum');
+ }
+
+ /**
+ * Get the minimum of integer type annotations of given name.
+ *
+ * @param string $name Annotation name
+ *
+ * @return int
+ */
+ function getAnnotationsMin($name) {
+ return $this->getAnnotationCalculation($name, 'min');
+ }
+
+ /**
+ * Get the maximum of integer type annotations of a given name.
+ *
+ * @param string $name Annotation name
+ *
+ * @return int
+ */
+ function getAnnotationsMax($name) {
+ return $this->getAnnotationCalculation($name, 'max');
+ }
+
+ /**
+ * Count the number of comments attached to this entity.
+ *
+ * @return int Number of comments
+ * @since 1.8.0
+ */
+ function countComments() {
+ $params = array('entity' => $this);
+ $num = elgg_trigger_plugin_hook('comments:count', $this->getType(), $params);
+
+ if (is_int($num)) {
+ return $num;
+ } else {
+ return $this->getAnnotationCalculation('generic_comment', 'count');
+ }
+ }
+
+ /**
+ * Gets an array of entities with a relationship to this entity.
+ *
+ * @param string $relationship Relationship type (eg "friends")
+ * @param bool $inverse Is this an inverse relationship?
+ * @param int $limit Number of elements to return
+ * @param int $offset Indexing offset
+ *
+ * @return array|false An array of entities or false on failure
+ */
+ function getEntitiesFromRelationship($relationship, $inverse = false, $limit = 50, $offset = 0) {
+ return elgg_get_entities_from_relationship(array(
+ 'relationship' => $relationship,
+ 'relationship_guid' => $this->getGUID(),
+ 'inverse_relationship' => $inverse,
+ 'limit' => $limit,
+ 'offset' => $offset
+ ));
+ }
+
+ /**
+ * Gets the number of of entities from a specific relationship type
+ *
+ * @param string $relationship Relationship type (eg "friends")
+ * @param bool $inverse_relationship Invert relationship
+ *
+ * @return int|false The number of entities or false on failure
+ */
+ function countEntitiesFromRelationship($relationship, $inverse_relationship = FALSE) {
+ return elgg_get_entities_from_relationship(array(
+ 'relationship' => $relationship,
+ 'relationship_guid' => $this->getGUID(),
+ 'inverse_relationship' => $inverse_relationship,
+ 'count' => TRUE
+ ));
+ }
+
+ /**
+ * Can a user edit this entity.
+ *
+ * @param int $user_guid The user GUID, optionally (default: logged in user)
+ *
+ * @return bool
+ */
+ function canEdit($user_guid = 0) {
+ return can_edit_entity($this->getGUID(), $user_guid);
+ }
+
+ /**
+ * Can a user edit metadata on this entity
+ *
+ * @param ElggMetadata $metadata The piece of metadata to specifically check
+ * @param int $user_guid The user GUID, optionally (default: logged in user)
+ *
+ * @return bool
+ */
+ function canEditMetadata($metadata = null, $user_guid = 0) {
+ return can_edit_entity_metadata($this->getGUID(), $user_guid, $metadata);
+ }
+
+ /**
+ * Can a user add an entity to this container
+ *
+ * @param int $user_guid The user.
+ * @param string $type The type of entity we're looking to write
+ * @param string $subtype The subtype of the entity we're looking to write
+ *
+ * @return bool
+ */
+ public function canWriteToContainer($user_guid = 0, $type = 'all', $subtype = 'all') {
+ return can_write_to_container($user_guid, $this->guid, $type, $subtype);
+ }
+
+ /**
+ * Can a user comment on an entity?
+ *
+ * @tip Can be overridden by registering for the permissions_check:comment,
+ * <entity type> plugin hook.
+ *
+ * @param int $user_guid User guid (default is logged in user)
+ *
+ * @return bool
+ */
+ public function canComment($user_guid = 0) {
+ if ($user_guid == 0) {
+ $user_guid = elgg_get_logged_in_user_guid();
+ }
+ $user = get_entity($user_guid);
+
+ // By default, we don't take a position of whether commenting is allowed
+ // because it is handled by the subclasses of ElggEntity
+ $params = array('entity' => $this, 'user' => $user);
+ return elgg_trigger_plugin_hook('permissions_check:comment', $this->type, $params, null);
+ }
+
+ /**
+ * Can a user annotate an entity?
+ *
+ * @tip Can be overridden by registering for the permissions_check:annotate,
+ * <entity type> plugin hook.
+ *
+ * @tip If you want logged out users to annotate an object, do not call
+ * canAnnotate(). It's easier than using the plugin hook.
+ *
+ * @param int $user_guid User guid (default is logged in user)
+ * @param string $annotation_name The name of the annotation (default is unspecified)
+ *
+ * @return bool
+ */
+ public function canAnnotate($user_guid = 0, $annotation_name = '') {
+ if ($user_guid == 0) {
+ $user_guid = elgg_get_logged_in_user_guid();
+ }
+ $user = get_entity($user_guid);
+
+ $return = true;
+ if (!$user) {
+ $return = false;
+ }
+
+ $params = array(
+ 'entity' => $this,
+ 'user' => $user,
+ 'annotation_name' => $annotation_name,
+ );
+ return elgg_trigger_plugin_hook('permissions_check:annotate', $this->type, $params, $return);
+ }
+
+ /**
+ * Returns the access_id.
+ *
+ * @return int The access ID
+ */
+ public function getAccessID() {
+ return $this->get('access_id');
+ }
+
+ /**
+ * Returns the guid.
+ *
+ * @return int|null GUID
+ */
+ public function getGUID() {
+ return $this->get('guid');
+ }
+
+ /**
+ * Returns the entity type
+ *
+ * @return string Entity type
+ */
+ public function getType() {
+ return $this->get('type');
+ }
+
+ /**
+ * Returns the entity subtype string
+ *
+ * @note This returns a string. If you want the id, use ElggEntity::subtype.
+ *
+ * @return string The entity subtype
+ */
+ public function getSubtype() {
+ // If this object hasn't been saved, then return the subtype string.
+ if (!((int) $this->guid > 0)) {
+ return $this->get('subtype');
+ }
+
+ return get_subtype_from_id($this->get('subtype'));
+ }
+
+ /**
+ * Get the guid of the entity's owner.
+ *
+ * @return int The owner GUID
+ */
+ public function getOwnerGUID() {
+ return $this->owner_guid;
+ }
+
+ /**
+ * Return the guid of the entity's owner.
+ *
+ * @return int The owner GUID
+ * @deprecated 1.8 Use getOwnerGUID()
+ */
+ public function getOwner() {
+ elgg_deprecated_notice("ElggEntity::getOwner deprecated for ElggEntity::getOwnerGUID", 1.8);
+ return $this->getOwnerGUID();
+ }
+
+ /**
+ * Gets the ElggEntity that owns this entity.
+ *
+ * @return ElggEntity The owning entity
+ */
+ public function getOwnerEntity() {
+ return get_entity($this->owner_guid);
+ }
+
+ /**
+ * Set the container for this object.
+ *
+ * @param int $container_guid The ID of the container.
+ *
+ * @return bool
+ */
+ public function setContainerGUID($container_guid) {
+ $container_guid = (int)$container_guid;
+
+ return $this->set('container_guid', $container_guid);
+ }
+
+ /**
+ * Set the container for this object.
+ *
+ * @param int $container_guid The ID of the container.
+ *
+ * @return bool
+ * @deprecated 1.8 use setContainerGUID()
+ */
+ public function setContainer($container_guid) {
+ elgg_deprecated_notice("ElggObject::setContainer deprecated for ElggEntity::setContainerGUID", 1.8);
+ $container_guid = (int)$container_guid;
+
+ return $this->set('container_guid', $container_guid);
+ }
+
+ /**
+ * Gets the container GUID for this entity.
+ *
+ * @return int
+ */
+ public function getContainerGUID() {
+ return $this->get('container_guid');
+ }
+
+ /**
+ * Gets the container GUID for this entity.
+ *
+ * @return int
+ * @deprecated 1.8 Use getContainerGUID()
+ */
+ public function getContainer() {
+ elgg_deprecated_notice("ElggObject::getContainer deprecated for ElggEntity::getContainerGUID", 1.8);
+ return $this->get('container_guid');
+ }
+
+ /**
+ * Get the container entity for this object.
+ *
+ * @return ElggEntity
+ * @since 1.8.0
+ */
+ public function getContainerEntity() {
+ return get_entity($this->getContainerGUID());
+ }
+
+ /**
+ * Returns the UNIX epoch time that this entity was last updated
+ *
+ * @return int UNIX epoch time
+ */
+ public function getTimeUpdated() {
+ return $this->get('time_updated');
+ }
+
+ /**
+ * Returns the URL for this entity
+ *
+ * @return string The URL
+ * @see register_entity_url_handler()
+ * @see ElggEntity::setURL()
+ */
+ public function getURL() {
+ if (!empty($this->url_override)) {
+ return $this->url_override;
+ }
+ return get_entity_url($this->getGUID());
+ }
+
+ /**
+ * Overrides the URL returned by getURL()
+ *
+ * @warning This override exists only for the life of the object.
+ *
+ * @param string $url The new item URL
+ *
+ * @return string The URL
+ */
+ public function setURL($url) {
+ $this->url_override = $url;
+ return $url;
+ }
+
+ /**
+ * Get the URL for this entity's icon
+ *
+ * Plugins can register for the 'entity:icon:url', <type> plugin hook
+ * to customize the icon for an entity.
+ *
+ * @param string $size Size of the icon: tiny, small, medium, large
+ *
+ * @return string The URL
+ * @since 1.8.0
+ */
+ public function getIconURL($size = 'medium') {
+ $size = elgg_strtolower($size);
+
+ if (isset($this->icon_override[$size])) {
+ elgg_deprecated_notice("icon_override on an individual entity is deprecated", 1.8);
+ return $this->icon_override[$size];
+ }
+
+ $type = $this->getType();
+ $params = array(
+ 'entity' => $this,
+ 'size' => $size,
+ );
+
+ $url = elgg_trigger_plugin_hook('entity:icon:url', $type, $params, null);
+ if ($url == null) {
+ $url = "_graphics/icons/default/$size.png";
+ }
+
+ return elgg_normalize_url($url);
+ }
+
+ /**
+ * Returns a URL for the entity's icon.
+ *
+ * @param string $size Either 'large', 'medium', 'small' or 'tiny'
+ *
+ * @return string The url or false if no url could be worked out.
+ * @deprecated Use getIconURL()
+ */
+ public function getIcon($size = 'medium') {
+ elgg_deprecated_notice("getIcon() deprecated by getIconURL()", 1.8);
+ return $this->getIconURL($size);
+ }
+
+ /**
+ * Set an icon override for an icon and size.
+ *
+ * @warning This override exists only for the life of the object.
+ *
+ * @param string $url The url of the icon.
+ * @param string $size The size its for.
+ *
+ * @return bool
+ * @deprecated 1.8 See getIconURL() for the plugin hook to use
+ */
+ public function setIcon($url, $size = 'medium') {
+ elgg_deprecated_notice("icon_override on an individual entity is deprecated", 1.8);
+
+ $url = sanitise_string($url);
+ $size = sanitise_string($size);
+
+ if (!$this->icon_override) {
+ $this->icon_override = array();
+ }
+ $this->icon_override[$size] = $url;
+
+ return true;
+ }
+
+ /**
+ * Tests to see whether the object has been fully loaded.
+ *
+ * @return bool
+ */
+ public function isFullyLoaded() {
+ return ! ($this->attributes['tables_loaded'] < $this->attributes['tables_split']);
+ }
+
+ /**
+ * Save an entity.
+ *
+ * @return bool|int
+ * @throws IOException
+ */
+ public function save() {
+ $guid = $this->getGUID();
+ if ($guid > 0) {
+
+ // See #5600. This ensures the lower level can_edit_entity() check will use a
+ // fresh entity from the DB so it sees the persisted owner_guid
+ _elgg_disable_caching_for_entity($guid);
+
+ $ret = update_entity(
+ $guid,
+ $this->get('owner_guid'),
+ $this->get('access_id'),
+ $this->get('container_guid'),
+ $this->get('time_created')
+ );
+
+ _elgg_enable_caching_for_entity($guid);
+ _elgg_cache_entity($this);
+
+ return $ret;
+ } else {
+ // Create a new entity (nb: using attribute array directly
+ // 'cos set function does something special!)
+ $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']);
+
+ if (!$this->attributes['guid']) {
+ throw new IOException(elgg_echo('IOException:BaseEntitySaveFailed'));
+ }
+
+ // Save any unsaved metadata
+ // @todo How to capture extra information (access id etc)
+ if (sizeof($this->temp_metadata) > 0) {
+ foreach ($this->temp_metadata as $name => $value) {
+ $this->$name = $value;
+ unset($this->temp_metadata[$name]);
+ }
+ }
+
+ // Save any unsaved annotations.
+ if (sizeof($this->temp_annotations) > 0) {
+ foreach ($this->temp_annotations as $name => $value) {
+ $this->annotate($name, $value);
+ unset($this->temp_annotations[$name]);
+ }
+ }
+
+ // Save any unsaved private settings.
+ if (sizeof($this->temp_private_settings) > 0) {
+ foreach ($this->temp_private_settings as $name => $value) {
+ $this->setPrivateSetting($name, $value);
+ unset($this->temp_private_settings[$name]);
+ }
+ }
+
+ // set the subtype to id now rather than a string
+ $this->attributes['subtype'] = get_subtype_id($this->attributes['type'],
+ $this->attributes['subtype']);
+
+ _elgg_cache_entity($this);
+
+ return $this->attributes['guid'];
+ }
+ }
+
+ /**
+ * Loads attributes from the entities table into the object.
+ *
+ * @param mixed $guid GUID of entity or stdClass object from entities table
+ *
+ * @return bool
+ */
+ protected function load($guid) {
+ if ($guid instanceof stdClass) {
+ $row = $guid;
+ } else {
+ $row = get_entity_as_row($guid);
+ }
+
+ if ($row) {
+ // Create the array if necessary - all subclasses should test before creating
+ if (!is_array($this->attributes)) {
+ $this->attributes = array();
+ }
+
+ // Now put these into the attributes array as core values
+ $objarray = (array) $row;
+ foreach ($objarray as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+
+ // Increment the portion counter
+ if (!$this->isFullyLoaded()) {
+ $this->attributes['tables_loaded']++;
+ }
+
+ // guid needs to be an int https://github.com/elgg/elgg/issues/4111
+ $this->attributes['guid'] = (int)$this->attributes['guid'];
+
+ // Cache object handle
+ if ($this->attributes['guid']) {
+ _elgg_cache_entity($this);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Disable this entity.
+ *
+ * Disabled entities are not returned by getter functions.
+ * To enable an entity, use {@link enable_entity()}.
+ *
+ * Recursively disabling an entity will disable all entities
+ * owned or contained by the parent entity.
+ *
+ * @internal Disabling an entity sets the 'enabled' column to 'no'.
+ *
+ * @param string $reason Optional reason
+ * @param bool $recursive Recursively disable all contained entities?
+ *
+ * @return bool
+ * @see enable_entity()
+ * @see ElggEntity::enable()
+ */
+ public function disable($reason = "", $recursive = true) {
+ if ($r = disable_entity($this->get('guid'), $reason, $recursive)) {
+ $this->attributes['enabled'] = 'no';
+ }
+
+ return $r;
+ }
+
+ /**
+ * Enable an entity
+ *
+ * @warning Disabled entities can't be loaded unless
+ * {@link access_show_hidden_entities(true)} has been called.
+ *
+ * @see enable_entity()
+ * @see access_show_hiden_entities()
+ * @return bool
+ */
+ public function enable() {
+ if ($r = enable_entity($this->get('guid'))) {
+ $this->attributes['enabled'] = 'yes';
+ }
+
+ return $r;
+ }
+
+ /**
+ * Is this entity enabled?
+ *
+ * @return boolean
+ */
+ public function isEnabled() {
+ if ($this->enabled == 'yes') {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Delete this entity.
+ *
+ * @param bool $recursive Whether to delete all the entities contained by this entity
+ *
+ * @return bool
+ */
+ public function delete($recursive = true) {
+ return delete_entity($this->get('guid'), $recursive);
+ }
+
+ /*
+ * LOCATABLE INTERFACE
+ */
+
+ /**
+ * Gets the 'location' metadata for the entity
+ *
+ * @return string The location
+ */
+ public function getLocation() {
+ return $this->location;
+ }
+
+ /**
+ * Sets the 'location' metadata for the entity
+ *
+ * @todo Unimplemented
+ *
+ * @param string $location String representation of the location
+ *
+ * @return bool
+ */
+ public function setLocation($location) {
+ $this->location = $location;
+ return true;
+ }
+
+ /**
+ * Set latitude and longitude metadata tags for a given entity.
+ *
+ * @param float $lat Latitude
+ * @param float $long Longitude
+ *
+ * @return bool
+ * @todo Unimplemented
+ */
+ public function setLatLong($lat, $long) {
+ $this->set('geo:lat', $lat);
+ $this->set('geo:long', $long);
+
+ return true;
+ }
+
+ /**
+ * Return the entity's latitude.
+ *
+ * @return float
+ * @todo Unimplemented
+ */
+ public function getLatitude() {
+ return (float)$this->get('geo:lat');
+ }
+
+ /**
+ * Return the entity's longitude
+ *
+ * @return float
+ */
+ public function getLongitude() {
+ return (float)$this->get('geo:long');
+ }
+
+ /*
+ * NOTABLE INTERFACE
+ */
+
+ /**
+ * Set the time and duration of an object
+ *
+ * @param int $hour If ommitted, now is assumed.
+ * @param int $minute If ommitted, now is assumed.
+ * @param int $second If ommitted, now is assumed.
+ * @param int $day If ommitted, now is assumed.
+ * @param int $month If ommitted, now is assumed.
+ * @param int $year If ommitted, now is assumed.
+ * @param int $duration Duration of event, remainder of the day is assumed.
+ *
+ * @return true
+ * @todo Unimplemented
+ */
+ public function setCalendarTimeAndDuration($hour = NULL, $minute = NULL, $second = NULL,
+ $day = NULL, $month = NULL, $year = NULL, $duration = NULL) {
+
+ $start = mktime($hour, $minute, $second, $month, $day, $year);
+ $end = $start + abs($duration);
+ if (!$duration) {
+ $end = get_day_end($day, $month, $year);
+ }
+
+ $this->calendar_start = $start;
+ $this->calendar_end = $end;
+
+ return true;
+ }
+
+ /**
+ * Returns the start timestamp.
+ *
+ * @return int
+ * @todo Unimplemented
+ */
+ public function getCalendarStartTime() {
+ return (int)$this->calendar_start;
+ }
+
+ /**
+ * Returns the end timestamp.
+ *
+ * @todo Unimplemented
+ *
+ * @return int
+ */
+ public function getCalendarEndTime() {
+ return (int)$this->calendar_end;
+ }
+
+ /*
+ * EXPORTABLE INTERFACE
+ */
+
+ /**
+ * Returns an array of fields which can be exported.
+ *
+ * @return array
+ */
+ public function getExportableValues() {
+ return array(
+ 'guid',
+ 'type',
+ 'subtype',
+ 'time_created',
+ 'time_updated',
+ 'container_guid',
+ 'owner_guid',
+ 'site_guid'
+ );
+ }
+
+ /**
+ * Export this class into an array of ODD Elements containing all necessary fields.
+ * Override if you wish to return more information than can be found in
+ * $this->attributes (shouldn't happen)
+ *
+ * @return array
+ */
+ public function export() {
+ $tmp = array();
+
+ // Generate uuid
+ $uuid = guid_to_uuid($this->getGUID());
+
+ // Create entity
+ $odd = new ODDEntity(
+ $uuid,
+ $this->attributes['type'],
+ get_subtype_from_id($this->attributes['subtype'])
+ );
+
+ $tmp[] = $odd;
+
+ $exportable_values = $this->getExportableValues();
+
+ // Now add its attributes
+ foreach ($this->attributes as $k => $v) {
+ $meta = NULL;
+
+ if (in_array($k, $exportable_values)) {
+ switch ($k) {
+ case 'guid': // Dont use guid in OpenDD
+ case 'type': // Type and subtype already taken care of
+ case 'subtype':
+ break;
+
+ case 'time_created': // Created = published
+ $odd->setAttribute('published', date("r", $v));
+ break;
+
+ case 'site_guid': // Container
+ $k = 'site_uuid';
+ $v = guid_to_uuid($v);
+ $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
+ break;
+
+ case 'container_guid': // Container
+ $k = 'container_uuid';
+ $v = guid_to_uuid($v);
+ $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
+ break;
+
+ case 'owner_guid': // Convert owner guid to uuid, this will be stored in metadata
+ $k = 'owner_uuid';
+ $v = guid_to_uuid($v);
+ $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
+ break;
+
+ default:
+ $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
+ }
+
+ // set the time of any metadata created
+ if ($meta) {
+ $meta->setAttribute('published', date("r", $this->time_created));
+ $tmp[] = $meta;
+ }
+ }
+ }
+
+ // Now we do something a bit special.
+ /*
+ * This provides a rendered view of the entity to foreign sites.
+ */
+
+ elgg_set_viewtype('default');
+ $view = elgg_view_entity($this, array('full_view' => true));
+ elgg_set_viewtype();
+
+ $tmp[] = new ODDMetaData($uuid . "volatile/renderedentity/", $uuid,
+ 'renderedentity', $view, 'volatile');
+
+ return $tmp;
+ }
+
+ /*
+ * IMPORTABLE INTERFACE
+ */
+
+ /**
+ * Import data from an parsed ODD xml data array.
+ *
+ * @param ODD $data XML data
+ *
+ * @return true
+ *
+ * @throws InvalidParameterException
+ */
+ public function import(ODD $data) {
+ if (!($data instanceof ODDEntity)) {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnexpectedODDClass'));
+ }
+
+ // Set type and subtype
+ $this->attributes['type'] = $data->getAttribute('class');
+ $this->attributes['subtype'] = $data->getAttribute('subclass');
+
+ // Set owner
+ $this->attributes['owner_guid'] = elgg_get_logged_in_user_guid(); // Import as belonging to importer.
+
+ // Set time
+ $this->attributes['time_created'] = strtotime($data->getAttribute('published'));
+ $this->attributes['time_updated'] = time();
+
+ return true;
+ }
+
+ /*
+ * SYSTEM LOG INTERFACE
+ */
+
+ /**
+ * Return an identification for the object for storage in the system log.
+ * This id must be an integer.
+ *
+ * @return int
+ */
+ public function getSystemLogID() {
+ return $this->getGUID();
+ }
+
+ /**
+ * For a given ID, return the object associated with it.
+ * This is used by the river functionality primarily.
+ *
+ * This is useful for checking access permissions etc on objects.
+ *
+ * @param int $id GUID.
+ *
+ * @todo How is this any different or more useful than get_entity($guid)
+ * or new ElggEntity($guid)?
+ *
+ * @return int GUID
+ */
+ public function getObjectFromID($id) {
+ return get_entity($id);
+ }
+
+ /**
+ * Returns tags for this entity.
+ *
+ * @warning Tags must be registered by {@link elgg_register_tag_metadata_name()}.
+ *
+ * @param array $tag_names Optionally restrict by tag metadata names.
+ *
+ * @return array
+ */
+ public function getTags($tag_names = NULL) {
+ if ($tag_names && !is_array($tag_names)) {
+ $tag_names = array($tag_names);
+ }
+
+ $valid_tags = elgg_get_registered_tag_metadata_names();
+ $entity_tags = array();
+
+ foreach ($valid_tags as $tag_name) {
+ if (is_array($tag_names) && !in_array($tag_name, $tag_names)) {
+ continue;
+ }
+
+ if ($tags = $this->$tag_name) {
+ // if a single tag, metadata returns a string.
+ // if multiple tags, metadata returns an array.
+ if (is_array($tags)) {
+ $entity_tags = array_merge($entity_tags, $tags);
+ } else {
+ $entity_tags[] = $tags;
+ }
+ }
+ }
+
+ return $entity_tags;
+ }
+}