aboutsummaryrefslogtreecommitdiff
path: root/engine/lib/entities.php
diff options
context:
space:
mode:
Diffstat (limited to 'engine/lib/entities.php')
-rw-r--r--engine/lib/entities.php859
1 files changed, 583 insertions, 276 deletions
diff --git a/engine/lib/entities.php b/engine/lib/entities.php
index cb197b569..4fcf1c657 100644
--- a/engine/lib/entities.php
+++ b/engine/lib/entities.php
@@ -17,13 +17,50 @@ global $ENTITY_CACHE;
$ENTITY_CACHE = array();
/**
- * Cache subtypes and related class names once loaded.
+ * GUIDs of entities banned from the entity cache (during this request)
*
- * @global array $SUBTYPE_CACHE
+ * @global array $ENTITY_CACHE_DISABLED_GUIDS
+ * @access private
+ */
+global $ENTITY_CACHE_DISABLED_GUIDS;
+$ENTITY_CACHE_DISABLED_GUIDS = array();
+
+/**
+ * Cache subtypes and related class names.
+ *
+ * @global array|null $SUBTYPE_CACHE array once populated from DB, initially null
* @access private
*/
global $SUBTYPE_CACHE;
-$SUBTYPE_CACHE = NULL;
+$SUBTYPE_CACHE = null;
+
+/**
+ * Remove this entity from the entity cache and make sure it is not re-added
+ *
+ * @param int $guid The entity guid
+ *
+ * @access private
+ * @todo this is a workaround until #5604 can be implemented
+ */
+function _elgg_disable_caching_for_entity($guid) {
+ global $ENTITY_CACHE_DISABLED_GUIDS;
+
+ _elgg_invalidate_cache_for_entity($guid);
+ $ENTITY_CACHE_DISABLED_GUIDS[$guid] = true;
+}
+
+/**
+ * Allow this entity to be stored in the entity cache
+ *
+ * @param int $guid The entity guid
+ *
+ * @access private
+ */
+function _elgg_enable_caching_for_entity($guid) {
+ global $ENTITY_CACHE_DISABLED_GUIDS;
+
+ unset($ENTITY_CACHE_DISABLED_GUIDS[$guid]);
+}
/**
* Invalidate this class's entry in the cache.
@@ -33,12 +70,14 @@ $SUBTYPE_CACHE = NULL;
* @return void
* @access private
*/
-function invalidate_cache_for_entity($guid) {
+function _elgg_invalidate_cache_for_entity($guid) {
global $ENTITY_CACHE;
$guid = (int)$guid;
unset($ENTITY_CACHE[$guid]);
+
+ elgg_get_metadata_cache()->clear($guid);
}
/**
@@ -49,60 +88,59 @@ function invalidate_cache_for_entity($guid) {
* @param ElggEntity $entity Entity to cache
*
* @return void
- * @see retrieve_cached_entity()
- * @see invalidate_cache_for_entity()
+ * @see _elgg_retrieve_cached_entity()
+ * @see _elgg_invalidate_cache_for_entity()
* @access private
+ * @todo Use an ElggCache object
*/
-function cache_entity(ElggEntity $entity) {
- global $ENTITY_CACHE;
+function _elgg_cache_entity(ElggEntity $entity) {
+ global $ENTITY_CACHE, $ENTITY_CACHE_DISABLED_GUIDS;
- $ENTITY_CACHE[$entity->guid] = $entity;
-}
+ // Don't cache non-plugin entities while access control is off, otherwise they could be
+ // exposed to users who shouldn't see them when control is re-enabled.
+ if (!($entity instanceof ElggPlugin) && elgg_get_ignore_access()) {
+ return;
+ }
-/**
- * Retrieve a entity from the cache.
- *
- * @param int $guid The guid
- *
- * @return void
- * @see cache_entity()
- * @see invalidate_cache_for_entity()
- * @access private
- */
-function retrieve_cached_entity($guid) {
- global $ENTITY_CACHE;
+ $guid = $entity->getGUID();
+ if (isset($ENTITY_CACHE_DISABLED_GUIDS[$guid])) {
+ return;
+ }
- $guid = (int)$guid;
+ // Don't store too many or we'll have memory problems
+ // @todo Pick a less arbitrary limit
+ if (count($ENTITY_CACHE) > 256) {
+ $random_guid = array_rand($ENTITY_CACHE);
- if (isset($ENTITY_CACHE[$guid])) {
- if ($ENTITY_CACHE[$guid]->isFullyLoaded()) {
- return $ENTITY_CACHE[$guid];
- }
+ unset($ENTITY_CACHE[$random_guid]);
+
+ // Purge separate metadata cache. Original idea was to do in entity destructor, but that would
+ // have caused a bunch of unnecessary purges at every shutdown. Doing it this way we have no way
+ // to know that the expunged entity will be GCed (might be another reference living), but that's
+ // OK; the metadata will reload if necessary.
+ elgg_get_metadata_cache()->clear($random_guid);
}
- return false;
+ $ENTITY_CACHE[$guid] = $entity;
}
/**
- * As retrieve_cached_entity, but returns the result as a stdClass
- * (compatible with load functions that expect a database row.)
+ * Retrieve a entity from the cache.
*
* @param int $guid The guid
*
- * @return mixed
- * @todo unused
+ * @return ElggEntity|bool false if entity not cached, or not fully loaded
+ * @see _elgg_cache_entity()
+ * @see _elgg_invalidate_cache_for_entity()
* @access private
*/
-function retrieve_cached_entity_row($guid) {
- $obj = retrieve_cached_entity($guid);
- if ($obj) {
- $tmp = new stdClass;
+function _elgg_retrieve_cached_entity($guid) {
+ global $ENTITY_CACHE;
- foreach ($obj as $k => $v) {
- $tmp->$k = $v;
+ if (isset($ENTITY_CACHE[$guid])) {
+ if ($ENTITY_CACHE[$guid]->isFullyLoaded()) {
+ return $ENTITY_CACHE[$guid];
}
-
- return $tmp;
}
return false;
@@ -124,8 +162,6 @@ function retrieve_cached_entity_row($guid) {
* @internal Subtypes are stored in the entity_subtypes table. There is a foreign
* key in the entities table.
*
- * @todo Move to a nicer place?
- *
* @param string $type Type
* @param string $subtype Subtype
*
@@ -135,75 +171,99 @@ function retrieve_cached_entity_row($guid) {
* @access private
*/
function get_subtype_id($type, $subtype) {
- global $CONFIG, $SUBTYPE_CACHE;
+ global $SUBTYPE_CACHE;
- $type = sanitise_string($type);
- $subtype = sanitise_string($subtype);
-
- if ($subtype == "") {
- return FALSE;
+ if (!$subtype) {
+ return false;
}
- // Todo: cache here? Or is looping less efficient that going to the db each time?
- $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes
- where type='$type' and subtype='$subtype'");
-
- if ($result) {
- if (!$SUBTYPE_CACHE) {
- $SUBTYPE_CACHE = array();
- }
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
+ }
- $SUBTYPE_CACHE[$result->id] = $result;
+ // use the cache before hitting database
+ $result = _elgg_retrieve_cached_subtype($type, $subtype);
+ if ($result !== null) {
return $result->id;
}
- return FALSE;
+ return false;
}
/**
* Return string name for a given subtype ID.
*
- * @todo Move to a nicer place?
- *
* @param int $subtype_id Subtype ID
*
- * @return string Subtype name
+ * @return string|false Subtype name, false if subtype not found
* @link http://docs.elgg.org/DataModel/Entities/Subtypes
* @see get_subtype_from_id()
* @access private
*/
function get_subtype_from_id($subtype_id) {
- global $CONFIG, $SUBTYPE_CACHE;
-
- $subtype_id = (int)$subtype_id;
+ global $SUBTYPE_CACHE;
if (!$subtype_id) {
return false;
}
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
+ }
+
if (isset($SUBTYPE_CACHE[$subtype_id])) {
return $SUBTYPE_CACHE[$subtype_id]->subtype;
}
- $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes where id=$subtype_id");
- if ($result) {
- if (!$SUBTYPE_CACHE) {
- $SUBTYPE_CACHE = array();
- }
+ return false;
+}
- $SUBTYPE_CACHE[$subtype_id] = $result;
- return $result->subtype;
+/**
+ * Retrieve subtype from the cache.
+ *
+ * @param string $type
+ * @param string $subtype
+ * @return stdClass|null
+ *
+ * @access private
+ */
+function _elgg_retrieve_cached_subtype($type, $subtype) {
+ global $SUBTYPE_CACHE;
+
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
}
- return false;
+ foreach ($SUBTYPE_CACHE as $obj) {
+ if ($obj->type === $type && $obj->subtype === $subtype) {
+ return $obj;
+ }
+ }
+ return null;
+}
+
+/**
+ * Fetch all suptypes from DB to local cache.
+ *
+ * @access private
+ */
+function _elgg_populate_subtype_cache() {
+ global $CONFIG, $SUBTYPE_CACHE;
+
+ $results = get_data("SELECT * FROM {$CONFIG->dbprefix}entity_subtypes");
+
+ $SUBTYPE_CACHE = array();
+ foreach ($results as $row) {
+ $SUBTYPE_CACHE[$row->id] = $row;
+ }
}
/**
- * Return a classname for a registered type and subtype.
+ * Return the class name for a registered type and subtype.
*
* Entities can be registered to always be loaded as a certain class
- * with {@link register_entity_subtype()}. This function returns
- * the class name if found, and NULL if not.
+ * with add_subtype() or update_subtype(). This function returns the class
+ * name if found and NULL if not.
*
* @param string $type The type
* @param string $subtype The subtype
@@ -214,29 +274,23 @@ function get_subtype_from_id($subtype_id) {
* @access private
*/
function get_subtype_class($type, $subtype) {
- global $CONFIG, $SUBTYPE_CACHE;
-
- $type = sanitise_string($type);
- $subtype = sanitise_string($subtype);
-
- // Todo: cache here? Or is looping less efficient that going to the db each time?
- $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes
- where type='$type' and subtype='$subtype'");
+ global $SUBTYPE_CACHE;
- if ($result) {
- if (!$SUBTYPE_CACHE) {
- $SUBTYPE_CACHE = array();
- }
-
- $SUBTYPE_CACHE[$result->id] = $result;
- return $result->class;
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
+ }
+
+ // use the cache before going to the database
+ $obj = _elgg_retrieve_cached_subtype($type, $subtype);
+ if ($obj) {
+ return $obj->class;
}
- return NULL;
+ return null;
}
/**
- * Returns the classname for a subtype id.
+ * Returns the class name for a subtype id.
*
* @param int $subtype_id The subtype id
*
@@ -246,29 +300,21 @@ function get_subtype_class($type, $subtype) {
* @access private
*/
function get_subtype_class_from_id($subtype_id) {
- global $CONFIG, $SUBTYPE_CACHE;
-
- $subtype_id = (int)$subtype_id;
+ global $SUBTYPE_CACHE;
if (!$subtype_id) {
- return false;
+ return null;
}
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
+ }
+
if (isset($SUBTYPE_CACHE[$subtype_id])) {
return $SUBTYPE_CACHE[$subtype_id]->class;
}
- $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes where id=$subtype_id");
-
- if ($result) {
- if (!$SUBTYPE_CACHE) {
- $SUBTYPE_CACHE = array();
- }
- $SUBTYPE_CACHE[$subtype_id] = $result;
- return $result->class;
- }
-
- return NULL;
+ return null;
}
/**
@@ -279,6 +325,9 @@ function get_subtype_class_from_id($subtype_id) {
* it will be loaded as that class automatically when retrieved from the database with
* {@link get_entity()}.
*
+ * @warning This function cannot be used to change the class for a type-subtype pair.
+ * Use update_subtype() for that.
+ *
* @param string $type The type you're subtyping (site, user, object, or group)
* @param string $subtype The subtype
* @param string $class Optional class name for the object
@@ -291,21 +340,32 @@ function get_subtype_class_from_id($subtype_id) {
* @see get_entity()
*/
function add_subtype($type, $subtype, $class = "") {
- global $CONFIG;
- $type = sanitise_string($type);
- $subtype = sanitise_string($subtype);
- $class = sanitise_string($class);
+ global $CONFIG, $SUBTYPE_CACHE;
- // Short circuit if no subtype is given
- if ($subtype == "") {
+ if (!$subtype) {
return 0;
}
$id = get_subtype_id($type, $subtype);
- if ($id == 0) {
- return insert_data("insert into {$CONFIG->dbprefix}entity_subtypes"
- . " (type, subtype, class) values ('$type','$subtype','$class')");
+ if (!$id) {
+ // In cache we store non-SQL-escaped strings because that's what's returned by query
+ $cache_obj = (object) array(
+ 'type' => $type,
+ 'subtype' => $subtype,
+ 'class' => $class,
+ );
+
+ $type = sanitise_string($type);
+ $subtype = sanitise_string($subtype);
+ $class = sanitise_string($class);
+
+ $id = insert_data("INSERT INTO {$CONFIG->dbprefix}entity_subtypes"
+ . " (type, subtype, class) VALUES ('$type', '$subtype', '$class')");
+
+ // add entry to cache
+ $cache_obj->id = $id;
+ $SUBTYPE_CACHE[$id] = $cache_obj;
}
return $id;
@@ -314,6 +374,10 @@ function add_subtype($type, $subtype, $class = "") {
/**
* Removes a registered ElggEntity type, subtype, and classname.
*
+ * @warning You do not want to use this function. If you want to unregister
+ * a class for a subtype, use update_subtype(). Using this function will
+ * permanently orphan all the objects created with the specified subtype.
+ *
* @param string $type Type
* @param string $subtype Subtype
*
@@ -332,7 +396,7 @@ function remove_subtype($type, $subtype) {
}
/**
- * Update a registered ElggEntity type, subtype, and classname
+ * Update a registered ElggEntity type, subtype, and class name
*
* @param string $type Type
* @param string $subtype Subtype
@@ -341,18 +405,33 @@ function remove_subtype($type, $subtype) {
* @return bool
*/
function update_subtype($type, $subtype, $class = '') {
- global $CONFIG;
+ global $CONFIG, $SUBTYPE_CACHE;
- if (!$id = get_subtype_id($type, $subtype)) {
- return FALSE;
+ $id = get_subtype_id($type, $subtype);
+ if (!$id) {
+ return false;
+ }
+
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
}
+
+ $unescaped_class = $class;
+
$type = sanitise_string($type);
$subtype = sanitise_string($subtype);
-
- return update_data("UPDATE {$CONFIG->dbprefix}entity_subtypes
+ $class = sanitise_string($class);
+
+ $success = update_data("UPDATE {$CONFIG->dbprefix}entity_subtypes
SET type = '$type', subtype = '$subtype', class = '$class'
WHERE id = $id
");
+
+ if ($success && isset($SUBTYPE_CACHE[$id])) {
+ $SUBTYPE_CACHE[$id]->class = $unescaped_class;
+ }
+
+ return $success;
}
/**
@@ -370,7 +449,7 @@ function update_subtype($type, $subtype, $class = '') {
* @param int $time_created The time creation timestamp
*
* @return bool
- * @link http://docs.elgg.org/DataModel/Entities
+ * @throws InvalidParameterException
* @access private
*/
function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $time_created = null) {
@@ -393,6 +472,10 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $
$time_created = (int) $time_created;
}
+ if ($access_id == ACCESS_DEFAULT) {
+ throw new InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h');
+ }
+
if ($entity && $entity->canEdit()) {
if (elgg_trigger_event('update', $entity->type, $entity)) {
$ret = update_data("UPDATE {$CONFIG->dbprefix}entities
@@ -410,7 +493,7 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $
$newentity_cache = new ElggMemcache('new_entity_cache');
}
if ($newentity_cache) {
- $new_entity = $newentity_cache->delete($guid);
+ $newentity_cache->delete($guid);
}
// Handle cases where there was no error BUT no rows were updated!
@@ -452,8 +535,10 @@ function can_write_to_container($user_guid = 0, $container_guid = 0, $type = 'al
$container_guid = elgg_get_page_owner_guid();
}
+ $return = false;
+
if (!$container_guid) {
- $return = TRUE;
+ $return = true;
}
$container = get_entity($container_guid);
@@ -461,16 +546,15 @@ function can_write_to_container($user_guid = 0, $container_guid = 0, $type = 'al
if ($container) {
// If the user can edit the container, they can also write to it
if ($container->canEdit($user_guid)) {
- $return = TRUE;
+ $return = true;
}
- // Basics, see if the user is a member of the group.
+ // If still not approved, see if the user is a member of the group
// @todo this should be moved to the groups plugin/library
- if ($user && $container instanceof ElggGroup) {
- if (!$container->isMember($user)) {
- $return = FALSE;
- } else {
- $return = TRUE;
+ if (!$return && $user && $container instanceof ElggGroup) {
+ /* @var ElggGroup $container */
+ if ($container->isMember($user)) {
+ $return = true;
}
}
}
@@ -507,8 +591,8 @@ function can_write_to_container($user_guid = 0, $container_guid = 0, $type = 'al
*
* @return int|false The new entity's GUID, or false on failure
* @throws InvalidParameterException
- * @access private
* @link http://docs.elgg.org/DataModel/Entities
+ * @access private
*/
function create_entity($type, $subtype, $owner_guid, $access_id, $site_guid = 0,
$container_guid = 0) {
@@ -518,7 +602,6 @@ $container_guid = 0) {
$type = sanitise_string($type);
$subtype_id = add_subtype($type, $subtype);
$owner_guid = (int)$owner_guid;
- $access_id = (int)$access_id;
$time = time();
if ($site_guid == 0) {
$site_guid = $CONFIG->site_guid;
@@ -527,13 +610,17 @@ $container_guid = 0) {
if ($container_guid == 0) {
$container_guid = $owner_guid;
}
+ $access_id = (int)$access_id;
+ if ($access_id == ACCESS_DEFAULT) {
+ throw new InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h');
+ }
- $user = elgg_get_logged_in_user_entity();
- if (!can_write_to_container($user->guid, $owner_guid, $type, $subtype)) {
+ $user_guid = elgg_get_logged_in_user_guid();
+ if (!can_write_to_container($user_guid, $owner_guid, $type, $subtype)) {
return false;
}
if ($owner_guid != $container_guid) {
- if (!can_write_to_container($user->guid, $container_guid, $type, $subtype)) {
+ if (!can_write_to_container($user_guid, $container_guid, $type, $subtype)) {
return false;
}
}
@@ -584,12 +671,14 @@ function get_entity_as_row($guid) {
*
* @param stdClass $row The row of the entry in the entities table.
*
- * @return object|false
+ * @return ElggEntity|false
* @link http://docs.elgg.org/DataModel/Entities
* @see get_entity_as_row()
* @see add_subtype()
* @see get_entity()
* @access private
+ *
+ * @throws ClassException|InstallationException
*/
function entity_row_to_elggstar($row) {
if (!($row instanceof stdClass)) {
@@ -667,26 +756,59 @@ function entity_row_to_elggstar($row) {
* @link http://docs.elgg.org/DataModel/Entities
*/
function get_entity($guid) {
- static $newentity_cache;
- $new_entity = false;
+ // This should not be a static local var. Notice that cache writing occurs in a completely
+ // different instance outside this function.
+ // @todo We need a single Memcache instance with a shared pool of namespace wrappers. This function would pull an instance from the pool.
+ static $shared_cache;
+
+ // We could also use: if (!(int) $guid) { return FALSE },
+ // but that evaluates to a false positive for $guid = TRUE.
+ // This is a bit slower, but more thorough.
+ if (!is_numeric($guid) || $guid === 0 || $guid === '0') {
+ return false;
+ }
+
+ // Check local cache first
+ $new_entity = _elgg_retrieve_cached_entity($guid);
+ if ($new_entity) {
+ return $new_entity;
+ }
- if (!is_numeric($guid)) {
- return FALSE;
+ // Check shared memory cache, if available
+ if (null === $shared_cache) {
+ if (is_memcache_available()) {
+ $shared_cache = new ElggMemcache('new_entity_cache');
+ } else {
+ $shared_cache = false;
+ }
}
- if ((!$newentity_cache) && (is_memcache_available())) {
- $newentity_cache = new ElggMemcache('new_entity_cache');
+ // until ACLs in memcache, DB query is required to determine access
+ $entity_row = get_entity_as_row($guid);
+ if (!$entity_row) {
+ return false;
}
- if ($newentity_cache) {
- $new_entity = $newentity_cache->load($guid);
+ if ($shared_cache) {
+ $cached_entity = $shared_cache->load($guid);
+ // @todo store ACLs in memcache https://github.com/elgg/elgg/issues/3018#issuecomment-13662617
+ if ($cached_entity) {
+ // @todo use ACL and cached entity access_id to determine if user can see it
+ return $cached_entity;
+ }
}
- if ($new_entity) {
- return $new_entity;
+ // don't let incomplete entities cause fatal exceptions
+ try {
+ $new_entity = entity_row_to_elggstar($entity_row);
+ } catch (IncompleteEntityException $e) {
+ return false;
}
- return entity_row_to_elggstar(get_entity_as_row($guid));
+ if ($new_entity) {
+ _elgg_cache_entity($new_entity);
+ }
+ return $new_entity;
}
/**
@@ -736,6 +858,7 @@ function elgg_entity_exists($guid) {
* Joined with subtypes by AND. See below)
*
* subtypes => NULL|STR entity subtype (SQL: subtype IN ('subtype1', 'subtype2))
+ * Use ELGG_ENTITIES_NO_VALUE for no subtype.
*
* type_subtype_pairs => NULL|ARR (array('type' => 'subtype'))
* (type = '$type' AND subtype = '$subtype') pairs
@@ -772,7 +895,7 @@ function elgg_entity_exists($guid) {
*
* callback => string A callback function to pass each row through
*
- * @return mixed int if count is true, an array of entity objects, or false on failure
+ * @return mixed If count, int. If not count, array. false on errors.
* @since 1.7.0
* @see elgg_get_entities_from_metadata()
* @see elgg_get_entities_from_relationship()
@@ -810,6 +933,8 @@ function elgg_get_entities(array $options = array()) {
'joins' => array(),
'callback' => 'entity_row_to_elggstar',
+
+ '__ElggBatch' => null,
);
$options = array_merge($defaults, $options);
@@ -846,9 +971,6 @@ function elgg_get_entities(array $options = array()) {
$wheres[] = elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
$options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
- // remove identical where clauses
- $wheres = array_unique($wheres);
-
// see if any functions failed
// remove empty strings on successful functions
foreach ($wheres as $i => $where) {
@@ -859,6 +981,9 @@ function elgg_get_entities(array $options = array()) {
}
}
+ // remove identical where clauses
+ $wheres = array_unique($wheres);
+
// evaluate join clauses
if (!is_array($options['joins'])) {
$options['joins'] = array($options['joins']);
@@ -912,21 +1037,46 @@ function elgg_get_entities(array $options = array()) {
}
if (!$options['count']) {
- if ($options['group_by'] = sanitise_string($options['group_by'])) {
+ if ($options['group_by']) {
$query .= " GROUP BY {$options['group_by']}";
}
- if ($options['order_by'] = sanitise_string($options['order_by'])) {
+ if ($options['order_by']) {
$query .= " ORDER BY {$options['order_by']}";
}
if ($options['limit']) {
- $limit = sanitise_int($options['limit']);
+ $limit = sanitise_int($options['limit'], false);
$offset = sanitise_int($options['offset'], false);
$query .= " LIMIT $offset, $limit";
}
- $dt = get_data($query, $options['callback']);
+ if ($options['callback'] === 'entity_row_to_elggstar') {
+ $dt = _elgg_fetch_entities_from_sql($query, $options['__ElggBatch']);
+ } else {
+ $dt = get_data($query, $options['callback']);
+ }
+
+ if ($dt) {
+ // populate entity and metadata caches
+ $guids = array();
+ foreach ($dt as $item) {
+ // A custom callback could result in items that aren't ElggEntity's, so check for them
+ if ($item instanceof ElggEntity) {
+ _elgg_cache_entity($item);
+ // plugins usually have only settings
+ if (!$item instanceof ElggPlugin) {
+ $guids[] = $item->guid;
+ }
+ }
+ }
+ // @todo Without this, recursive delete fails. See #4568
+ reset($dt);
+
+ if ($guids) {
+ elgg_get_metadata_cache()->populateFromEntities($guids);
+ }
+ }
return $dt;
} else {
$total = get_data_row($query);
@@ -935,6 +1085,104 @@ function elgg_get_entities(array $options = array()) {
}
/**
+ * Return entities from an SQL query generated by elgg_get_entities.
+ *
+ * @param string $sql
+ * @param ElggBatch $batch
+ * @return ElggEntity[]
+ *
+ * @access private
+ * @throws LogicException
+ */
+function _elgg_fetch_entities_from_sql($sql, ElggBatch $batch = null) {
+ static $plugin_subtype;
+ if (null === $plugin_subtype) {
+ $plugin_subtype = get_subtype_id('object', 'plugin');
+ }
+
+ // Keys are types, values are columns that, if present, suggest that the secondary
+ // table is already JOINed
+ $types_to_optimize = array(
+ 'object' => 'title',
+ 'user' => 'password',
+ 'group' => 'name',
+ );
+
+ $rows = get_data($sql);
+
+ // guids to look up in each type
+ $lookup_types = array();
+ // maps GUIDs to the $rows key
+ $guid_to_key = array();
+
+ if (isset($rows[0]->type, $rows[0]->subtype)
+ && $rows[0]->type === 'object'
+ && $rows[0]->subtype == $plugin_subtype) {
+ // Likely the entire resultset is plugins, which have already been optimized
+ // to JOIN the secondary table. In this case we allow retrieving from cache,
+ // but abandon the extra queries.
+ $types_to_optimize = array();
+ }
+
+ // First pass: use cache where possible, gather GUIDs that we're optimizing
+ foreach ($rows as $i => $row) {
+ if (empty($row->guid) || empty($row->type)) {
+ throw new LogicException('Entity row missing guid or type');
+ }
+ if ($entity = _elgg_retrieve_cached_entity($row->guid)) {
+ $rows[$i] = $entity;
+ continue;
+ }
+ if (isset($types_to_optimize[$row->type])) {
+ // check if row already looks JOINed.
+ if (isset($row->{$types_to_optimize[$row->type]})) {
+ // Row probably already contains JOINed secondary table. Don't make another query just
+ // to pull data that's already there
+ continue;
+ }
+ $lookup_types[$row->type][] = $row->guid;
+ $guid_to_key[$row->guid] = $i;
+ }
+ }
+ // Do secondary queries and merge rows
+ if ($lookup_types) {
+ $dbprefix = elgg_get_config('dbprefix');
+
+ foreach ($lookup_types as $type => $guids) {
+ $set = "(" . implode(',', $guids) . ")";
+ $sql = "SELECT * FROM {$dbprefix}{$type}s_entity WHERE guid IN $set";
+ $secondary_rows = get_data($sql);
+ if ($secondary_rows) {
+ foreach ($secondary_rows as $secondary_row) {
+ $key = $guid_to_key[$secondary_row->guid];
+ // cast to arrays to merge then cast back
+ $rows[$key] = (object)array_merge((array)$rows[$key], (array)$secondary_row);
+ }
+ }
+ }
+ }
+ // Second pass to finish conversion
+ foreach ($rows as $i => $row) {
+ if ($row instanceof ElggEntity) {
+ continue;
+ } else {
+ try {
+ $rows[$i] = entity_row_to_elggstar($row);
+ } catch (IncompleteEntityException $e) {
+ // don't let incomplete entities throw fatal errors
+ unset($rows[$i]);
+
+ // report incompletes to the batch process that spawned this query
+ if ($batch) {
+ $batch->reportIncompleteEntity($row);
+ }
+ }
+ }
+ }
+ return $rows;
+}
+
+/**
* Returns SQL where clause for type and subtype on main entity table
*
* @param string $table Entity table prefix as defined in SELECT...FROM entities $table
@@ -958,8 +1206,8 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
return '';
}
- // these are the only valid types for entities in elgg as defined in the DB.
- $valid_types = array('object', 'user', 'group', 'site');
+ // these are the only valid types for entities in elgg
+ $valid_types = elgg_get_config('entity_types');
// pairs override
$wheres = array();
@@ -985,7 +1233,7 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
foreach ($types as $type) {
if (!in_array($type, $valid_types)) {
$valid_types_count--;
- unset ($types[array_search($type, $types)]);
+ unset($types[array_search($type, $types)]);
} else {
// do the checking (and decrementing) in the subtype section.
$valid_subtypes_count += count($subtypes);
@@ -1003,13 +1251,24 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
$subtype_ids = array();
if ($subtypes) {
foreach ($subtypes as $subtype) {
- // check that the subtype is valid (with ELGG_ENTITIES_NO_VALUE being a valid subtype)
- if (ELGG_ENTITIES_NO_VALUE === $subtype || $subtype_id = get_subtype_id($type, $subtype)) {
- $subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $subtype) ? ELGG_ENTITIES_NO_VALUE : $subtype_id;
- } else {
- $valid_subtypes_count--;
- elgg_log("Type-subtype '$type:$subtype' does not exist!", 'NOTICE');
+ // check that the subtype is valid
+ if (!$subtype && ELGG_ENTITIES_NO_VALUE === $subtype) {
+ // subtype value is 0
+ $subtype_ids[] = ELGG_ENTITIES_NO_VALUE;
+ } elseif (!$subtype) {
+ // subtype is ignored.
+ // this handles ELGG_ENTITIES_ANY_VALUE, '', and anything falsy that isn't 0
continue;
+ } else {
+ $subtype_id = get_subtype_id($type, $subtype);
+
+ if ($subtype_id) {
+ $subtype_ids[] = $subtype_id;
+ } else {
+ $valid_subtypes_count--;
+ elgg_log("Type-subtype '$type:$subtype' does not exist!", 'NOTICE');
+ continue;
+ }
}
}
@@ -1037,7 +1296,7 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
foreach ($pairs as $paired_type => $paired_subtypes) {
if (!in_array($paired_type, $valid_types)) {
$valid_pairs_count--;
- unset ($pairs[array_search($paired_type, $pairs)]);
+ unset($pairs[array_search($paired_type, $pairs)]);
} else {
if ($paired_subtypes && !is_array($paired_subtypes)) {
$pairs[$paired_type] = array($paired_subtypes);
@@ -1100,7 +1359,7 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
* best to provide in table.column format.
* @param NULL|array $guids Array of GUIDs.
*
- * @return false|str
+ * @return false|string
* @since 1.8.0
* @access private
*/
@@ -1118,8 +1377,12 @@ function elgg_get_guid_based_where_sql($column, $guids) {
$guids_sanitized = array();
foreach ($guids as $guid) {
- if (($guid != sanitise_int($guid))) {
- return FALSE;
+ if ($guid !== ELGG_ENTITIES_NO_VALUE) {
+ $guid = sanitise_int($guid);
+
+ if (!$guid) {
+ return false;
+ }
}
$guids_sanitized[] = $guid;
}
@@ -1145,7 +1408,7 @@ function elgg_get_guid_based_where_sql($column, $guids) {
* @param NULL|int $time_updated_upper Time updated upper limit
* @param NULL|int $time_updated_lower Time updated lower limit
*
- * @return FALSE|str FALSE on fail, string on success.
+ * @return FALSE|string FALSE on fail, string on success.
* @since 1.7.0
* @access private
*/
@@ -1190,10 +1453,10 @@ $time_created_lower = NULL, $time_updated_upper = NULL, $time_updated_lower = NU
* @internal This also provides the views for elgg_view_annotation().
*
* @param array $options Any options from $getter options plus:
- * full_view => BOOL Display full view entities
- * list_type_toggle => BOOL Display gallery / list switch
- * pagination => BOOL Display pagination links
- * gallery => BOOL display in gallery view
+ * full_view => BOOL Display full view entities
+ * list_type => STR 'list' or 'gallery'
+ * list_type_toggle => BOOL Display gallery / list switch
+ * pagination => BOOL Display pagination links
*
* @param mixed $getter The entity getter function to use to fetch the entities
* @param mixed $viewer The function to use to view the entity list.
@@ -1210,13 +1473,14 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti
global $autofeed;
$autofeed = true;
+ $offset_key = isset($options['offset_key']) ? $options['offset_key'] : 'offset';
+
$defaults = array(
- 'offset' => (int) max(get_input('offset', 0), 0),
+ 'offset' => (int) max(get_input($offset_key, 0), 0),
'limit' => (int) max(get_input('limit', 10), 0),
'full_view' => TRUE,
'list_type_toggle' => FALSE,
'pagination' => TRUE,
- 'gallery' => FALSE,
);
$options = array_merge($defaults, $options);
@@ -1242,13 +1506,15 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti
*
* @tip Use this to generate a list of archives by month for when entities were added or updated.
*
+ * @todo document how to pass in array for $subtype
+ *
* @warning Months are returned in the form YYYYMM.
*
* @param string $type The type of entity
* @param string $subtype The subtype of entity
- * @param int $container_guid The container GUID that the entinties belong to
+ * @param int $container_guid The container GUID that the entities belong to
* @param int $site_guid The site GUID
- * @param str $order_by Order_by SQL order by clause
+ * @param string $order_by Order_by SQL order by clause
*
* @return array|false Either an array months as YYYYMM, or false on failure
*/
@@ -1355,9 +1621,9 @@ $order_by = 'time_created') {
* @param bool $recursive Recursively disable all entities owned or contained by $guid?
*
* @return bool
- * @access private
* @see access_show_hidden_entities()
* @link http://docs.elgg.org/Entities
+ * @access private
*/
function disable_entity($guid, $reason = "", $recursive = true) {
global $CONFIG;
@@ -1373,34 +1639,34 @@ function disable_entity($guid, $reason = "", $recursive = true) {
}
if ($recursive) {
- // Temporary token overriding access controls
- // @todo Do this better.
- static $__RECURSIVE_DELETE_TOKEN;
- // Make it slightly harder to guess
- $__RECURSIVE_DELETE_TOKEN = md5(elgg_get_logged_in_user_guid());
-
- $sub_entities = get_data("SELECT * from {$CONFIG->dbprefix}entities
- WHERE container_guid=$guid
- or owner_guid=$guid
- or site_guid=$guid", 'entity_row_to_elggstar');
+ $hidden = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $ia = elgg_set_ignore_access(true);
+
+ $sub_entities = get_data("SELECT * FROM {$CONFIG->dbprefix}entities
+ WHERE (
+ container_guid = $guid
+ OR owner_guid = $guid
+ OR site_guid = $guid
+ ) AND enabled='yes'", 'entity_row_to_elggstar');
if ($sub_entities) {
foreach ($sub_entities as $e) {
+ add_entity_relationship($e->guid, 'disabled_with', $entity->guid);
$e->disable($reason);
}
}
-
- $__RECURSIVE_DELETE_TOKEN = null;
+ access_show_hidden_entities($hidden);
+ elgg_set_ignore_access($ia);
}
$entity->disableMetadata();
$entity->disableAnnotations();
- // relationships can't be disabled. hope they join to the entities table.
- //$entity->disableRelationships();
+ _elgg_invalidate_cache_for_entity($guid);
$res = update_data("UPDATE {$CONFIG->dbprefix}entities
- set enabled='no'
- where guid={$guid}");
+ SET enabled = 'no'
+ WHERE guid = $guid");
return $res;
}
@@ -1412,43 +1678,55 @@ function disable_entity($guid, $reason = "", $recursive = true) {
/**
* Enable an entity.
*
- * @warning In order to enable an entity using ElggEntity::enable(),
- * you must first use {@link access_show_hidden_entities()}.
+ * @warning In order to enable an entity, you must first use
+ * {@link access_show_hidden_entities()}.
*
- * @param int $guid GUID of entity to enable
+ * @param int $guid GUID of entity to enable
+ * @param bool $recursive Recursively enable all entities disabled with the entity?
*
* @return bool
*/
-function enable_entity($guid) {
+function enable_entity($guid, $recursive = true) {
global $CONFIG;
$guid = (int)$guid;
// Override access only visible entities
- $access_status = access_get_show_hidden_status();
+ $old_access_status = access_get_show_hidden_status();
access_show_hidden_entities(true);
+ $result = false;
if ($entity = get_entity($guid)) {
if (elgg_trigger_event('enable', $entity->type, $entity)) {
if ($entity->canEdit()) {
- access_show_hidden_entities($access_status);
-
$result = update_data("UPDATE {$CONFIG->dbprefix}entities
- set enabled='yes'
- where guid={$guid}");
+ SET enabled = 'yes'
+ WHERE guid = $guid");
$entity->deleteMetadata('disable_reason');
$entity->enableMetadata();
$entity->enableAnnotations();
- return $result;
+ if ($recursive) {
+ $disabled_with_it = elgg_get_entities_from_relationship(array(
+ 'relationship' => 'disabled_with',
+ 'relationship_guid' => $entity->guid,
+ 'inverse_relationship' => true,
+ 'limit' => 0,
+ ));
+
+ foreach ($disabled_with_it as $e) {
+ $e->enable();
+ remove_entity_relationship($e->guid, 'disabled_with', $entity->guid);
+ }
+ }
}
}
}
- access_show_hidden_entities($access_status);
- return false;
+ access_show_hidden_entities($old_access_status);
+ return $result;
}
/**
@@ -1469,8 +1747,8 @@ function enable_entity($guid) {
* @param bool $recursive If true (default) then all entities which are
* owned or contained by $guid will also be deleted.
*
- * @access private
* @return bool
+ * @access private
*/
function delete_entity($guid, $recursive = true) {
global $CONFIG, $ENTITY_CACHE;
@@ -1482,7 +1760,16 @@ function delete_entity($guid, $recursive = true) {
// delete cache
if (isset($ENTITY_CACHE[$guid])) {
- invalidate_cache_for_entity($guid);
+ _elgg_invalidate_cache_for_entity($guid);
+ }
+
+ // If memcache is available then delete this entry from the cache
+ static $newentity_cache;
+ if ((!$newentity_cache) && (is_memcache_available())) {
+ $newentity_cache = new ElggMemcache('new_entity_cache');
+ }
+ if ($newentity_cache) {
+ $newentity_cache->delete($guid);
}
// Delete contained owned and otherwise releated objects (depth first)
@@ -1495,20 +1782,35 @@ function delete_entity($guid, $recursive = true) {
$entity_disable_override = access_get_show_hidden_status();
access_show_hidden_entities(true);
- $sub_entities = get_data("SELECT * from {$CONFIG->dbprefix}entities
- WHERE container_guid=$guid
- or owner_guid=$guid
- or site_guid=$guid", 'entity_row_to_elggstar');
- if ($sub_entities) {
- foreach ($sub_entities as $e) {
- $e->delete(true);
- }
+ $ia = elgg_set_ignore_access(true);
+
+ // @todo there was logic in the original code that ignored
+ // entities with owner or container guids of themselves.
+ // this should probably be prevented in ElggEntity instead of checked for here
+ $options = array(
+ 'wheres' => array(
+ "((container_guid = $guid OR owner_guid = $guid OR site_guid = $guid)"
+ . " AND guid != $guid)"
+ ),
+ 'limit' => 0
+ );
+
+ $batch = new ElggBatch('elgg_get_entities', $options);
+ $batch->setIncrementOffset(false);
+
+ foreach ($batch as $e) {
+ $e->delete(true);
}
access_show_hidden_entities($entity_disable_override);
$__RECURSIVE_DELETE_TOKEN = null;
+ elgg_set_ignore_access($ia);
}
+ $entity_disable_override = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $ia = elgg_set_ignore_access(true);
+
// Now delete the entity itself
$entity->deleteMetadata();
$entity->deleteOwnedMetadata();
@@ -1516,8 +1818,11 @@ function delete_entity($guid, $recursive = true) {
$entity->deleteOwnedAnnotations();
$entity->deleteRelationships();
- remove_from_river_by_subject($guid);
- remove_from_river_by_object($guid);
+ access_show_hidden_entities($entity_disable_override);
+ elgg_set_ignore_access($ia);
+
+ elgg_delete_river(array('subject_guid' => $guid));
+ elgg_delete_river(array('object_guid' => $guid));
remove_all_private_settings($guid);
$res = delete_data("DELETE from {$CONFIG->dbprefix}entities where guid={$guid}");
@@ -1545,7 +1850,7 @@ function delete_entity($guid, $recursive = true) {
}
}
- return $res;
+ return (bool)$res;
}
}
}
@@ -1561,7 +1866,7 @@ function delete_entity($guid, $recursive = true) {
* @param string $returnvalue Return value from previous hook
* @param array $params The parameters, passed 'guid' and 'varname'
*
- * @return null
+ * @return ElggMetadata|null
* @elgg_plugin_hook_handler volatile metadata
* @todo investigate more.
* @access private
@@ -1605,6 +1910,9 @@ function volatile_data_export_plugin_hook($hook, $entity_type, $returnvalue, $pa
*
* @elgg_event_handler export all
* @return mixed
+ * @access private
+ *
+ * @throws InvalidParameterException|InvalidClassException
*/
function export_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
// Sanity check values
@@ -1646,6 +1954,9 @@ function export_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
*
* @return ElggEntity the unsaved entity which should be populated by items.
* @todo Remove this.
+ * @access private
+ *
+ * @throws ClassException|InstallationException|ImportException
*/
function oddentity_to_elggentity(ODDEntity $element) {
$class = $element->getAttribute('class');
@@ -1657,7 +1968,7 @@ function oddentity_to_elggentity(ODDEntity $element) {
if (!$tmp) {
// Construct new class with owner from session
$classname = get_subtype_class($class, $subclass);
- if ($classname != "") {
+ if ($classname) {
if (class_exists($classname)) {
$tmp = new $classname();
@@ -1716,12 +2027,14 @@ function oddentity_to_elggentity(ODDEntity $element) {
* @return mixed
* @elgg_plugin_hook_handler import all
* @todo document
+ * @access private
*
+ * @throws ImportException
*/
function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
$element = $params['element'];
- $tmp = NULL;
+ $tmp = null;
if ($element instanceof ODDEntity) {
$tmp = oddentity_to_elggentity($element);
@@ -1729,7 +2042,7 @@ function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
if ($tmp) {
// Make sure its saved
if (!$tmp->save()) {
- elgg_echo('ImportException:ProblemSaving', array($element->getAttribute('uuid')));
+ $msg = elgg_echo('ImportException:ProblemSaving', array($element->getAttribute('uuid')));
throw new ImportException($msg);
}
@@ -1763,8 +2076,6 @@ function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
* @link http://docs.elgg.org/Entities/AccessControl
*/
function can_edit_entity($entity_guid, $user_guid = 0) {
- global $CONFIG;
-
$user_guid = (int)$user_guid;
$user = get_entity($user_guid);
if (!$user) {
@@ -1817,14 +2128,19 @@ function can_edit_entity_metadata($entity_guid, $user_guid = 0, $metadata = null
$return = null;
- if ($metadata->owner_guid == 0) {
+ if ($metadata && ($metadata->owner_guid == 0)) {
$return = true;
}
if (is_null($return)) {
$return = can_edit_entity($entity_guid, $user_guid);
}
- $user = get_entity($user_guid);
+ if ($user_guid) {
+ $user = get_entity($user_guid);
+ } else {
+ $user = elgg_get_logged_in_user_entity();
+ }
+
$params = array('entity' => $entity, 'user' => $user, 'metadata' => $metadata);
$return = elgg_trigger_plugin_hook('permissions_check:metadata', $entity->type, $params, $return);
return $return;
@@ -1883,7 +2199,7 @@ function get_entity_url($entity_guid) {
* @param string $entity_subtype The entity subtype
* @param string $function_name The function to register
*
- * @return true|false Depending on success
+ * @return bool Depending on success
* @see get_entity_url()
* @see ElggEntity::getURL()
* @since 1.8.0
@@ -1891,7 +2207,7 @@ function get_entity_url($entity_guid) {
function elgg_register_entity_url_handler($entity_type, $entity_subtype, $function_name) {
global $CONFIG;
- if (!is_callable($function_name)) {
+ if (!is_callable($function_name, true)) {
return false;
}
@@ -1919,7 +2235,7 @@ function elgg_register_entity_url_handler($entity_type, $entity_subtype, $functi
* @param string $type The type of entity (object, site, user, group)
* @param string $subtype The subtype to register (may be blank)
*
- * @return true|false Depending on success
+ * @return bool Depending on success
* @see get_registered_entity_types()
* @link http://docs.elgg.org/Search
* @link http://docs.elgg.org/Tutorials/Search
@@ -1928,7 +2244,7 @@ function elgg_register_entity_type($type, $subtype = null) {
global $CONFIG;
$type = strtolower($type);
- if (!in_array($type, array('object', 'site', 'group', 'user'))) {
+ if (!in_array($type, $CONFIG->entity_types)) {
return FALSE;
}
@@ -1956,14 +2272,14 @@ function elgg_register_entity_type($type, $subtype = null) {
* @param string $type The type of entity (object, site, user, group)
* @param string $subtype The subtype to register (may be blank)
*
- * @return true|false Depending on success
+ * @return bool Depending on success
* @see elgg_register_entity_type()
*/
function unregister_entity_type($type, $subtype) {
global $CONFIG;
$type = strtolower($type);
- if (!in_array($type, array('object', 'site', 'group', 'user'))) {
+ if (!in_array($type, $CONFIG->entity_types)) {
return FALSE;
}
@@ -2023,7 +2339,7 @@ function get_registered_entity_types($type = null) {
* @param string $type The type of entity (object, site, user, group)
* @param string $subtype The subtype (may be blank)
*
- * @return true|false Depending on whether or not the type has been registered
+ * @return bool Depending on whether or not the type has been registered
*/
function is_registered_entity_type($type, $subtype = null) {
global $CONFIG;
@@ -2051,15 +2367,18 @@ function is_registered_entity_type($type, $subtype = null) {
*
* @param array $page Page elements from pain page handler
*
- * @return void
+ * @return bool
* @elgg_page_handler view
+ * @access private
*/
function entities_page_handler($page) {
if (isset($page[0])) {
global $CONFIG;
set_input('guid', $page[0]);
include($CONFIG->path . "pages/entities/index.php");
+ return true;
}
+ return false;
}
/**
@@ -2120,34 +2439,16 @@ function elgg_list_registered_entities(array $options = array()) {
}
}
- $count = elgg_get_entities(array_merge(array('count' => TRUE), $options));
- $entities = elgg_get_entities($options);
-
- return elgg_view_entity_list($entities, $count, $options['offset'],
- $options['limit'], $options['full_view'], $options['list_type_toggle'], $options['pagination']);
-}
-
-/**
- * Check the recursive delete permissions token.
- *
- * If an entity is deleted recursively, a permissions override is required to allow
- * contained or owned entities to be removed.
- *
- * @access private
- * @return bool
- * @elgg_plugin_hook_handler permissions_check all
- * @elgg_plugin_hook_handler permissions_check:metadata all
- */
-function recursive_delete_permissions_check() {
- static $__RECURSIVE_DELETE_TOKEN;
-
- if ((elgg_is_logged_in()) && ($__RECURSIVE_DELETE_TOKEN)
- && (strcmp($__RECURSIVE_DELETE_TOKEN, md5(elgg_get_logged_in_user_guid())))) {
- return true;
+ if (!empty($options['type_subtype_pairs'])) {
+ $count = elgg_get_entities(array_merge(array('count' => TRUE), $options));
+ $entities = elgg_get_entities($options);
+ } else {
+ $count = 0;
+ $entities = array();
}
- // consult next function
- return NULL;
+ $options['count'] = $count;
+ return elgg_view_entity_list($entities, $options);
}
/**
@@ -2168,6 +2469,7 @@ function elgg_instanceof($entity, $type = NULL, $subtype = NULL, $class = NULL)
$return = ($entity instanceof ElggEntity);
if ($type) {
+ /* @var ElggEntity $entity */
$return = $return && ($entity->getType() == $type);
}
@@ -2185,8 +2487,6 @@ function elgg_instanceof($entity, $type = NULL, $subtype = NULL, $class = NULL)
/**
* Update the last_action column in the entities table for $guid.
*
- * This determines the sort order of 1.8's default river.
- *
* @warning This is different to time_updated. Time_updated is automatically set,
* while last_action is only set when explicitly called.
*
@@ -2194,7 +2494,8 @@ function elgg_instanceof($entity, $type = NULL, $subtype = NULL, $class = NULL)
* @param int $posted Timestamp of last action
*
* @return bool
- **/
+ * @access private
+ */
function update_entity_last_action($guid, $posted = NULL) {
global $CONFIG;
$guid = (int)$guid;
@@ -2223,27 +2524,36 @@ function update_entity_last_action($guid, $posted = NULL) {
*
* @return void
* @elgg_plugin_hook_handler gc system
+ * @access private
*/
function entities_gc() {
global $CONFIG;
- $tables = array ('sites_entity', 'objects_entity', 'groups_entity', 'users_entity');
+ $tables = array(
+ 'site' => 'sites_entity',
+ 'object' => 'objects_entity',
+ 'group' => 'groups_entity',
+ 'user' => 'users_entity'
+ );
- foreach ($tables as $table) {
- delete_data("DELETE from {$CONFIG->dbprefix}{$table}
- where guid NOT IN (SELECT guid from {$CONFIG->dbprefix}entities)");
+ foreach ($tables as $type => $table) {
+ delete_data("DELETE FROM {$CONFIG->dbprefix}{$table}
+ WHERE guid NOT IN (SELECT guid FROM {$CONFIG->dbprefix}entities)");
+ delete_data("DELETE FROM {$CONFIG->dbprefix}entities
+ WHERE type = '$type' AND guid NOT IN (SELECT guid FROM {$CONFIG->dbprefix}{$table})");
}
}
/**
* Runs unit tests for the entity objects.
*
- * @param sting $hook unit_test
+ * @param string $hook unit_test
* @param string $type system
* @param mixed $value Array of tests
* @param mixed $params Params
*
* @return array
+ * @access private
*/
function entities_test($hook, $type, $value, $params) {
global $CONFIG;
@@ -2256,17 +2566,13 @@ function entities_test($hook, $type, $value, $params) {
*
* @return void
* @elgg_event_handler init system
+ * @access private
*/
function entities_init() {
elgg_register_page_handler('view', 'entities_page_handler');
elgg_register_plugin_hook_handler('unit_test', 'system', 'entities_test');
- // Allow a permission override for recursive entity deletion
- // @todo Can this be done better?
- elgg_register_plugin_hook_handler('permissions_check', 'all', 'recursive_delete_permissions_check');
- elgg_register_plugin_hook_handler('permissions_check:metadata', 'all', 'recursive_delete_permissions_check');
-
elgg_register_plugin_hook_handler('gc', 'system', 'entities_gc');
}
@@ -2281,3 +2587,4 @@ elgg_register_plugin_hook_handler('volatile', 'metadata', 'volatile_data_export_
/** Register init system event **/
elgg_register_event_handler('init', 'system', 'entities_init');
+