diff options
Diffstat (limited to 'engine/lib')
93 files changed, 17320 insertions, 10013 deletions
diff --git a/engine/lib/access.php b/engine/lib/access.php index b87d150c0..de0693ea8 100644 --- a/engine/lib/access.php +++ b/engine/lib/access.php @@ -1,9 +1,9 @@ <?php /** - * Primary function for Elgg's entity and metadata access systems. + * Functions for Elgg's access system for entities, metadata, and annotations. * * Access is generally saved in the database as access_id. This corresponds to - * one of the ACCESS_* constants defined in {@link elgglib.php}, or the ID of an + * one of the ACCESS_* constants defined in {@link elgglib.php} or the ID of an * access collection. * * @package Elgg.Core @@ -12,30 +12,51 @@ */ /** + * Return an ElggCache static variable cache for the access caches + * + * @staticvar ElggStaticVariableCache $access_cache + * @return \ElggStaticVariableCache + * @access private + */ +function _elgg_get_access_cache() { + /** + * A default filestore cache using the dataroot. + */ + static $access_cache; + + if (!$access_cache) { + $access_cache = new ElggStaticVariableCache('access'); + } + + return $access_cache; +} + +/** * Return a string of access_ids for $user_id appropriate for inserting into an SQL IN clause. * * @uses get_access_array * - * @return string A list of access collections suitable for injection in an SQL call * @link http://docs.elgg.org/Access * @see get_access_array() * * @param int $user_id User ID; defaults to currently logged in user * @param int $site_id Site ID; defaults to current site - * @param bool $flush If set to true, will refresh the access list from the database + * @param bool $flush If set to true, will refresh the access list from the + * database rather than using this function's cache. * - * @return string + * @return string A list of access collections suitable for using in an SQL call + * @access private */ function get_access_list($user_id = 0, $site_id = 0, $flush = false) { global $CONFIG, $init_finished; - static $access_list; - - if (!isset($access_list) || !$init_finished) { - $access_list = array(); + $cache = _elgg_get_access_cache(); + + if ($flush) { + $cache->clear(); } if ($user_id == 0) { - $user_id = get_loggedin_userid(); + $user_id = elgg_get_logged_in_user_guid(); } if (($site_id == 0) && (isset($CONFIG->site_id))) { @@ -44,40 +65,55 @@ function get_access_list($user_id = 0, $site_id = 0, $flush = false) { $user_id = (int) $user_id; $site_id = (int) $site_id; - if (isset($access_list[$user_id])) { - return $access_list[$user_id]; - } + $hash = $user_id . $site_id . 'get_access_list'; - $access_list[$user_id] = "(" . implode(",", get_access_array($user_id, $site_id, $flush)) . ")"; + if ($cache[$hash]) { + return $cache[$hash]; + } + + $access_array = get_access_array($user_id, $site_id, $flush); + $access = "(" . implode(",", $access_array) . ")"; - return $access_list[$user_id]; + if ($init_finished) { + $cache[$hash] = $access; + } + + return $access; } /** * Returns an array of access IDs a user is permitted to see. * - * Can be overridden with the access:collections:read, user plugin hook. + * Can be overridden with the 'access:collections:read', 'user' plugin hook. + * + * This returns a list of all the collection ids a user owns or belongs + * to plus public and logged in access levels. If the user is an admin, it includes + * the private access level. * - * @param int $user_id User ID; defaults to currently logged in user - * @param int $site_id Site ID; defaults to current site - * @param boolean $flush If set to true, will refresh the access list from the database + * @internal this is only used in core for creating the SQL where clause when + * retrieving content from the database. The friends access level is handled by + * get_access_sql_suffix(). + * + * @see get_write_access_array() for the access levels that a user can write to. + * + * @param int $user_id User ID; defaults to currently logged in user + * @param int $site_id Site ID; defaults to current site + * @param bool $flush If set to true, will refresh the access ids from the + * database rather than using this function's cache. * * @return array An array of access collections ids - * @see get_access_list() */ function get_access_array($user_id = 0, $site_id = 0, $flush = false) { global $CONFIG, $init_finished; - // @todo everything from the db is cached. - // this cache might be redundant. - static $access_array; + $cache = _elgg_get_access_cache(); - if (!isset($access_array) || (!isset($init_finished)) || (!$init_finished)) { - $access_array = array(); + if ($flush) { + $cache->clear(); } if ($user_id == 0) { - $user_id = get_loggedin_userid(); + $user_id = elgg_get_logged_in_user_guid(); } if (($site_id == 0) && (isset($CONFIG->site_guid))) { @@ -87,35 +123,41 @@ function get_access_array($user_id = 0, $site_id = 0, $flush = false) { $user_id = (int) $user_id; $site_id = (int) $site_id; - if (empty($access_array[$user_id]) || $flush == true) { - $tmp_access_array = array(ACCESS_PUBLIC); + $hash = $user_id . $site_id . 'get_access_array'; + + if ($cache[$hash]) { + $access_array = $cache[$hash]; + } else { + $access_array = array(ACCESS_PUBLIC); // The following can only return sensible data if the user is logged in. - if (isloggedin()) { - $tmp_access_array[] = ACCESS_LOGGED_IN; + if (elgg_is_logged_in()) { + $access_array[] = ACCESS_LOGGED_IN; // Get ACL memberships $query = "SELECT am.access_collection_id" . " FROM {$CONFIG->dbprefix}access_collection_membership am" . " LEFT JOIN {$CONFIG->dbprefix}access_collections ag ON ag.id = am.access_collection_id" - . " WHERE am.user_guid = {$user_id} AND (ag.site_guid = {$site_id} OR ag.site_guid = 0)"; + . " WHERE am.user_guid = $user_id AND (ag.site_guid = $site_id OR ag.site_guid = 0)"; - if ($collections = get_data($query)) { + $collections = get_data($query); + if ($collections) { foreach ($collections as $collection) { if (!empty($collection->access_collection_id)) { - $tmp_access_array[] = $collection->access_collection_id; + $access_array[] = (int)$collection->access_collection_id; } } } // Get ACLs owned. $query = "SELECT ag.id FROM {$CONFIG->dbprefix}access_collections ag "; - $query .= "WHERE ag.owner_guid = {$user_id} AND (ag.site_guid = {$site_id} OR ag.site_guid = 0)"; + $query .= "WHERE ag.owner_guid = $user_id AND (ag.site_guid = $site_id OR ag.site_guid = 0)"; - if ($collections = get_data($query)) { + $collections = get_data($query); + if ($collections) { foreach ($collections as $collection) { if (!empty($collection->id)) { - $tmp_access_array[] = $collection->id; + $access_array[] = (int)$collection->id; } } } @@ -123,21 +165,21 @@ function get_access_array($user_id = 0, $site_id = 0, $flush = false) { $ignore_access = elgg_check_access_overrides($user_id); if ($ignore_access == true) { - $tmp_access_array[] = ACCESS_PRIVATE; + $access_array[] = ACCESS_PRIVATE; } - - $access_array[$user_id] = $tmp_access_array; - } else { - // No user id logged in so we can only access public info - $tmp_return = $tmp_access_array; } - } else { - $tmp_access_array = $access_array[$user_id]; + if ($init_finished) { + $cache[$hash] = $access_array; + } } - $options = array('user_id' => $user_id, 'site_id' => $site_id); - return elgg_trigger_plugin_hook('access:collections:read', 'user', $options, $tmp_access_array); + $options = array( + 'user_id' => $user_id, + 'site_id' => $site_id + ); + + return elgg_trigger_plugin_hook('access:collections:read', 'user', $options, $access_array); } /** @@ -157,7 +199,7 @@ function get_default_access(ElggUser $user = null) { return $CONFIG->default_access; } - if (!($user) && (!$user = get_loggedin_user())) { + if (!($user) && (!$user = elgg_get_logged_in_user_entity())) { return $CONFIG->default_access; } @@ -180,11 +222,9 @@ $ENTITY_SHOW_HIDDEN_OVERRIDE = false; /** * Show or hide disabled entities. * - * @access private - * * @param bool $show_hidden Show disabled entities. - * * @return void + * @access private */ function access_show_hidden_entities($show_hidden) { global $ENTITY_SHOW_HIDDEN_OVERRIDE; @@ -194,8 +234,8 @@ function access_show_hidden_entities($show_hidden) { /** * Return current status of showing disabled entities. * - * @access private * @return bool + * @access private */ function access_get_show_hidden_status() { global $ENTITY_SHOW_HIDDEN_OVERRIDE; @@ -203,48 +243,11 @@ function access_get_show_hidden_status() { } /** - * Add annotation restriction - * - * Returns an SQL fragment that is true (or optionally false) if the given user has - * added an annotation with the given name to the given entity. - * - * @todo This is fairly generic so perhaps it could be moved to annotations.php - * - * @param string $annotation_name Name of the annotation - * @param string $entity_guid SQL GUID of entity the annotation is attached to. - * @param string $owner_guid SQL string that evaluates to the GUID of the annotation owner - * @param boolean $exists If true, returns BOOL if the annotation exists - * - * @return string An SQL fragment suitable for inserting into a WHERE clause - * @todo Document and maybe even remove. At least rename to something that makes sense. - */ -function get_annotation_sql($annotation_name, $entity_guid, $owner_guid, $exists) { - global $CONFIG; - - if ($exists) { - $not = ''; - } else { - $not = 'NOT'; - } - - $sql = <<<END -$not EXISTS (SELECT * FROM {$CONFIG->dbprefix}annotations a -INNER JOIN {$CONFIG->dbprefix}metastrings ms ON (a.name_id = ms.id) -WHERE ms.string = '$annotation_name' -AND a.entity_guid = $entity_guid -AND a.owner_guid = $owner_guid) -END; - return $sql; -} - -/** * Returns the SQL where clause for a table with a access_id and enabled columns. * - * This handles returning where clauses for ACCESS_FRIENDS, and the currently - * unused block and filter lists. - * - * @warning If an admin is logged in or {@link elgg_set_ignore_access()} is true, - * this will return blank. + * This handles returning where clauses for ACCESS_FRIENDS and the currently + * unused block and filter lists in addition to using get_access_list() for + * access collections and the standard access levels. * * @param string $table_prefix Optional table. prefix for the access code. * @param int $owner The guid to check access for. Defaults to logged in user. @@ -260,11 +263,11 @@ function get_access_sql_suffix($table_prefix = '', $owner = null) { $enemies_bit = ""; if ($table_prefix) { - $table_prefix = sanitise_string($table_prefix) . "."; + $table_prefix = sanitise_string($table_prefix) . "."; } if (!isset($owner)) { - $owner = get_loggedin_userid(); + $owner = elgg_get_logged_in_user_guid(); } if (!$owner) { @@ -277,6 +280,7 @@ function get_access_sql_suffix($table_prefix = '', $owner = null) { if ($ignore_access) { $sql = " (1 = 1) "; } else if ($owner != -1) { + // we have an entity's guid and auto check for friend relationships $friends_bit = "{$table_prefix}access_id = " . ACCESS_FRIENDS . " AND {$table_prefix}owner_guid IN ( SELECT guid_one FROM {$CONFIG->dbprefix}entity_relationships @@ -285,14 +289,15 @@ function get_access_sql_suffix($table_prefix = '', $owner = null) { $friends_bit = '(' . $friends_bit . ') OR '; + // @todo untested and unsupported at present if ((isset($CONFIG->user_block_and_filter_enabled)) && ($CONFIG->user_block_and_filter_enabled)) { // check to see if the user is in the entity owner's block list // or if the entity owner is in the user's filter list // if so, disallow access - $enemies_bit = get_annotation_sql('elgg_block_list', "{$table_prefix}owner_guid", $owner, false); + $enemies_bit = get_access_restriction_sql('elgg_block_list', "{$table_prefix}owner_guid", $owner, false); $enemies_bit = '(' . $enemies_bit - . ' AND ' . get_annotation_sql('elgg_filter_list', $owner, "{$table_prefix}owner_guid", false) + . ' AND ' . get_access_restriction_sql('elgg_filter_list', $owner, "{$table_prefix}owner_guid", false) . ')'; } } @@ -319,19 +324,59 @@ function get_access_sql_suffix($table_prefix = '', $owner = null) { } /** - * Can $user access $entity. + * Get the where clause for an access restriction based on annotations + * + * Returns an SQL fragment that is true (or optionally false) if the given user has + * added an annotation with the given name to the given entity. + * + * @warning this is a private function for an untested capability and will likely + * be removed from a future version of Elgg. + * + * @param string $annotation_name Name of the annotation + * @param string $entity_guid SQL GUID of entity the annotation is attached to. + * @param string $owner_guid SQL string that evaluates to the GUID of the annotation owner + * @param boolean $exists If true, returns BOOL if the annotation exists + * + * @return string An SQL fragment suitable for inserting into a WHERE clause + * @access private + */ +function get_access_restriction_sql($annotation_name, $entity_guid, $owner_guid, $exists) { + global $CONFIG; + + if ($exists) { + $not = ''; + } else { + $not = 'NOT'; + } + + $sql = <<<END +$not EXISTS (SELECT * FROM {$CONFIG->dbprefix}annotations a +INNER JOIN {$CONFIG->dbprefix}metastrings ms ON (a.name_id = ms.id) +WHERE ms.string = '$annotation_name' +AND a.entity_guid = $entity_guid +AND a.owner_guid = $owner_guid) +END; + return $sql; +} + +/** + * Can a user access an entity. * * @warning If a logged in user doesn't have access to an entity, the * core engine will not load that entity. * - * @tip This is mostly useful for checking if a 3rd user has access - * to an entity that is currently loaded. + * @tip This is mostly useful for checking if a user other than the logged in + * user has access to an entity that is currently loaded. + * + * @todo This function would be much more useful if we could pass the guid of the + * entity to test access for. We need to be able to tell whether the entity exists + * and whether the user has access to the entity. * * @param ElggEntity $entity The entity to check access for. * @param ElggUser $user Optionally user to check access for. Defaults to - * logged in user (which doesn't make sense). + * logged in user (which is a useless default). * - * @return boolean True if the user can access the entity + * @return bool * @link http://docs.elgg.org/Access */ function has_access_to_entity($entity, $user = null) { @@ -354,23 +399,41 @@ function has_access_to_entity($entity, $user = null) { } /** - * Returns an array of access permissions that the user is allowed to save objects with. - * Permissions are of the form ('id' => 'Description') + * Returns an array of access permissions that the user is allowed to save content with. + * Permissions returned are of the form (id => 'name'). + * + * Example return value in English: + * array( + * 0 => 'Private', + * -2 => 'Friends', + * 1 => 'Logged in users', + * 2 => 'Public', + * 34 => 'My favorite friends', + * ); + * + * Plugin hook of 'access:collections:write', 'user' + * + * @warning this only returns access collections that the user owns plus the + * standard access levels. It does not return access collections that the user + * belongs to such as the access collection for a group. * * @param int $user_id The user's GUID. * @param int $site_id The current site. - * @param bool $flush If this is set to true, this will ignore any cached version + * @param bool $flush If this is set to true, this will ignore a cached access array * * @return array List of access permissions * @link http://docs.elgg.org/Access */ function get_write_access_array($user_id = 0, $site_id = 0, $flush = false) { - global $CONFIG; - //@todo this is probably not needed since caching happens at the DB level. - static $access_array; + global $CONFIG, $init_finished; + $cache = _elgg_get_access_cache(); + + if ($flush) { + $cache->clear(); + } if ($user_id == 0) { - $user_id = get_loggedin_userid(); + $user_id = elgg_get_logged_in_user_guid(); } if (($site_id == 0) && (isset($CONFIG->site_id))) { @@ -380,34 +443,78 @@ function get_write_access_array($user_id = 0, $site_id = 0, $flush = false) { $user_id = (int) $user_id; $site_id = (int) $site_id; - if (empty($access_array[$user_id]) || $flush == true) { + $hash = $user_id . $site_id . 'get_write_access_array'; + + if ($cache[$hash]) { + $access_array = $cache[$hash]; + } else { + // @todo is there such a thing as public write access? + $access_array = array( + ACCESS_PRIVATE => elgg_echo("PRIVATE"), + ACCESS_FRIENDS => elgg_echo("access:friends:label"), + ACCESS_LOGGED_IN => elgg_echo("LOGGED_IN"), + ACCESS_PUBLIC => elgg_echo("PUBLIC") + ); + $query = "SELECT ag.* FROM {$CONFIG->dbprefix}access_collections ag "; - $query .= " WHERE (ag.site_guid = {$site_id} OR ag.site_guid = 0)"; - $query .= " AND (ag.owner_guid = {$user_id})"; - $query .= " AND ag.id >= 3"; - - $tmp_access_array = array( - ACCESS_PRIVATE => elgg_echo("PRIVATE"), - ACCESS_FRIENDS => elgg_echo("access:friends:label"), - ACCESS_LOGGED_IN => elgg_echo("LOGGED_IN"), - ACCESS_PUBLIC => elgg_echo("PUBLIC") - ); - if ($collections = get_data($query)) { + $query .= " WHERE (ag.site_guid = $site_id OR ag.site_guid = 0)"; + $query .= " AND (ag.owner_guid = $user_id)"; + + $collections = get_data($query); + if ($collections) { foreach ($collections as $collection) { - $tmp_access_array[$collection->id] = $collection->name; + $access_array[$collection->id] = $collection->name; } } - $access_array[$user_id] = $tmp_access_array; + if ($init_finished) { + $cache[$hash] = $access_array; + } + } + + $options = array( + 'user_id' => $user_id, + 'site_id' => $site_id + ); + return elgg_trigger_plugin_hook('access:collections:write', 'user', + $options, $access_array); +} + +/** + * Can the user change this access collection? + * + * Use the plugin hook of 'access:collections:write', 'user' to change this. + * @see get_write_access_array() for details on the hook. + * + * Respects access control disabling for admin users and {@see elgg_set_ignore_access()} + * + * @see get_write_access_array() + * + * @param int $collection_id The collection id + * @param mixed $user_guid The user GUID to check for. Defaults to logged in user. + * @return bool + */ +function can_edit_access_collection($collection_id, $user_guid = null) { + if ($user_guid) { + $user = get_entity((int) $user_guid); } else { - $tmp_access_array = $access_array[$user_id]; + $user = elgg_get_logged_in_user_entity(); + } + + $collection = get_access_collection($collection_id); + + if (!($user instanceof ElggUser) || !$collection) { + return false; } - $options = array('user_id' => $user_id, 'site_id' => $site_id); - $tmp_access_array = elgg_trigger_plugin_hook('access:collections:write', 'user', - $options, $tmp_access_array); + $write_access = get_write_access_array($user->getGUID(), 0, true); - return $tmp_access_array; + // don't ignore access when checking users. + if ($user_guid) { + return array_key_exists($collection_id, $write_access); + } else { + return elgg_get_ignore_access() || array_key_exists($collection_id, $write_access); + } } /** @@ -416,6 +523,8 @@ function get_write_access_array($user_id = 0, $site_id = 0, $flush = false) { * Access colletions allow plugins and users to create granular access * for entities. * + * Triggers plugin hook 'access:collections:addcollection', 'collection' + * * @internal Access collections are stored in the access_collections table. * Memberships to collections are in access_collections_membership. * @@ -423,7 +532,7 @@ function get_write_access_array($user_id = 0, $site_id = 0, $flush = false) { * @param int $owner_guid The GUID of the owner (default: currently logged in user). * @param int $site_guid The GUID of the site (default: current site). * - * @return int|false Depending on success (the collection ID if successful). + * @return int|false The collection ID if successful and false on failure. * @link http://docs.elgg.org/Access/Collections * @see update_access_collection() * @see delete_access_collection() @@ -437,7 +546,7 @@ function create_access_collection($name, $owner_guid = 0, $site_guid = 0) { } if ($owner_guid == 0) { - $owner_guid = get_loggedin_userid(); + $owner_guid = elgg_get_logged_in_user_guid(); } if (($site_guid == 0) && (isset($CONFIG->site_guid))) { $site_guid = $CONFIG->site_guid; @@ -448,7 +557,8 @@ function create_access_collection($name, $owner_guid = 0, $site_guid = 0) { SET name = '{$name}', owner_guid = {$owner_guid}, site_guid = {$site_guid}"; - if (!$id = insert_data($q)) { + $id = insert_data($q); + if (!$id) { return false; } @@ -467,7 +577,7 @@ function create_access_collection($name, $owner_guid = 0, $site_guid = 0) { * Updates the membership in an access collection. * * @warning Expects a full list of all members that should - * be part o the access collection + * be part of the access collection * * @note This will run all hooks associated with adding or removing * members to access collections. @@ -475,45 +585,36 @@ function create_access_collection($name, $owner_guid = 0, $site_guid = 0) { * @param int $collection_id The ID of the collection. * @param array $members Array of member GUIDs * - * @return true|false Depending on success + * @return bool * @link http://docs.elgg.org/Access/Collections * @see add_user_to_access_collection() * @see remove_user_from_access_collection() */ function update_access_collection($collection_id, $members) { - global $CONFIG; + $acl = get_access_collection($collection_id); - $collection_id = (int) $collection_id; + if (!$acl) { + return false; + } $members = (is_array($members)) ? $members : array(); - $collections = get_write_access_array(); + $cur_members = get_members_of_access_collection($collection_id, true); + $cur_members = (is_array($cur_members)) ? $cur_members : array(); - if (array_key_exists($collection_id, $collections)) { - $cur_members = get_members_of_access_collection($collection_id, true); - $cur_members = (is_array($cur_members)) ? $cur_members : array(); + $remove_members = array_diff($cur_members, $members); + $add_members = array_diff($members, $cur_members); - $remove_members = array_diff($cur_members, $members); - $add_members = array_diff($members, $cur_members); - - $params = array( - 'collection_id' => $collection_id, - 'members' => $members, - 'add_members' => $add_members, - 'remove_members' => $remove_members - ); - - foreach ($add_members as $guid) { - add_user_to_access_collection($guid, $collection_id); - } + $result = true; - foreach ($remove_members as $guid) { - remove_user_from_access_collection($guid, $collection_id); - } + foreach ($add_members as $guid) { + $result = $result && add_user_to_access_collection($guid, $collection_id); + } - return true; + foreach ($remove_members as $guid) { + $result = $result && remove_user_from_access_collection($guid, $collection_id); } - return false; + return $result; } /** @@ -527,27 +628,25 @@ function update_access_collection($collection_id, $members) { * @see update_access_collection() */ function delete_access_collection($collection_id) { + global $CONFIG; + $collection_id = (int) $collection_id; - $collections = get_write_access_array(null, null, TRUE); $params = array('collection_id' => $collection_id); if (!elgg_trigger_plugin_hook('access:collections:deletecollection', 'collection', $params, true)) { return false; } - if (array_key_exists($collection_id, $collections)) { - global $CONFIG; - $query = "delete from {$CONFIG->dbprefix}access_collection_membership" - . " where access_collection_id = {$collection_id}"; - delete_data($query); + // Deleting membership doesn't affect result of deleting ACL. + $q = "DELETE FROM {$CONFIG->dbprefix}access_collection_membership + WHERE access_collection_id = {$collection_id}"; + delete_data($q); - $query = "delete from {$CONFIG->dbprefix}access_collections where id = {$collection_id}"; - delete_data($query); - return true; - } else { - return false; - } + $q = "DELETE FROM {$CONFIG->dbprefix}access_collections + WHERE id = {$collection_id}"; + $result = delete_data($q); + return (bool)$result; } /** @@ -556,9 +655,11 @@ function delete_access_collection($collection_id) { * @note This doesn't return the members of an access collection, * just the database row of the actual collection. * + * @see get_members_of_access_collection() + * * @param int $collection_id The collection ID * - * @return array|false + * @return object|false */ function get_access_collection($collection_id) { global $CONFIG; @@ -573,91 +674,88 @@ function get_access_collection($collection_id) { /** * Adds a user to an access collection. * - * Emits the access:collections:add_user, collection plugin hook. + * Triggers the 'access:collections:add_user', 'collection' plugin hook. * * @param int $user_guid The GUID of the user to add * @param int $collection_id The ID of the collection to add them to * - * @return true|false Depending on success - * @link http://docs.elgg.org/Access/Collections + * @return bool * @see update_access_collection() * @see remove_user_from_access_collection() + * @link http://docs.elgg.org/Access/Collections */ function add_user_to_access_collection($user_guid, $collection_id) { + global $CONFIG; + $collection_id = (int) $collection_id; $user_guid = (int) $user_guid; - $collections = get_write_access_array(); + $user = get_user($user_guid); + + $collection = get_access_collection($collection_id); - if (!($collection = get_access_collection($collection_id))) { + if (!($user instanceof Elgguser) || !$collection) { return false; } - if ((array_key_exists($collection_id, $collections) || $collection->owner_guid == 0) - && $user = get_user($user_guid)) { - global $CONFIG; - - $params = array( - 'collection_id' => $collection_id, - 'user_guid' => $user_guid - ); - - if (!elgg_trigger_plugin_hook('access:collections:add_user', 'collection', $params, true)) { - return false; - } - - try { - $query = "insert into {$CONFIG->dbprefix}access_collection_membership" - . " set access_collection_id = {$collection_id}, user_guid = {$user_guid}"; - insert_data($query); - } catch (DatabaseException $e) { - // nothing. - } - return true; + $params = array( + 'collection_id' => $collection_id, + 'user_guid' => $user_guid + ); + $result = elgg_trigger_plugin_hook('access:collections:add_user', 'collection', $params, true); + if ($result == false) { + return false; } - return false; + // if someone tries to insert the same data twice, we do a no-op on duplicate key + $q = "INSERT INTO {$CONFIG->dbprefix}access_collection_membership + SET access_collection_id = $collection_id, user_guid = $user_guid + ON DUPLICATE KEY UPDATE user_guid = user_guid"; + $result = insert_data($q); + + return $result !== false; } /** * Removes a user from an access collection. * - * Emits the access:collections:remove_user, collection plugin hook. + * Triggers the 'access:collections:remove_user', 'collection' plugin hook. * * @param int $user_guid The user GUID * @param int $collection_id The access collection ID * - * @return true|false Depending on success + * @return bool + * @see update_access_collection() + * @see remove_user_from_access_collection() + * @link http://docs.elgg.org/Access/Collections */ function remove_user_from_access_collection($user_guid, $collection_id) { + global $CONFIG; + $collection_id = (int) $collection_id; $user_guid = (int) $user_guid; - $collections = get_write_access_array(); - $user = $user = get_user($user_guid); + $user = get_user($user_guid); + + $collection = get_access_collection($collection_id); - if (!($collection = get_access_collection($collection_id))) { + if (!($user instanceof Elgguser) || !$collection) { return false; } - if ((array_key_exists($collection_id, $collections) || $collection->owner_guid == 0) && $user) { - global $CONFIG; - $params = array( - 'collection_id' => $collection_id, - 'user_guid' => $user_guid - ); - - if (!elgg_trigger_plugin_hook('access:collections:remove_user', 'collection', $params, true)) { - return false; - } - - delete_data("delete from {$CONFIG->dbprefix}access_collection_membership " - . "where access_collection_id = {$collection_id} and user_guid = {$user_guid}"); - - return true; + $params = array( + 'collection_id' => $collection_id, + 'user_guid' => $user_guid + ); + if (!elgg_trigger_plugin_hook('access:collections:remove_user', 'collection', $params, true)) { + return false; } - return false; + $q = "DELETE FROM {$CONFIG->dbprefix}access_collection_membership + WHERE access_collection_id = {$collection_id} + AND user_guid = {$user_guid}"; + + return (bool)delete_data($q); } /** @@ -725,109 +823,13 @@ function get_members_of_access_collection($collection, $idonly = FALSE) { } /** - * Displays a user's access collections, using the friends/collections view - * - * @param int $owner_guid The GUID of the owning user - * - * @return string A formatted rendition of the collections - * @todo Move to the friends/collection.php page. - */ -function elgg_view_access_collections($owner_guid) { - if ($collections = get_user_access_collections($owner_guid)) { - foreach ($collections as $key => $collection) { - $collections[$key]->members = get_members_of_access_collection($collection->id, true); - $collections[$key]->entities = get_user_friends($owner_guid, "", 9999); - } - } - - return elgg_view('friends/collections', array('collections' => $collections)); -} - -/** - * Get entities with the specified access collection id. - * - * @deprecated 1.7. Use elgg_get_entities_from_access_id() - * - * @param int $collection_id ID of collection - * @param string $entity_type Type of entities - * @param string $entity_subtype Subtype of entities - * @param int $owner_guid Guid of owner - * @param int $limit Limit of number of entities to return - * @param int $offset Skip this many entities - * @param string $order_by Column to order by - * @param int $site_guid The site guid - * @param bool $count Return a count or entities - * - * @return array - */ -function get_entities_from_access_id($collection_id, $entity_type = "", $entity_subtype = "", - $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) { - // log deprecated warning - elgg_deprecated_notice('get_entities_from_access_id() was deprecated by elgg_get_entities()', 1.7); - - if (!$collection_id) { - return FALSE; - } - - // build the options using given parameters - $options = array(); - $options['limit'] = $limit; - $options['offset'] = $offset; - $options['count'] = $count; - - if ($entity_type) { - $options['type'] = sanitise_string($entity_type); - } - - if ($entity_subtype) { - $options['subtype'] = $entity_subtype; - } - - if ($site_guid) { - $options['site_guid'] = $site_guid; - } - - if ($order_by) { - $options['order_by'] = sanitise_string("e.time_created, $order_by"); - } - - if ($owner_guid) { - if (is_array($owner_guid)) { - $options['owner_guids'] = $owner_guid; - } else { - $options['owner_guid'] = $owner_guid; - } - } - - if ($site_guid) { - $options['site_guid'] = $site_guid; - } - - $options['access_id'] = $collection_id; - - return elgg_get_entities_from_access_id($options); -} - -/** - * @deprecated 1.7 - */ -function get_entities_from_access_collection($collection_id, $entity_type = "", $entity_subtype = "", - $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) { - - elgg_deprecated_notice('get_entities_from_access_collection() was deprecated by elgg_get_entities()', 1.7); - - return get_entities_from_access_id($collection_id, $entity_type, $entity_subtype, - $owner_guid, $limit, $offset, $order_by, $site_guid, $count); -} - -/** * Return entities based upon access id. * - * @param array $options Any options accepted by {@link elgg_get_entities()} and: + * @param array $options Any options accepted by {@link elgg_get_entities()} and * access_id => int The access ID of the entity. * * @see elgg_get_entities() - * @return array + * @return mixed If count, int. If not count, array. false on errors. * @since 1.7.0 */ function elgg_get_entities_from_access_id(array $options = array()) { @@ -860,61 +862,37 @@ function elgg_get_entities_from_access_id(array $options = array()) { * @see elgg_list_entities() * @see elgg_get_entities_from_access_id() * - * @return str + * @return string */ function elgg_list_entities_from_access_id(array $options = array()) { return elgg_list_entities($options, 'elgg_get_entities_from_access_id'); } /** - * @return str - * @deprecated 1.8 Use elgg_list_entities_from_access_id() - */ -function list_entities_from_access_id($access_id, $entity_type = "", $entity_subtype = "", - $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true) { - - elgg_deprecated_notice("All list_entities* functions were deprecated in 1.8. Use elgg_list_entities* instead.", 1.8); - - echo elgg_list_entities_from_access_id(array( - 'access_id' => $access_id, - 'types' => $entity_type, - 'subtypes' => $entity_subtype, - 'owner_guids' => $owner_guid, - 'limit' => $limit, - 'full_view' => $fullview, - 'list_type_toggle' => $listtypetoggle, - 'pagination' => $pagination, - )); -} - -/** * Return the name of an ACCESS_* constant or a access collection, * but only if the user has write access on that ACL. * * @warning This function probably doesn't work how it's meant to. * - * @param int $entity_accessid The entity's access id + * @param int $entity_access_id The entity's access id * - * @return string e.g. Public, Private etc + * @return string 'Public', 'Private', etc. * @since 1.7.0 * @todo I think this probably wants get_access_array() instead of get_write_access_array(), * but those two functions return different types of arrays. */ -function get_readable_access_level($entity_accessid) { - $access = (int) $entity_accessid; +function get_readable_access_level($entity_access_id) { + $access = (int) $entity_access_id; //get the access level for object in readable string $options = get_write_access_array(); - //@todo Really? Use array_key_exists() - foreach ($options as $key => $option) { - if ($key == $access) { - $entity_acl = htmlentities($option, ENT_QUOTES, 'UTF-8'); - return $entity_acl; - break; - } + if (array_key_exists($access, $options)) { + return $options[$access]; } - return false; + + // return 'Limited' if the user does not have access to the access collection + return elgg_echo('access:limited:label'); } /** @@ -923,13 +901,15 @@ function get_readable_access_level($entity_accessid) { * The access system will not return entities in any getter * functions if the user doesn't have access. * - * @internal For performance reasons this is done at the database level. + * @internal For performance reasons this is done at the database access clause level. * * @tip Use this to access entities in automated scripts * when no user is logged in. * - * @warning This will not show disabled entities. Use {@link $ENTITY_SHOW_HIDDEN_OVERRIDE} - * for that. + * @note This clears the access cache. + * + * @warning This will not show disabled entities. + * Use {@link access_show_hidden_entities()} to access disabled entities. * * @param bool $ignore If true, disables all access checks. * @@ -939,6 +919,8 @@ function get_readable_access_level($entity_accessid) { * @see elgg_get_ignore_access() */ function elgg_set_ignore_access($ignore = true) { + $cache = _elgg_get_access_cache(); + $cache->clear(); $elgg_access = elgg_get_access_object(); return $elgg_access->setIgnoreAccess($ignore); } @@ -956,17 +938,20 @@ function elgg_get_ignore_access() { } /** - * Decides if the access system is being ignored. + * Decides if the access system should be ignored for a user. * - * The access system can be ignored if 1) an admin user is logged in - * or 2) {@link elgg_set_ignore_access()} was called with true. + * Returns true (meaning ignore access) if either of these 2 conditions are true: + * 1) an admin user guid is passed to this function. + * 2) {@link elgg_get_ignore_access()} returns true. * - * @param mixed $user_guid The user to check against. Defaults to logged in. + * @see elgg_set_ignore_access() + * + * @param int $user_guid The user to check against. * * @return bool * @since 1.7.0 */ -function elgg_check_access_overrides($user_guid = null) { +function elgg_check_access_overrides($user_guid = 0) { if (!$user_guid || $user_guid <= 0) { $is_admin = false; } else { @@ -979,6 +964,7 @@ function elgg_check_access_overrides($user_guid = null) { /** * Returns the ElggAccess object. * + * // @todo comment is incomplete * This is used to * * @return ElggAccess @@ -1000,7 +986,8 @@ function elgg_get_access_object() { * * @global bool $init_finished * @access private - * @todo investigate why this is needed + * @todo This is required to tell the access system to start caching because + * calls are made while in ignore access mode and before the user is logged in. */ $init_finished = false; @@ -1018,17 +1005,35 @@ function access_init() { } /** - * Check if the access system should be overridden. + * Overrides the access system if appropriate. * * Allows admin users and calls after {@link elgg_set_ignore_access} to - * by pass the access system. + * bypass the access system. + * + * Registered for the 'permissions_check', 'all' and the + * 'container_permissions_check', 'all' plugin hooks. + * + * Returns true to override the access system or null if no change is needed. * + * @param string $hook + * @param string $type + * @param bool $value + * @param array $params * @return true|null - * @since 1.7.0 - * @elgg_event_handler permissions_check all + * @access private */ -function elgg_override_permissions_hook() { - $user_guid = get_loggedin_userid(); +function elgg_override_permissions($hook, $type, $value, $params) { + $user = elgg_extract('user', $params); + if ($user) { + $user_guid = $user->getGUID(); + } else { + $user_guid = elgg_get_logged_in_user_guid(); + } + + // don't do this so ignore access still works with no one logged in + //if (!$user instanceof ElggUser) { + // return false; + //} // check for admin if ($user_guid && elgg_is_admin_user($user_guid)) { @@ -1044,9 +1049,30 @@ function elgg_override_permissions_hook() { return NULL; } -// This function will let us know when 'init' has finished -elgg_register_event_handler('init', 'system', 'access_init', 9999); +/** + * Runs unit tests for the entities object. + * + * @param string $hook + * @param string $type + * @param array $value + * @param array $params + * @return array + * + * @access private + */ +function access_test($hook, $type, $value, $params) { + global $CONFIG; + + $value[] = $CONFIG->path . 'engine/tests/api/access_collections.php'; + return $value; +} + +// Tell the access functions the system has booted, plugins are loaded, +// and the user is logged in so it can start caching +elgg_register_event_handler('ready', 'system', 'access_init'); // For overrided permissions -elgg_register_plugin_hook_handler('permissions_check', 'all', 'elgg_override_permissions_hook'); -elgg_register_plugin_hook_handler('container_permissions_check', 'all', 'elgg_override_permissions_hook');
\ No newline at end of file +elgg_register_plugin_hook_handler('permissions_check', 'all', 'elgg_override_permissions'); +elgg_register_plugin_hook_handler('container_permissions_check', 'all', 'elgg_override_permissions'); + +elgg_register_plugin_hook_handler('unit_test', 'system', 'access_test');
\ No newline at end of file diff --git a/engine/lib/actions.php b/engine/lib/actions.php index d95fe0435..8047914ac 100644 --- a/engine/lib/actions.php +++ b/engine/lib/actions.php @@ -2,21 +2,23 @@ /** * Elgg Actions * - * Actions are the primary controllers (The C in MVC) in Elgg. They are - * registered by {@link register_elgg_action()} and are called either by URL - * http://elggsite.org/action/action_name or {@link action($action_name}. For - * URLs, a rewrite rule in .htaccess passes the action name to - * engine/handlers/action_handler.php, which dispatches the action. + * Actions are one of the primary controllers (The C in MVC) in Elgg. They are + * registered by {@link register_elgg_action()} and are called by URL + * http://elggsite.org/action/action_name. For URLs, a rewrite rule in + * .htaccess passes the action name to engine/handlers/action_handler.php, + * which dispatches the request for the action. * - * An action name should be registered to exactly one file in the system, usually under - * the actions/ directory. + * An action name must be registered to a file in the system. Core actions are + * found in /actions/ and plugin actions are usually under /mod/<plugin>/actions/. + * It is recommended that actions be namespaced to avoid collisions. * * All actions require security tokens. Using the {@elgg_view input/form} view - * will automatically add tokens as hidden inputs. To manually add hidden inputs, - * use the {@elgg_view input/securitytoken} view. + * will automatically add tokens as hidden inputs as will the elgg_view_form() + * function. To manually add hidden inputs, use the {@elgg_view input/securitytoken} view. * * To include security tokens for actions called via GET, use - * {@link elgg_add_security_tokens_to_url()}. + * {@link elgg_add_security_tokens_to_url()} or specify is_action as true when + * using {@lgg_view output/url}. * * Action tokens can be manually generated by using {@link generate_action_token()}. * @@ -31,30 +33,30 @@ */ /** -* Perform an action. -* -* This function executes the action with name $action as -* registered by {@link elgg_register_action()}. -* -* The plugin hook action, $action_name will be emitted before -* the action is executed. If a handler returns false, it will -* prevent the action from being called. -* -* @note If an action isn't registered in the system or is registered -* to an unavailable file the user will be forwarded to the site front -* page and an error will be emitted via {@link register_error()}. -* -* @warning All actions require {@link http://docs.elgg.org/Actions/Tokens Action Tokens}. -* @warning Most plugin shouldn't call this manually. -* -* @param string $action The requested action -* @param string $forwarder Optionally, the location to forward to -* -* @link http://docs.elgg.org/Actions -* @see elgg_register_action() -* -* @return void -*/ + * Perform an action. + * + * This function executes the action with name $action as registered + * by {@link elgg_register_action()}. + * + * The plugin hook 'action', $action_name will be triggered before the action + * is executed. If a handler returns false, it will prevent the action script + * from being called. + * + * @note If an action isn't registered in the system or is registered + * to an unavailable file the user will be forwarded to the site front + * page and an error will be emitted via {@link register_error()}. + * + * @warning All actions require {@link http://docs.elgg.org/Actions/Tokens Action Tokens}. + * + * @param string $action The requested action + * @param string $forwarder Optionally, the location to forward to + * + * @link http://docs.elgg.org/Actions + * @see elgg_register_action() + * + * @return void + * @access private + */ function action($action, $forwarder = "") { global $CONFIG; @@ -63,67 +65,52 @@ function action($action, $forwarder = "") { // @todo REMOVE THESE ONCE #1509 IS IN PLACE. // Allow users to disable plugins without a token in order to // remove plugins that are incompatible. - // Login and logout are for convenience. + // Logout for convenience. // file/download (see #2010) $exceptions = array( 'admin/plugins/disable', 'logout', - 'login', 'file/download', ); if (!in_array($action, $exceptions)) { - // All actions require a token. - action_gatekeeper(); + action_gatekeeper($action); } $forwarder = str_replace(elgg_get_site_url(), "", $forwarder); $forwarder = str_replace("http://", "", $forwarder); $forwarder = str_replace("@", "", $forwarder); - if (substr($forwarder, 0, 1) == "/") { $forwarder = substr($forwarder, 1); } - if (isset($CONFIG->actions[$action])) { - if (isadminloggedin() || ($CONFIG->actions[$action]['access'] !== 'admin')) { - if (isloggedin() || ($CONFIG->actions[$action]['access'] === 'public')) { - - // Trigger action event - // @todo This is only called before the primary action is called. - $event_result = true; - $event_result = elgg_trigger_plugin_hook('action', $action, null, $event_result); - - // Include action - // Event_result being false doesn't produce an error - // since i assume this will be handled in the hook itself. - // @todo make this better! - if ($event_result) { - if (!include($CONFIG->actions[$action]['file'])) { - register_error(elgg_echo('actionnotfound', array($action))); - } - } - } else { - register_error(elgg_echo('actionloggedout')); + if (!isset($CONFIG->actions[$action])) { + register_error(elgg_echo('actionundefined', array($action))); + } elseif (!elgg_is_admin_logged_in() && ($CONFIG->actions[$action]['access'] === 'admin')) { + register_error(elgg_echo('actionunauthorized')); + } elseif (!elgg_is_logged_in() && ($CONFIG->actions[$action]['access'] !== 'public')) { + register_error(elgg_echo('actionloggedout')); + } else { + // Returning falsy doesn't produce an error + // We assume this will be handled in the hook itself. + if (elgg_trigger_plugin_hook('action', $action, null, true)) { + if (!include($CONFIG->actions[$action]['file'])) { + register_error(elgg_echo('actionnotfound', array($action))); } - } else { - register_error(elgg_echo('actionunauthorized')); } - } else { - register_error(elgg_echo('actionundefined', array($action))); } + $forwarder = empty($forwarder) ? REFERER : $forwarder; forward($forwarder); } /** * Registers an action. * - * Actions are registered to a single file in the system and are executed - * either by the URL http://elggsite.org/action/action_name or by calling - * {@link action()}. + * Actions are registered to a script in the system and are executed + * either by the URL http://elggsite.org/action/action_name/. * - * $file must be the full path of the file to register, or a path relative + * $filename must be the full path of the file to register, or a path relative * to the core actions/ dir. * * Actions should be namedspaced for your plugin. Example: @@ -131,10 +118,9 @@ function action($action, $forwarder = "") { * elgg_register_action('myplugin/save_settings', ...); * </code> * - * @tip Put action files under the actions/ directory of your plugin. + * @tip Put action files under the actions/<plugin_name> directory of your plugin. * - * @tip You don't need to include engine/start.php, call {@link gatekeeper()}, - * or call {@link admin_gatekeeper()}. + * @tip You don't need to include engine/start.php in your action files. * * @internal Actions are saved in $CONFIG->actions as an array in the form: * <code> @@ -145,13 +131,15 @@ function action($action, $forwarder = "") { * </code> * * @param string $action The name of the action (eg "register", "account/settings/save") - * @param string $filename Optionally, the filename where this action is located - * @param string $access Who is allowed to execute this action + * @param string $filename Optionally, the filename where this action is located. If not specified, + * will assume the action is in elgg/actions/<action>.php + * @param string $access Who is allowed to execute this action: public, logged_in, admin. + * (default: logged_in) * * @see action() * @see http://docs.elgg.org/Actions * - * @return true + * @return bool */ function elgg_register_action($action, $filename = "", $access = 'logged_in') { global $CONFIG; @@ -181,33 +169,51 @@ function elgg_register_action($action, $filename = "", $access = 'logged_in') { } /** - * @deprecated 1.8 Use {@link elgg_register_action()} instead + * Unregisters an action + * + * @param string $action Action name + * @return bool + * @since 1.8.1 */ -function register_action($action, $public = false, $filename = "", $admin_only = false) { - elgg_deprecated_notice("register_action() was deprecated by elgg_register_action()", 1.8); - - if ($admin_only) { - $access = 'admin'; - } elseif ($public) { - $access = 'public'; +function elgg_unregister_action($action) { + global $CONFIG; + + if (isset($CONFIG->actions[$action])) { + unset($CONFIG->actions[$action]); + return true; } else { - $access = 'logged_in'; + return false; } - - return elgg_register_action($action, $filename, $access); +} + +/** + * Is the token timestamp within acceptable range? + * + * @param int $ts timestamp from the CSRF token + * + * @return bool + */ +function _elgg_validate_token_timestamp($ts) { + $action_token_timeout = elgg_get_config('action_token_timeout'); + // default is 2 hours + $timeout = ($action_token_timeout !== null) ? $action_token_timeout : 2; + + $hour = 60 * 60; + $timeout = $timeout * $hour; + $now = time(); + + // Validate time to ensure its not crazy + return ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout)); } /** * Validate an action token. * - * Calls to actions will automatically validate tokens. - * If tokens are not present or invalid, the action will be - * denied and the user will be redirected to the front page. + * Calls to actions will automatically validate tokens. If tokens are not + * present or invalid, the action will be denied and the user will be redirected. * * Plugin authors should never have to manually validate action tokens. * - * @access private - * * @param bool $visibleerrors Emit {@link register_error()} errors on failure? * @param mixed $token The token to test against. Default: $_REQUEST['__elgg_token'] * @param mixed $ts The time stamp to test against. Default: $_REQUEST['__elgg_ts'] @@ -215,6 +221,7 @@ function register_action($action, $public = false, $filename = "", $admin_only = * @return bool * @see generate_action_token() * @link http://docs.elgg.org/Actions/Tokens + * @access private */ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL) { if (!$token) { @@ -229,17 +236,14 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL) if (($token) && ($ts) && ($session_id)) { // generate token, check with input and forward if invalid - $generated_token = generate_action_token($ts); + $required_token = generate_action_token($ts); // Validate token - if ($token == $generated_token) { - $hour = 60 * 60; - $now = time(); - - // Validate time to ensure its not crazy - if (($ts > $now - $hour) && ($ts < $now + $hour)) { + if ($token == $required_token) { + + if (_elgg_validate_token_timestamp($ts)) { // We have already got this far, so unless anything - // else says something to the contry we assume we're ok + // else says something to the contrary we assume we're ok $returnval = true; $returnval = elgg_trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', array( @@ -253,37 +257,78 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL) register_error(elgg_echo('actiongatekeeper:pluginprevents')); } } else if ($visibleerrors) { - register_error(elgg_echo('actiongatekeeper:timeerror')); + // this is necessary because of #5133 + if (elgg_is_xhr()) { + register_error(elgg_echo('js:security:token_refresh_failed', array(elgg_get_site_url()))); + } else { + register_error(elgg_echo('actiongatekeeper:timeerror')); + } } } else if ($visibleerrors) { - register_error(elgg_echo('actiongatekeeper:tokeninvalid')); + // this is necessary because of #5133 + if (elgg_is_xhr()) { + register_error(elgg_echo('js:security:token_refresh_failed', array(elgg_get_site_url()))); + } else { + register_error(elgg_echo('actiongatekeeper:tokeninvalid')); + } + } + } else { + if (! empty($_SERVER['CONTENT_LENGTH']) && empty($_POST)) { + // The size of $_POST or uploaded file has exceed the size limit + $error_msg = elgg_trigger_plugin_hook('action_gatekeeper:upload_exceeded_msg', 'all', array( + 'post_size' => $_SERVER['CONTENT_LENGTH'], + 'visible_errors' => $visibleerrors, + ), elgg_echo('actiongatekeeper:uploadexceeded')); + } else { + $error_msg = elgg_echo('actiongatekeeper:missingfields'); + } + if ($visibleerrors) { + register_error($error_msg); } - } else if ($visibleerrors) { - register_error(elgg_echo('actiongatekeeper:missingfields')); } return FALSE; } /** -* Validates the presence of action tokens. -* -* This function is called for all actions. If action tokens are missing, -* the user will be forwarded to the site front page and an error emitted. -* -* This function verifies form input for security features (like a generated token), and forwards -* the page if they are invalid. -* -* @access private -* @return mixed True if valid, or redirects to front page and exists. -*/ -function action_gatekeeper() { - if (validate_action_token()) { - return TRUE; + * Validates the presence of action tokens. + * + * This function is called for all actions. If action tokens are missing, + * the user will be forwarded to the site front page and an error emitted. + * + * This function verifies form input for security features (like a generated token), + * and forwards if they are invalid. + * + * @param string $action The action being performed + * + * @return mixed True if valid or redirects. + * @access private + */ +function action_gatekeeper($action) { + if ($action === 'login') { + if (validate_action_token(false)) { + return true; + } + + $token = get_input('__elgg_token'); + $ts = (int)get_input('__elgg_ts'); + if ($token && _elgg_validate_token_timestamp($ts)) { + // The tokens are present and the time looks valid: this is probably a mismatch due to the + // login form being on a different domain. + register_error(elgg_echo('actiongatekeeper:crosssitelogin')); + + + forward('login', 'csrf'); + } + + // let the validator send an appropriate msg + validate_action_token(); + + } elseif (validate_action_token()) { + return true; } - forward('', 'csrf'); - exit; + forward(REFERER, 'csrf'); } /** @@ -303,6 +348,7 @@ function action_gatekeeper() { * @example actions/manual_tokens.php * * @return string|false + * @access private */ function generate_action_token($timestamp) { $site_secret = get_site_secret(); @@ -318,16 +364,19 @@ function generate_action_token($timestamp) { } /** - * Initialise the site secret hash. + * Initialise the site secret (32 bytes: "z" to indicate format + 186-bit key in Base64 URL). * * Used during installation and saves as a datalist. * + * Note: Old secrets were hex encoded. + * * @return mixed The site secret hash or false * @access private * @todo Move to better file. */ function init_site_secret() { - $secret = md5(rand() . microtime()); + $secret = 'z' . ElggCrypto::getRandomString(31); + if (datalist_set('__site_secret__', $secret)) { return $secret; } @@ -354,39 +403,49 @@ function get_site_secret() { } /** - * Check if an action is registered and its file exists. - * - * @param string $action Action name + * Get the strength of the site secret * - * @return BOOL - * @since 1.8 + * @return string "strong", "moderate", or "weak" + * @access private */ -function elgg_action_exist($action) { - global $CONFIG; - - return (isset($CONFIG->actions[$action]) && file_exists($CONFIG->actions[$action]['file'])); +function _elgg_get_site_secret_strength() { + $secret = get_site_secret(); + if ($secret[0] !== 'z') { + $rand_max = getrandmax(); + if ($rand_max < pow(2, 16)) { + return 'weak'; + } + if ($rand_max < pow(2, 32)) { + return 'moderate'; + } + } + return 'strong'; } /** - * Initialize some ajaxy actions features + * Check if an action is registered and its script exists. + * + * @param string $action Action name + * + * @return bool + * @since 1.8.0 */ -function actions_init() { - elgg_register_action('security/refreshtoken', '', 'public'); - - elgg_view_register_simplecache('js/languages/en'); +function elgg_action_exists($action) { + global $CONFIG; - elgg_register_plugin_hook_handler('action', 'all', 'ajax_action_hook'); - elgg_register_plugin_hook_handler('forward', 'all', 'ajax_forward_hook'); + return (isset($CONFIG->actions[$action]) && file_exists($CONFIG->actions[$action]['file'])); } /** * Checks whether the request was requested via ajax * * @return bool whether page was requested via ajax + * @since 1.8.0 */ function elgg_is_xhr() { return isset($_SERVER['HTTP_X_REQUESTED_WITH']) - && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; + && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' || + get_input('X-Requested-With') === 'XMLHttpRequest'; } /** @@ -410,10 +469,21 @@ function elgg_is_xhr() { * @param string $type * @param string $reason * @param array $params - * + * @return void + * @access private */ function ajax_forward_hook($hook, $type, $reason, $params) { if (elgg_is_xhr()) { + // always pass the full structure to avoid boilerplate JS code. + $params = array( + 'output' => '', + 'status' => 0, + 'system_messages' => array( + 'error' => array(), + 'success' => array() + ) + ); + //grab any data echo'd in the action $output = ob_get_clean(); @@ -426,15 +496,27 @@ function ajax_forward_hook($hook, $type, $reason, $params) { } //Grab any system messages so we can inject them via ajax too - $params['system_messages'] = system_messages(NULL, ""); + $system_messages = system_messages(NULL, ""); + + if (isset($system_messages['success'])) { + $params['system_messages']['success'] = $system_messages['success']; + } - if (isset($params['system_messages']['errors'])) { + if (isset($system_messages['error'])) { + $params['system_messages']['error'] = $system_messages['error']; $params['status'] = -1; + } + + // Check the requester can accept JSON responses, if not fall back to + // returning JSON in a plain-text response. Some libraries request + // JSON in an invisible iframe which they then read from the iframe, + // however some browsers will not accept the JSON MIME type. + if (stripos($_SERVER['HTTP_ACCEPT'], 'application/json') === FALSE) { + header("Content-type: text/plain"); } else { - $params['status'] = 0; + header("Content-type: application/json"); } - header("Content-type: application/json"); echo json_encode($params); exit; } @@ -442,6 +524,8 @@ function ajax_forward_hook($hook, $type, $reason, $params) { /** * Buffer all output echo'd directly in the action for inclusion in the returned JSON. + * @return void + * @access private */ function ajax_action_hook() { if (elgg_is_xhr()) { @@ -449,4 +533,17 @@ function ajax_action_hook() { } } -elgg_register_event_handler('init', 'system', 'actions_init');
\ No newline at end of file +/** + * Initialize some ajaxy actions features + * @access private + */ +function actions_init() { + elgg_register_action('security/refreshtoken', '', 'public'); + + elgg_register_simplecache_view('js/languages/en'); + + elgg_register_plugin_hook_handler('action', 'all', 'ajax_action_hook'); + elgg_register_plugin_hook_handler('forward', 'all', 'ajax_forward_hook'); +} + +elgg_register_event_handler('init', 'system', 'actions_init'); diff --git a/engine/lib/admin.php b/engine/lib/admin.php index aab4b3aea..f36f29668 100644 --- a/engine/lib/admin.php +++ b/engine/lib/admin.php @@ -1,115 +1,228 @@ <?php /** * Elgg admin functions. - * Functions for adding and manipulating options on the admin panel. * - * @package Elgg - * @subpackage Core - */ - -/** - * Register an admin page with the admin panel. - * This function extends the view "admin/main" with the provided view. - * This view should provide a description and either a control or a link to. + * Admin menu items + * Elgg has a convenience function for adding menu items to the sidebar of the + * admin area. @see elgg_register_admin_menu_item() * - * Usage: - * - To add a control to the main admin panel then extend admin/main - * - To add a control to a new page create a page which renders a view admin/subpage - * (where subpage is your new page - - * nb. some pages already exist that you can extend), extend the main view to point to it, - * and add controls to your new view. + * Admin pages + * Plugins no not need to provide their own page handler to add a page to the + * admin area. A view placed at admin/<section>/<subsection> can be access + * at http://example.org/admin/<section>/<subsection>. The title of the page + * will be elgg_echo('admin:<section>:<subsection>'). For an example of how to + * add a page to the admin area, see the diagnostics plugin. * - * At the moment this is essentially a wrapper around elgg_extend_view(). + * Admin notices + * System messages (success and error messages) are used in both the main site + * and the admin area. There is a special presistent message for the admin area + * called an admin notice. It should be used when a plugin requires an + * administrator to take an action. An example is the categories plugin + * requesting that the administrator set site categories after the plugin has + * been activated. @see elgg_add_admin_notice() * - * @param string $new_admin_view The view associated with the control you're adding - * @param string $view The view to extend, by default this is 'admin/main'. - * @param int $priority Optional priority to govern the appearance in the list. * - * @return void + * @package Elgg.Core + * @subpackage Admin */ -function extend_elgg_admin_page($new_admin_view, $view = 'admin/main', $priority = 500) { - elgg_deprecated_notice('extend_elgg_admin_page() does nothing. Extend admin views manually.', 1.8); -} /** - * Calculate the plugin settings submenu. - * This is done in a separate function called from the admin - * page handler because of performance concerns. + * Get the admin users * - * @return void + * @param array $options Options array, @see elgg_get_entities() for parameters + * + * @return mixed Array of admin users or false on failure. If a count, returns int. + * @since 1.8.0 */ -function elgg_admin_add_plugin_settings_sidemenu() { +function elgg_get_admins(array $options = array()) { global $CONFIG; - if (!$installed_plugins = get_installed_plugins()) { - // nothing added because no items - return FALSE; + if (isset($options['joins'])) { + if (!is_array($options['joins'])) { + $options['joins'] = array($options['joins']); + } + $options['joins'][] = "join {$CONFIG->dbprefix}users_entity u on e.guid=u.guid"; + } else { + $options['joins'] = array("join {$CONFIG->dbprefix}users_entity u on e.guid=u.guid"); } - $parent_item = array( - 'text' => elgg_echo('admin:plugin_settings'), - 'id' => 'admin:plugin_settings' - ); + if (isset($options['wheres'])) { + if (!is_array($options['wheres'])) { + $options['wheres'] = array($options['wheres']); + } + $options['wheres'][] = "u.admin = 'yes'"; + } else { + $options['wheres'][] = "u.admin = 'yes'"; + } - elgg_add_submenu_item($parent_item, 'admin'); + return elgg_get_entities($options); +} - foreach ($installed_plugins as $plugin_id => $info) { - if (!$info['active']) { - continue; +/** + * Write a persistent message to the admin view. + * Useful to alert the admin to take a certain action. + * The id is a unique ID that can be cleared once the admin + * completes the action. + * + * eg: add_admin_notice('twitter_services_no_api', + * 'Before your users can use Twitter services on this site, you must set up + * the Twitter API key in the <a href="link">Twitter Services Settings</a>'); + * + * @param string $id A unique ID that your plugin can remember + * @param string $message Body of the message + * + * @return bool + * @since 1.8.0 + */ +function elgg_add_admin_notice($id, $message) { + if ($id && $message) { + if (elgg_admin_notice_exists($id)) { + return false; } - if (elgg_view_exists("settings/{$plugin_id}/edit")) { - $item = array( - 'text' => $info['manifest']['name'], - 'href' => "pg/admin/plugin_settings/$plugin_id", - 'parent_id' => 'admin:plugin_settings' - ); + // need to handle when no one is logged in + $old_ia = elgg_set_ignore_access(true); + + $admin_notice = new ElggObject(); + $admin_notice->subtype = 'admin_notice'; + // admins can see ACCESS_PRIVATE but no one else can. + $admin_notice->access_id = ACCESS_PRIVATE; + $admin_notice->admin_notice_id = $id; + $admin_notice->description = $message; + + $result = $admin_notice->save(); + + elgg_set_ignore_access($old_ia); + + return (bool)$result; + } + + return false; +} + +/** + * Remove an admin notice by ID. + * + * eg In actions/twitter_service/save_settings: + * if (is_valid_twitter_api_key()) { + * delete_admin_notice('twitter_services_no_api'); + * } + * + * @param string $id The unique ID assigned in add_admin_notice() + * + * @return bool + * @since 1.8.0 + */ +function elgg_delete_admin_notice($id) { + if (!$id) { + return FALSE; + } + $result = TRUE; + $notices = elgg_get_entities_from_metadata(array( + 'metadata_name' => 'admin_notice_id', + 'metadata_value' => $id + )); - elgg_add_submenu_item($item, 'admin'); + if ($notices) { + // in case a bad plugin adds many, let it remove them all at once. + foreach ($notices as $notice) { + $result = ($result && $notice->delete()); } + return $result; } + return FALSE; +} + +/** + * Get admin notices. An admin must be logged in since the notices are private. + * + * @param int $limit Limit + * + * @return array Array of admin notices + * @since 1.8.0 + */ +function elgg_get_admin_notices($limit = 10) { + return elgg_get_entities_from_metadata(array( + 'type' => 'object', + 'subtype' => 'admin_notice', + 'limit' => $limit + )); +} + +/** + * Check if an admin notice is currently active. + * + * @param string $id The unique ID used to register the notice. + * + * @return bool + * @since 1.8.0 + */ +function elgg_admin_notice_exists($id) { + $old_ia = elgg_set_ignore_access(true); + $notice = elgg_get_entities_from_metadata(array( + 'type' => 'object', + 'subtype' => 'admin_notice', + 'metadata_name_value_pair' => array('name' => 'admin_notice_id', 'value' => $id) + )); + elgg_set_ignore_access($old_ia); + + return ($notice) ? TRUE : FALSE; } /** * Add an admin area section or child section. - * This is a wrapper for elgg_add_admin_item(array(...), 'admin'). + * This is a wrapper for elgg_register_menu_item(). * * Used in conjuction with http://elgg.org/admin/section_id/child_section style - * page handler. + * page handler. See the documentation at the top of this file for more details + * on that. * - * @param string $section_id The Unique ID of section - * @param string $section_title Human readable section title. - * @param string $parent_id If a child section, the parent section id. + * The text of the menu item is obtained from elgg_echo(admin:$parent_id:$menu_id) + * + * This function handles registering the parent if it has not been registered. + * + * @param string $section The menu section to add to + * @param string $menu_id The unique ID of section + * @param string $parent_id If a child section, the parent section id + * @param int $priority The menu item priority * * @return bool + * @since 1.8.0 */ -function elgg_add_admin_submenu_item($section_id, $section_title, $parent_id = NULL) { - global $CONFIG; +function elgg_register_admin_menu_item($section, $menu_id, $parent_id = NULL, $priority = 100) { + + // make sure parent is registered + if ($parent_id && !elgg_is_menu_item_registered('page', $parent_id)) { + elgg_register_admin_menu_item($section, $parent_id); + } // in the admin section parents never have links if ($parent_id) { - $href = "pg/admin/$parent_id/$section_id"; - } elseif ($section_id == 'overview') { - $href = "pg/admin/$section_id"; - + $href = "admin/$parent_id/$menu_id"; } else { $href = NULL; } - $item = array( - 'text' => $section_title, - 'href' => $href, - 'id' => $section_id, - 'parent_id' => $parent_id - ); + $name = $menu_id; + if ($parent_id) { + $name = "$parent_id:$name"; + } - return elgg_add_submenu_item($item, 'admin'); + return elgg_register_menu_item('page', array( + 'name' => $name, + 'href' => $href, + 'text' => elgg_echo("admin:$name"), + 'context' => 'admin', + 'parent_name' => $parent_id, + 'priority' => $priority, + 'section' => $section + )); } /** - * Initialise the admin page. - * + * Initialize the admin backend. * @return void + * @access private */ function admin_init() { elgg_register_action('admin/user/ban', '', 'admin'); @@ -121,49 +234,209 @@ function admin_init() { elgg_register_action('admin/site/update_basic', '', 'admin'); elgg_register_action('admin/site/update_advanced', '', 'admin'); - - elgg_register_action('admin/menu_items', '', 'admin'); - - elgg_register_action('admin/plugins/simple_update_states', '', 'admin'); + elgg_register_action('admin/site/flush_cache', '', 'admin'); + elgg_register_action('admin/site/unlock_upgrade', '', 'admin'); + elgg_register_action('admin/site/regenerate_secret', '', 'admin'); + + elgg_register_action('admin/menu/save', '', 'admin'); + + elgg_register_action('admin/delete_admin_notice', '', 'admin'); + + elgg_register_action('profile/fields/reset', '', 'admin'); + elgg_register_action('profile/fields/add', '', 'admin'); + elgg_register_action('profile/fields/edit', '', 'admin'); + elgg_register_action('profile/fields/delete', '', 'admin'); + elgg_register_action('profile/fields/reorder', '', 'admin'); + + elgg_register_simplecache_view('css/admin'); + elgg_register_simplecache_view('js/admin'); + $url = elgg_get_simplecache_url('js', 'admin'); + elgg_register_js('elgg.admin', $url); + elgg_register_js('jquery.jeditable', 'vendors/jquery/jquery.jeditable.mini.js'); + + // administer + // dashboard + elgg_register_menu_item('page', array( + 'name' => 'dashboard', + 'href' => 'admin/dashboard', + 'text' => elgg_echo('admin:dashboard'), + 'context' => 'admin', + 'priority' => 10, + 'section' => 'administer' + )); + // statistics + elgg_register_admin_menu_item('administer', 'statistics', null, 20); + elgg_register_admin_menu_item('administer', 'overview', 'statistics'); + elgg_register_admin_menu_item('administer', 'server', 'statistics'); - // admin area overview and basic site settings - elgg_add_admin_submenu_item('overview', elgg_echo('admin:overview')); + // users + elgg_register_admin_menu_item('administer', 'users', null, 20); + elgg_register_admin_menu_item('administer', 'online', 'users', 10); + elgg_register_admin_menu_item('administer', 'admins', 'users', 20); + elgg_register_admin_menu_item('administer', 'newest', 'users', 30); + elgg_register_admin_menu_item('administer', 'add', 'users', 40); - elgg_add_admin_submenu_item('site', elgg_echo('admin:site')); - elgg_add_admin_submenu_item('basic', elgg_echo('admin:site:basic'), 'site'); - elgg_add_admin_submenu_item('advanced', elgg_echo('admin:site:advanced'), 'site'); + // configure + // plugins + elgg_register_menu_item('page', array( + 'name' => 'plugins', + 'href' => 'admin/plugins', + 'text' => elgg_echo('admin:plugins'), + 'context' => 'admin', + 'priority' => 75, + 'section' => 'configure' + )); - // appearance - elgg_add_admin_submenu_item('appearance', elgg_echo('admin:appearance')); + // settings + elgg_register_admin_menu_item('configure', 'appearance', null, 50); + elgg_register_admin_menu_item('configure', 'settings', null, 100); + elgg_register_admin_menu_item('configure', 'basic', 'settings', 10); + elgg_register_admin_menu_item('configure', 'advanced', 'settings', 20); + elgg_register_admin_menu_item('configure', 'advanced/site_secret', 'settings', 25); + elgg_register_admin_menu_item('configure', 'menu_items', 'appearance', 30); + elgg_register_admin_menu_item('configure', 'profile_fields', 'appearance', 40); + // default widgets is added via an event handler elgg_default_widgets_init() in widgets.php + // because it requires additional setup. + + // plugin settings are added in elgg_admin_add_plugin_settings_menu() via the admin page handler + // for performance reasons. + + // we want plugin settings menu items to be sorted alphabetical + if (elgg_in_context('admin')) { + elgg_register_plugin_hook_handler('prepare', 'menu:page', 'elgg_admin_sort_page_menu'); + } - //elgg_add_admin_submenu_item('basic', elgg_echo('admin:appearance'), 'appearance'); - elgg_add_admin_submenu_item('menu_items', elgg_echo('admin:menu_items'), 'appearance'); + if (elgg_is_admin_logged_in()) { + elgg_register_menu_item('topbar', array( + 'name' => 'administration', + 'href' => 'admin', + 'text' => elgg_view_icon('settings') . elgg_echo('admin'), + 'priority' => 100, + 'section' => 'alt', + )); + } + + // widgets + $widgets = array('online_users', 'new_users', 'content_stats', 'admin_welcome', 'control_panel'); + foreach ($widgets as $widget) { + elgg_register_widget_type( + $widget, + elgg_echo("admin:widget:$widget"), + elgg_echo("admin:widget:$widget:help"), + 'admin' + ); + } - // users - elgg_add_admin_submenu_item('users', elgg_echo('admin:users')); - elgg_add_admin_submenu_item('online', elgg_echo('admin:users:online'), 'users'); - elgg_add_admin_submenu_item('newest', elgg_echo('admin:users:newest'), 'users'); - elgg_add_admin_submenu_item('add', elgg_echo('admin:users:add'), 'users'); + // automatic adding of widgets for admin + elgg_register_event_handler('make_admin', 'user', 'elgg_add_admin_widgets'); - // plugins - elgg_add_admin_submenu_item('plugins', elgg_echo('admin:plugins')); - elgg_add_admin_submenu_item('simple', elgg_echo('admin:plugins:simple'), 'plugins'); - elgg_add_admin_submenu_item('advanced', elgg_echo('admin:plugins:advanced'), 'plugins'); + elgg_register_page_handler('admin', 'admin_page_handler'); + elgg_register_page_handler('admin_plugin_screenshot', 'admin_plugin_screenshot_page_handler'); + elgg_register_page_handler('admin_plugin_text_file', 'admin_markdown_page_handler'); +} - // handled in the admin sidemenu so we don't have to generate this on every page load. - //elgg_add_admin_submenu_item('plugin_settings', elgg_echo('admin:plugin_settings')); +/** + * Create the plugin settings page menu. + * + * This is done in a separate function called from the admin + * page handler because of performance concerns. + * + * @return void + * @access private + * @since 1.8.0 + */ +function elgg_admin_add_plugin_settings_menu() { - register_page_handler('admin', 'admin_settings_page_handler'); + $active_plugins = elgg_get_plugins('active'); + if (!$active_plugins) { + // nothing added because no items + return; + } + + foreach ($active_plugins as $plugin) { + $plugin_id = $plugin->getID(); + $settings_view_old = 'settings/' . $plugin_id . '/edit'; + $settings_view_new = 'plugins/' . $plugin_id . '/settings'; + if (elgg_view_exists($settings_view_new) || elgg_view_exists($settings_view_old)) { + elgg_register_menu_item('page', array( + 'name' => $plugin_id, + 'href' => "admin/plugin_settings/$plugin_id", + 'text' => $plugin->getManifest()->getName(), + 'parent_name' => 'settings', + 'context' => 'admin', + 'section' => 'configure', + )); + } + } +} + +/** + * Sort the plugin settings menu items + * + * @param string $hook + * @param string $type + * @param array $return + * @param array $params + * + * @return void + * @since 1.8.0 + * @access private + */ +function elgg_admin_sort_page_menu($hook, $type, $return, $params) { + $configure_items = $return['configure']; + /* @var ElggMenuItem[] $configure_items */ + foreach ($configure_items as $menu_item) { + if ($menu_item->getName() == 'settings') { + $settings = $menu_item; + } + } + + // keep the basic and advanced settings at the top + /* @var ElggMenuItem $settings */ + $children = $settings->getChildren(); + $site_settings = array_splice($children, 0, 2); + usort($children, array('ElggMenuBuilder', 'compareByText')); + array_splice($children, 0, 0, $site_settings); + $settings->setChildren($children); } /** * Handles any set up required for administration pages + * + * @return void + * @access private */ function admin_pagesetup() { if (elgg_in_context('admin')) { - $url = elgg_view_get_simplecache_url('css', 'admin'); - elgg_register_css($url, 'admin'); + $url = elgg_get_simplecache_url('css', 'admin'); + elgg_register_css('elgg.admin', $url); + elgg_load_css('elgg.admin'); elgg_unregister_css('elgg'); + + // setup footer menu + elgg_register_menu_item('admin_footer', array( + 'name' => 'faq', + 'text' => elgg_echo('admin:footer:faq'), + 'href' => 'http://docs.elgg.org/wiki/Category:Administration_FAQ', + )); + + elgg_register_menu_item('admin_footer', array( + 'name' => 'manual', + 'text' => elgg_echo('admin:footer:manual'), + 'href' => 'http://docs.elgg.org/wiki/Administration_Manual', + )); + + elgg_register_menu_item('admin_footer', array( + 'name' => 'community_forums', + 'text' => elgg_echo('admin:footer:community_forums'), + 'href' => 'http://community.elgg.org/groups/all/', + )); + + elgg_register_menu_item('admin_footer', array( + 'name' => 'blog', + 'text' => elgg_echo('admin:footer:blog'), + 'href' => 'http://blog.elgg.org/', + )); } } @@ -172,18 +445,22 @@ function admin_pagesetup() { * * @param array $page Array of pages * - * @return void + * @return bool + * @access private */ -function admin_settings_page_handler($page) { - global $CONFIG; +function admin_page_handler($page) { admin_gatekeeper(); - elgg_admin_add_plugin_settings_sidemenu(); + elgg_admin_add_plugin_settings_menu(); elgg_set_context('admin'); - // default to overview + elgg_unregister_css('elgg'); + elgg_load_js('elgg.admin'); + elgg_load_js('jquery.jeditable'); + + // default to dashboard if (!isset($page[0]) || empty($page[0])) { - $page = array('overview'); + $page = array('dashboard'); } // was going to fix this in the page_handler() function but @@ -195,130 +472,191 @@ function admin_settings_page_handler($page) { $vars = array('page' => $page); // special page for plugin settings since we create the form for them - if ($page[0] == 'plugin_settings' && isset($page[1]) - && elgg_view_exists("settings/{$page[1]}/edit")) { + if ($page[0] == 'plugin_settings') { + if (isset($page[1]) && (elgg_view_exists("settings/{$page[1]}/edit") || + elgg_view_exists("plugins/{$page[1]}/settings"))) { - $view = '/admin/components/plugin_settings'; - $vars['plugin'] = $page[1]; - $vars['entity'] = find_plugin_settings($page[1]); - $title = elgg_echo("admin:plugin_settings:{$page[1]}"); + $view = 'admin/plugin_settings'; + $plugin = elgg_get_plugin_from_id($page[1]); + $vars['plugin'] = $plugin; + + $title = elgg_echo("admin:{$page[0]}"); + } else { + forward('', '404'); + } } else { $view = 'admin/' . implode('/', $page); - $title = elgg_echo('admin:' . implode(':', $page)); + $title = elgg_echo("admin:{$page[0]}"); + if (count($page) > 1) { + $title .= ' : ' . elgg_echo('admin:' . implode(':', $page)); + } } - // allow a place to store helper views outside of the web-accessible views + // gets content and prevents direct access to 'components' views if ($page[0] == 'components' || !($content = elgg_view($view, $vars))) { $title = elgg_echo('admin:unknown_section'); $content = elgg_echo('admin:unknown_section'); } - $notices_html = ''; - if ($notices = elgg_get_admin_notices()) { - foreach ($notices as $notice) { - $notices_html .= elgg_view_entity($notice); - } - - $content = "<div class=\"admin_notices\">$notices_html</div>$content"; - } - - $body = elgg_view_layout('administration', array('content' => $content)); + $body = elgg_view_layout('admin', array('content' => $content, 'title' => $title)); echo elgg_view_page($title, $body, 'admin'); + return true; } /** - * Write a persistent message to the admin view. - * Useful to alert the admin to take a certain action. - * The id is a unique ID that can be cleared once the admin - * completes the action. - * - * eg: add_admin_notice('twitter_services_no_api', - * 'Before your users can use Twitter services on this site, you must set up - * the Twitter API key in the <a href="link">Twitter Services Settings</a>'); - * - * @param string $id A unique ID that your plugin can remember - * @param string $message Body of the message + * Serves up screenshots for plugins from + * admin_plugin_screenshot/<plugin_id>/<size>/<ss_name>.<ext> * - * @return boo + * @param array $pages The pages array + * @return bool + * @access private */ -function elgg_add_admin_notice($id, $message) { - if ($id && $message) { - $admin_notice = new ElggObject(); - $admin_notice->subtype = 'admin_notice'; - // admins can see ACCESS_PRIVATE but no one else can. - $admin_notice->access_id = ACCESS_PRIVATE; - $admin_notice->admin_notice_id = $id; - $admin_notice->description = $message; +function admin_plugin_screenshot_page_handler($pages) { + // only admins can use this for security + admin_gatekeeper(); + + $plugin_id = elgg_extract(0, $pages); + // only thumbnail or full. + $size = elgg_extract(1, $pages, 'thumbnail'); - return $admin_notice->save(); + // the rest of the string is the filename + $filename_parts = array_slice($pages, 2); + $filename = implode('/', $filename_parts); + $filename = sanitise_filepath($filename, false); + + $plugin = new ElggPlugin($plugin_id); + if (!$plugin) { + $file = elgg_get_root_path() . '_graphics/icons/default/medium.png'; + } else { + $file = $plugin->getPath() . $filename; + if (!file_exists($file)) { + $file = elgg_get_root_path() . '_graphics/icons/default/medium.png'; + } } - return FALSE; -} + header("Content-type: image/jpeg"); + // resize to 100x100 for thumbnails + switch ($size) { + case 'thumbnail': + echo get_resized_image_from_existing_file($file, 100, 100, true); + break; + + case 'full': + default: + echo file_get_contents($file); + break; + } + return true; +} /** - * Remove an admin notice by ID. + * Formats and serves out markdown files from plugins. * - * eg In actions/twitter_service/save_settings: - * if (is_valid_twitter_api_key()) { - * delete_admin_notice('twitter_services_no_api'); - * } + * URLs in format like admin_plugin_text_file/<plugin_id>/filename.ext * - * @param string $id The unique ID assigned in add_admin_notice() + * The only valid files are: + * * README.txt + * * CHANGES.txt + * * INSTALL.txt + * * COPYRIGHT.txt + * * LICENSE.txt * + * @param array $pages * @return bool + * @access private */ -function elgg_delete_admin_notice($id) { - if (!$id) { - return FALSE; +function admin_markdown_page_handler($pages) { + admin_gatekeeper(); + + elgg_set_context('admin'); + + elgg_unregister_css('elgg'); + elgg_load_js('elgg.admin'); + elgg_load_js('jquery.jeditable'); + elgg_load_library('elgg:markdown'); + + $plugin_id = elgg_extract(0, $pages); + $plugin = elgg_get_plugin_from_id($plugin_id); + $filename = elgg_extract(1, $pages); + + $error = false; + if (!$plugin) { + $error = elgg_echo('admin:plugins:markdown:unknown_plugin'); + $body = elgg_view_layout('admin', array('content' => $error, 'title' => $error)); + echo elgg_view_page($error, $body, 'admin'); + return true; } - $result = TRUE; - $notices = elgg_get_entities_from_metadata(array( - 'metadata_name' => 'admin_notice_id', - 'metadata_value' => $id - )); - if ($notices) { - // in case a bad plugin adds many, let it remove them all at once. - foreach ($notices as $notice) { - $result = ($result && $notice->delete()); - } - return $result; + $text_files = $plugin->getAvailableTextFiles(); + + if (!array_key_exists($filename, $text_files)) { + $error = elgg_echo('admin:plugins:markdown:unknown_file'); } - return FALSE; -} -/** - * List all admin messages. - * - * @param int $limit Limit - * - * @return array List of admin notices - */ -function elgg_get_admin_notices($limit = 10) { - return elgg_get_entities_from_metadata(array( - 'type' => 'object', - 'subtype' => 'admin_notice', - 'limit' => $limit + $file = $text_files[$filename]; + $file_contents = file_get_contents($file); + + if (!$file_contents) { + $error = elgg_echo('admin:plugins:markdown:unknown_file'); + } + + if ($error) { + $title = $error; + $body = elgg_view_layout('admin', array('content' => $error, 'title' => $title)); + echo elgg_view_page($title, $body, 'admin'); + return true; + } + + $title = $plugin->getManifest()->getName() . ": $filename"; + $text = Markdown($file_contents); + + $body = elgg_view_layout('admin', array( + // setting classes here because there's no way to pass classes + // to the layout + 'content' => '<div class="elgg-markdown">' . $text . '</div>', + 'title' => $title )); + + echo elgg_view_page($title, $body, 'admin'); + return true; } /** - * Check if an admin notice is currently active. + * Adds default admin widgets to the admin dashboard. * - * @param string $id The unique ID used to register the notice. + * @param string $event + * @param string $type + * @param ElggUser $user * - * @return bool + * @return null|true + * @access private */ -function elgg_admin_notice_exists($id) { - $notice = elgg_get_entities_from_metadata(array( - 'type' => 'object', - 'subtype' => 'admin_notice', - 'metadata_name_value_pair' => array('name' => 'admin_notice_id', 'value' => $id) - )); +function elgg_add_admin_widgets($event, $type, $user) { + elgg_set_ignore_access(true); - return ($notice) ? TRUE : FALSE; + // check if the user already has widgets + if (elgg_get_widgets($user->getGUID(), 'admin')) { + return true; + } + + // In the form column => array of handlers in order, top to bottom + $adminWidgets = array( + 1 => array('control_panel', 'admin_welcome'), + 2 => array('online_users', 'new_users', 'content_stats'), + ); + + foreach ($adminWidgets as $column => $handlers) { + foreach ($handlers as $position => $handler) { + $guid = elgg_create_widget($user->getGUID(), $handler, 'admin'); + if ($guid) { + $widget = get_entity($guid); + /* @var ElggWidget $widget */ + $widget->move($column, $position); + } + } + } + elgg_set_ignore_access(false); } elgg_register_event_handler('init', 'system', 'admin_init'); diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php index 851d558b5..5e9b530de 100644 --- a/engine/lib/annotations.php +++ b/engine/lib/annotations.php @@ -13,9 +13,11 @@ * @param stdClass $row Db row result object * * @return ElggAnnotation + * @access private */ function row_to_elggannotation($row) { if (!($row instanceof stdClass)) { + // @todo should throw in this case? return $row; } @@ -23,25 +25,30 @@ function row_to_elggannotation($row) { } /** - * Get a specific annotation. + * Get a specific annotation by its id. + * If you want multiple annotation objects, use + * {@link elgg_get_annotations()}. * - * @param int $annotation_id Annotation ID + * @param int $id The id of the annotation object being retrieved. * - * @return ElggAnnotation + * @return ElggAnnotation|false */ -function get_annotation($annotation_id) { - global $CONFIG; - - $annotation_id = (int) $annotation_id; - $access = get_access_sql_suffix("a"); - - $query = "SELECT a.*, n.string as name, v.string as value" - . " from {$CONFIG->dbprefix}annotations a" - . " JOIN {$CONFIG->dbprefix}metastrings n on a.name_id = n.id" - . " JOIN {$CONFIG->dbprefix}metastrings v on a.value_id = v.id" - . " where a.id=$annotation_id and $access"; +function elgg_get_annotation_from_id($id) { + return elgg_get_metastring_based_object_from_id($id, 'annotations'); +} - return row_to_elggannotation(get_data_row($query)); +/** + * Deletes an annotation using its ID. + * + * @param int $id The annotation ID to delete. + * @return bool + */ +function elgg_delete_annotation_by_id($id) { + $annotation = elgg_get_annotation_from_id($id); + if (!$annotation) { + return false; + } + return $annotation->delete(); } /** @@ -50,14 +57,14 @@ function get_annotation($annotation_id) { * @param int $entity_guid Entity Guid * @param string $name Name of annotation * @param string $value Value of annotation - * @param string $value_type Type of value - * @param int $owner_guid Owner of annotation + * @param string $value_type Type of value (default is auto detection) + * @param int $owner_guid Owner of annotation (default is logged in user) * @param int $access_id Access level of annotation * * @return int|bool id on success or false on failure */ -function create_annotation($entity_guid, $name, $value, $value_type, -$owner_guid, $access_id = ACCESS_PRIVATE) { +function create_annotation($entity_guid, $name, $value, $value_type = '', +$owner_guid = 0, $access_id = ACCESS_PRIVATE) { global $CONFIG; $result = false; @@ -69,7 +76,7 @@ $owner_guid, $access_id = ACCESS_PRIVATE) { $owner_guid = (int)$owner_guid; if ($owner_guid == 0) { - $owner_guid = get_loggedin_userid(); + $owner_guid = elgg_get_logged_in_user_guid(); } $access_id = (int)$access_id; @@ -89,20 +96,18 @@ $owner_guid, $access_id = ACCESS_PRIVATE) { $entity = get_entity($entity_guid); if (elgg_trigger_event('annotate', $entity->type, $entity)) { - system_log($entity, 'annotate'); - // If ok then add it $result = insert_data("INSERT into {$CONFIG->dbprefix}annotations (entity_guid, name_id, value_id, value_type, owner_guid, time_created, access_id) VALUES ($entity_guid,'$name',$value,'$value_type', $owner_guid, $time, $access_id)"); if ($result !== false) { - $obj = get_annotation($result); + $obj = elgg_get_annotation_from_id($result); if (elgg_trigger_event('create', 'annotation', $obj)) { return $result; } else { // plugin returned false to reject annotation - delete_annotation($result); + elgg_delete_annotation_by_id($result); return FALSE; } } @@ -133,7 +138,7 @@ function update_annotation($annotation_id, $name, $value, $value_type, $owner_gu $owner_guid = (int)$owner_guid; if ($owner_guid == 0) { - $owner_guid = get_loggedin_userid(); + $owner_guid = elgg_get_logged_in_user_guid(); } $access_id = (int)$access_id; @@ -153,186 +158,158 @@ function update_annotation($annotation_id, $name, $value, $value_type, $owner_gu // If ok then add it $result = update_data("UPDATE {$CONFIG->dbprefix}annotations - set value_id='$value', value_type='$value_type', access_id=$access_id, owner_guid=$owner_guid - where id=$annotation_id and name_id='$name' and $access"); + set name_id='$name', value_id='$value', value_type='$value_type', access_id=$access_id, owner_guid=$owner_guid + where id=$annotation_id and $access"); if ($result !== false) { - $obj = get_annotation($annotation_id); - if (elgg_trigger_event('update', 'annotation', $obj)) { - return true; - } else { - // @todo add plugin hook that sends old and new annotation information before db access - delete_annotation($annotation_id); - } + // @todo add plugin hook that sends old and new annotation information before db access + $obj = elgg_get_annotation_from_id($annotation_id); + elgg_trigger_event('update', 'annotation', $obj); } return $result; } /** - * Get a list of annotations for a given object/user/annotation type. + * Returns annotations. Accepts all elgg_get_entities() options for entity + * restraints. * - * @param int|array $entity_guid GUID to return annotations of (falsey for any) - * @param string $entity_type Type of entity - * @param string $entity_subtype Subtype of entity - * @param string $name Name of annotation - * @param mixed $value Value of annotation - * @param int|array $owner_guid Owner(s) of annotation - * @param int $limit Limit - * @param int $offset Offset - * @param string $order_by Order annotations by SQL - * @param int $timelower Lower time limit - * @param int $timeupper Upper time limit - * @param int $entity_owner_guid Owner guid for the entity + * @see elgg_get_entities * - * @return array + * @param array $options Array in format: + * + * annotation_names => NULL|ARR Annotation names + * annotation_values => NULL|ARR Annotation values + * annotation_ids => NULL|ARR annotation ids + * annotation_case_sensitive => BOOL Overall Case sensitive + * annotation_owner_guids => NULL|ARR guids for annotation owners + * annotation_created_time_lower => INT Lower limit for created time. + * annotation_created_time_upper => INT Upper limit for created time. + * annotation_calculation => STR Perform the MySQL function on the annotation values returned. + * Do not confuse this "annotation_calculation" option with the + * "calculation" option to elgg_get_entities_from_annotation_calculation(). + * The "annotation_calculation" option causes this function to + * return the result of performing a mathematical calculation on + * all annotations that match the query instead of ElggAnnotation + * objects. + * See the docs for elgg_get_entities_from_annotation_calculation() + * for the proper use of the "calculation" option. + * + * + * @return ElggAnnotation[]|mixed + * @since 1.8.0 */ -function get_annotations($entity_guid = 0, $entity_type = "", $entity_subtype = "", $name = "", -$value = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "asc", $timelower = 0, -$timeupper = 0, $entity_owner_guid = 0) { - global $CONFIG; - - $timelower = (int) $timelower; - $timeupper = (int) $timeupper; +function elgg_get_annotations(array $options = array()) { - if (is_array($entity_guid)) { - if (sizeof($entity_guid) > 0) { - foreach ($entity_guid as $key => $val) { - $entity_guid[$key] = (int) $val; - } - } else { - $entity_guid = 0; - } + // @todo remove support for count shortcut - see #4393 + if (isset($options['__egefac']) && $options['__egefac']) { + unset($options['__egefac']); } else { - $entity_guid = (int)$entity_guid; - } - - $entity_type = sanitise_string($entity_type); - - if ($entity_subtype) { - if (!$entity_subtype = get_subtype_id($entity_type, $entity_subtype)) { - // requesting a non-existing subtype: return false - return FALSE; - } - } - - if ($name) { - $name = get_metastring_id($name); - - if ($name === false) { - $name = 0; - } - } - if ($value != "") { - $value = get_metastring_id($value); - } - - if (is_array($owner_guid)) { - if (sizeof($owner_guid) > 0) { - foreach ($owner_guid as $key => $val) { - $owner_guid[$key] = (int) $val; - } - } else { - $owner_guid = 0; - } - } else { - $owner_guid = (int)$owner_guid; - } - - if (is_array($entity_owner_guid)) { - if (sizeof($entity_owner_guid) > 0) { - foreach ($entity_owner_guid as $key => $val) { - $entity_owner_guid[$key] = (int) $val; - } - } else { - $entity_owner_guid = 0; - } - } else { - $entity_owner_guid = (int)$entity_owner_guid; - } - - $limit = (int)$limit; - $offset = (int)$offset; - if ($order_by == 'asc') { - $order_by = "a.time_created asc"; - } - - if ($order_by == 'desc') { - $order_by = "a.time_created desc"; - } - - $where = array(); - - if ($entity_guid != 0 && !is_array($entity_guid)) { - $where[] = "a.entity_guid=$entity_guid"; - } else if (is_array($entity_guid)) { - $where[] = "a.entity_guid in (" . implode(",", $entity_guid) . ")"; - } - - if ($entity_type != "") { - $where[] = "e.type='$entity_type'"; - } - - if ($entity_subtype != "") { - $where[] = "e.subtype='$entity_subtype'"; - } + // support shortcut of 'count' => true for 'annotation_calculation' => 'count' + if (isset($options['count']) && $options['count']) { + $options['annotation_calculation'] = 'count'; + unset($options['count']); + } + } + + $options['metastring_type'] = 'annotations'; + return elgg_get_metastring_based_objects($options); +} - if ($owner_guid != 0 && !is_array($owner_guid)) { - $where[] = "a.owner_guid=$owner_guid"; - } else { - if (is_array($owner_guid)) { - $where[] = "a.owner_guid in (" . implode(",", $owner_guid) . ")"; - } +/** + * Deletes annotations based on $options. + * + * @warning Unlike elgg_get_annotations() this will not accept an empty options array! + * This requires at least one constraint: annotation_owner_guid(s), + * annotation_name(s), annotation_value(s), or guid(s) must be set. + * + * @param array $options An options array. {@See elgg_get_annotations()} + * @return bool|null true on success, false on failure, null if no annotations to delete. + * @since 1.8.0 + */ +function elgg_delete_annotations(array $options) { + if (!elgg_is_valid_options_for_batch_operation($options, 'annotations')) { + return false; } - if ($entity_owner_guid != 0 && !is_array($entity_owner_guid)) { - $where[] = "e.owner_guid=$entity_owner_guid"; - } else { - if (is_array($entity_owner_guid)) { - $where[] = "e.owner_guid in (" . implode(",", $entity_owner_guid) . ")"; - } - } + $options['metastring_type'] = 'annotations'; + return elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false); +} - if ($name !== "") { - $where[] = "a.name_id='$name'"; +/** + * Disables annotations based on $options. + * + * @warning Unlike elgg_get_annotations() this will not accept an empty options array! + * + * @param array $options An options array. {@See elgg_get_annotations()} + * @return bool|null true on success, false on failure, null if no annotations disabled. + * @since 1.8.0 + */ +function elgg_disable_annotations(array $options) { + if (!elgg_is_valid_options_for_batch_operation($options, 'annotations')) { + return false; } + + // if we can see hidden (disabled) we need to use the offset + // otherwise we risk an infinite loop if there are more than 50 + $inc_offset = access_get_show_hidden_status(); - if ($value != "") { - $where[] = "a.value_id='$value'"; - } + $options['metastring_type'] = 'annotations'; + return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset); +} - if ($timelower) { - $where[] = "a.time_created >= {$timelower}"; +/** + * Enables annotations based on $options. + * + * @warning Unlike elgg_get_annotations() this will not accept an empty options array! + * + * @warning In order to enable annotations, you must first use + * {@link access_show_hidden_entities()}. + * + * @param array $options An options array. {@See elgg_get_annotations()} + * @return bool|null true on success, false on failure, null if no metadata enabled. + * @since 1.8.0 + */ +function elgg_enable_annotations(array $options) { + if (!$options || !is_array($options)) { + return false; } - if ($timeupper) { - $where[] = "a.time_created <= {$timeupper}"; - } + $options['metastring_type'] = 'annotations'; + return elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback'); +} - $query = "SELECT a.*, n.string as name, v.string as value - FROM {$CONFIG->dbprefix}annotations a - JOIN {$CONFIG->dbprefix}entities e on a.entity_guid = e.guid - JOIN {$CONFIG->dbprefix}metastrings v on a.value_id=v.id - JOIN {$CONFIG->dbprefix}metastrings n on a.name_id = n.id where "; +/** + * Returns a rendered list of annotations with pagination. + * + * @param array $options Annotation getter and display options. + * {@see elgg_get_annotations()} and {@see elgg_list_entities()}. + * + * @return string The list of entities + * @since 1.8.0 + */ +function elgg_list_annotations($options) { + $defaults = array( + 'limit' => 25, + 'offset' => (int) max(get_input('annoff', 0), 0), + ); - foreach ($where as $w) { - $query .= " $w and "; - } - $query .= get_access_sql_suffix("a"); // Add access controls - $query .= " order by $order_by limit $offset,$limit"; // Add order and limit + $options = array_merge($defaults, $options); - return get_data($query, "row_to_elggannotation"); + return elgg_list_entities($options, 'elgg_get_annotations', 'elgg_view_annotation_list'); } /** - * Returns entities based upon annotations. Accepts the same values as - * elgg_get_entities_from_metadata() but uses the annotations table. + * Entities interfaces + */ + +/** + * Returns entities based upon annotations. Also accepts all options available + * to elgg_get_entities() and elgg_get_entities_from_metadata(). * - * NB: Entity creation time is selected as max_time. To sort based upon + * Entity creation time is selected as maxtime. To sort based upon * this, pass 'order_by' => 'maxtime asc' || 'maxtime desc' * - * time_created in this case will be the time the annotation was created. - * * @see elgg_get_entities * @see elgg_get_entities_from_metadata * @@ -343,9 +320,9 @@ $timeupper = 0, $entity_owner_guid = 0) { * annotation_values => NULL|ARR annotations values * * annotation_name_value_pairs => NULL|ARR (name = 'name', value => 'value', - * 'operand' => '=', 'case_sensitive' => TRUE) entries. + * 'operator' => '=', 'case_sensitive' => TRUE) entries. * Currently if multiple values are sent via an array (value => array('value1', 'value2') - * the pair's operand will be forced to "IN". + * the pair's operator will be forced to "IN". * * annotation_name_value_pairs_operator => NULL|STR The operator to use for combining * (name = value) OPERATOR (name = value); default AND @@ -359,7 +336,7 @@ $timeupper = 0, $entity_owner_guid = 0) { * * annotation_owner_guids => NULL|ARR guids for annotaiton owners * - * @return array + * @return mixed If count, int. If not count, array. false on errors. * @since 1.7.0 */ function elgg_get_entities_from_annotations(array $options = array()) { @@ -372,6 +349,9 @@ function elgg_get_entities_from_annotations(array $options = array()) { 'annotation_case_sensitive' => TRUE, 'order_by_annotation' => array(), + 'annotation_created_time_lower' => ELGG_ENTITIES_ANY_VALUE, + 'annotation_created_time_upper' => ELGG_ENTITIES_ANY_VALUE, + 'annotation_owner_guids' => ELGG_ENTITIES_ANY_VALUE, 'order_by' => 'maxtime desc', @@ -384,9 +364,10 @@ function elgg_get_entities_from_annotations(array $options = array()) { 'annotation_name_value_pair', 'annotation_owner_guid'); $options = elgg_normalise_plural_options_array($options, $singulars); + $options = elgg_entities_get_metastrings_options('annotation', $options); - if (!$options = elgg_entities_get_metastrings_options('annotation', $options)) { - return FALSE; + if (!$options) { + return false; } // special sorting for annotations @@ -394,727 +375,116 @@ function elgg_get_entities_from_annotations(array $options = array()) { $options['selects'][] = "max(n_table.time_created) as maxtime"; $options['group_by'] = 'n_table.entity_guid'; - return elgg_get_entities($options); -} + $time_wheres = elgg_get_entity_time_where_sql('a', $options['annotation_created_time_upper'], + $options['annotation_created_time_lower']); -/** - * Get entities from annotations - * - * No longer used. - * - * @deprecated 1.7 Use elgg_get_entities_from_annotations() - * - * @param mixed $entity_type Type of entity - * @param mixed $entity_subtype Subtype of entity - * @param string $name Name of annotation - * @param string $value Value of annotation - * @param int $owner_guid Guid of owner of annotation - * @param int $group_guid Guid of group - * @param int $limit Limit - * @param int $offset Offset - * @param string $order_by SQL order by string - * @param bool $count Count or return entities - * @param int $timelower Lower time limit - * @param int $timeupper Upper time limit - * - * @return unknown_type - */ -function get_entities_from_annotations($entity_type = "", $entity_subtype = "", $name = "", -$value = "", $owner_guid = 0, $group_guid = 0, $limit = 10, $offset = 0, $order_by = "asc", -$count = false, $timelower = 0, $timeupper = 0) { - $msg = 'get_entities_from_annotations() is deprecated by elgg_get_entities_from_annotations().'; - elgg_deprecated_notice($msg, 1.7); - - $options = array(); - - $options['annotation_names'] = $name; - - if ($value) { - $options['annotation_values'] = $value; - } - - if ($entity_type) { - $options['types'] = $entity_type; - } - - if ($entity_subtype) { - $options['subtypes'] = $entity_subtype; - } - - if ($owner_guid) { - if (is_array($owner_guid)) { - $options['annotation_owner_guids'] = $owner_guid; - } else { - $options['annotation_owner_guid'] = $owner_guid; - } - } - - if ($group_guid) { - $options['container_guid'] = $group_guid; - } - - if ($limit) { - $options['limit'] = $limit; - } - - if ($offset) { - $options['offset'] = $offset; - } - - if ($order_by) { - $options['order_by'] = "maxtime $order_by"; - } - - if ($count) { - $options['count'] = $count; - } - - return elgg_get_entities_from_annotations($options); -} - -/** - * Lists entities - * - * @see elgg_view_entity_list - * - * @param string $entity_type Type of entity. - * @param string $entity_subtype Subtype of entity. - * @param string $name Name of annotation. - * @param string $value Value of annotation. - * @param int $limit Maximum number of results to return. - * @param int $owner_guid Owner. - * @param int $group_guid Group container. Currently only supported if entity_type is object - * @param boolean $asc Whether to list in ascending or descending order (default: desc) - * @param boolean $fullview Whether to display the entities in full - * @param boolean $listtypetoggle Can 'gallery' view can be displayed (default: no) - * - * @deprecated 1.7 Use elgg_list_entities_from_annotations() - * @return string Formatted entity list - */ -function list_entities_from_annotations($entity_type = "", $entity_subtype = "", $name = "", -$value = "", $limit = 10, $owner_guid = 0, $group_guid = 0, $asc = false, $fullview = true, -$listtypetoggle = false) { - - $msg = 'list_entities_from_annotations is deprecated by elgg_list_entities_from_annotations'; - elgg_deprecated_notice($msg, 1.8); - - $options = array(); - - if ($entity_type) { - $options['types'] = $entity_type; - } - - if ($entity_subtype) { - $options['subtypes'] = $entity_subtype; - } - - if ($name) { - $options['annotation_names'] = $name; - } - - if ($value) { - $options['annotation_values'] = $value; - } - - if ($limit) { - $options['limit'] = $limit; - } - - if ($owner_guid) { - $options['annotation_owner_guid'] = $owner_guid; - } - - if ($group_guid) { - $options['container_guid'] = $group_guid; - } - - if ($asc) { - $options['order_by'] = 'maxtime desc'; + if ($time_wheres) { + $options['wheres'] = array_merge($options['wheres'], $time_wheres); } - if ($offset = sanitise_int(get_input('offset', null))) { - $options['offset'] = $offset; - } - - $options['full_view'] = $fullview; - $options['list_type_toggle'] = $listtypetoggle; - $options['pagination'] = $pagination; - - return elgg_list_entities_from_annotations($options); + return elgg_get_entities_from_metadata($options); } /** * Returns a viewable list of entities from annotations. * - * @param array $options + * @param array $options Options array * * @see elgg_get_entities_from_annotations() * @see elgg_list_entities() * - * @return str + * @return string */ function elgg_list_entities_from_annotations($options = array()) { return elgg_list_entities($options, 'elgg_get_entities_from_annotations'); } /** - * Returns a human-readable list of annotations on a particular entity. - * - * @param int $entity_guid The entity GUID - * @param string $name The name of the kind of annotation - * @param int $limit The number of annotations to display at once - * @param true|false $asc Display annotations in ascending order. (Default: true) - * - * @return string HTML (etc) version of the annotation list - */ -function list_annotations($entity_guid, $name = "", $limit = 25, $asc = true) { - if ($asc) { - $asc = "asc"; - } else { - $asc = "desc"; - } - $count = count_annotations($entity_guid, "", "", $name); - $offset = (int) get_input("annoff", 0); - $annotations = get_annotations($entity_guid, "", "", $name, "", "", $limit, $offset, $asc); - - return elgg_view_annotation_list($annotations, $count, $offset, $limit); -} - -/** - * Return the sum of a given integer annotation. - * - * @param int $entity_guid Guid of Entity - * @param string $entity_type Type of Entity - * @param string $entity_subtype Subtype of Entity - * @param string $name Name of annotation - * @param string $value Value of annotation - * @param string $value_type Type of value - * @param int $owner_guid GUID of owner of annotation - * - * @return int - */ -function get_annotations_sum($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", -$value = "", $value_type = "", $owner_guid = 0) { - - return get_annotations_calculate_x("sum", $entity_guid, $entity_type, $entity_subtype, $name, - $value, $value_type, $owner_guid); -} - -/** - * Return the max of a given integer annotation. - * - * @param int $entity_guid Guid of Entity - * @param string $entity_type Type of Entity - * @param string $entity_subtype Subtype of Entity - * @param string $name Name of annotation - * @param string $value Value of annotation - * @param string $value_type Type of value - * @param int $owner_guid GUID of owner of annotation - * - * @return int - */ -function get_annotations_max($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", -$value = "", $value_type = "", $owner_guid = 0) { - - return get_annotations_calculate_x("max", $entity_guid, $entity_type, $entity_subtype, $name, - $value, $value_type, $owner_guid); -} - -/** - * Return the minumum of a given integer annotation. - * - * @param int $entity_guid Guid of Entity - * @param string $entity_type Type of Entity - * @param string $entity_subtype Subtype of Entity - * @param string $name Name of annotation - * @param string $value Value of annotation - * @param string $value_type Type of value - * @param int $owner_guid GUID of owner of annotation - * - * @return int - */ -function get_annotations_min($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", -$value = "", $value_type = "", $owner_guid = 0) { - - return get_annotations_calculate_x("min", $entity_guid, $entity_type, $entity_subtype, $name, - $value, $value_type, $owner_guid); -} - -/** - * Return the average of a given integer annotation. - * - * @param int $entity_guid Guid of Entity - * @param string $entity_type Type of Entity - * @param string $entity_subtype Subtype of Entity - * @param string $name Name of annotation - * @param string $value Value of annotation - * @param string $value_type Type of value - * @param int $owner_guid GUID of owner of annotation - * - * @return int - */ -function get_annotations_avg($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", -$value = "", $value_type = "", $owner_guid = 0) { - - return get_annotations_calculate_x("avg", $entity_guid, $entity_type, $entity_subtype, $name, - $value, $value_type, $owner_guid); -} - -/** - * Count the number of annotations based on search parameters - * - * @param int $entity_guid Guid of Entity - * @param string $entity_type Type of Entity - * @param string $entity_subtype Subtype of Entity - * @param string $name Name of annotation - * @param string $value Value of annotation - * @param string $value_type Type of value - * @param int $owner_guid GUID of owner of annotation - * @param int $timelower Lower time limit - * @param int $timeupper Upper time limit - * - * @return int + * Get entities ordered by a mathematical calculation on annotation values + * + * @param array $options An options array: + * 'calculation' => The calculation to use. Must be a valid MySQL function. + * Defaults to sum. Result selected as 'annotation_calculation'. + * Don't confuse this "calculation" option with the + * "annotation_calculation" option to elgg_get_annotations(). + * This "calculation" option is applied to each entity's set of + * annotations and is selected as annotation_calculation for that row. + * See the docs for elgg_get_annotations() for proper use of the + * "annotation_calculation" option. + * 'order_by' => The order for the sorting. Defaults to 'annotation_calculation desc'. + * 'annotation_names' => The names of annotations on the entity. + * 'annotation_values' => The values of annotations on the entity. + * + * 'metadata_names' => The name of metadata on the entity. + * 'metadata_values' => The value of metadata on the entitiy. + * + * @return mixed If count, int. If not count, array. false on errors. */ -function count_annotations($entity_guid = 0, $entity_type = "", $entity_subtype = "", -$name = "", $value = "", $value_type = "", $owner_guid = 0, $timelower = 0, -$timeupper = 0) { - return get_annotations_calculate_x("count", $entity_guid, $entity_type, $entity_subtype, - $name, $value, $value_type, $owner_guid, $timelower, $timeupper); -} - -/** - * Perform a mathmatical calculation on integer annotations. - * - * @param string $sum What sort of calculation to perform - * @param int $entity_guid Guid of Entity - * @param string $entity_type Type of Entity - * @param string $entity_subtype Subtype of Entity - * @param string $name Name of annotation - * @param string $value Value of annotation - * @param string $value_type Type of value - * @param int $owner_guid GUID of owner of annotation - * @param int $timelower Lower time limit - * @param int $timeupper Upper time limit - * - * @return int - */ -function get_annotations_calculate_x($sum = "avg", $entity_guid, $entity_type = "", -$entity_subtype = "", $name = "", $value = "", $value_type = "", $owner_guid = 0, -$timelower = 0, $timeupper = 0) { - - global $CONFIG; - - $sum = sanitise_string($sum); - $entity_guid = (int)$entity_guid; - $entity_type = sanitise_string($entity_type); - $timeupper = (int)$timeupper; - $timelower = (int)$timelower; - - if ($entity_subtype) { - if (!$entity_subtype = get_subtype_id($entity_type, $entity_subtype)) { - // requesting a non-existing subtype: return false - return FALSE; - } - } - - if ($name != '' AND !$name = get_metastring_id($name)) { - return 0; - } - - if ($value != '' AND !$value = get_metastring_id($value)) { - return 0; - } - $value_type = sanitise_string($value_type); - $owner_guid = (int)$owner_guid; - - // if (empty($name)) return 0; - - $where = array(); - - if ($entity_guid) { - $where[] = "e.guid=$entity_guid"; - } - - if ($entity_type != "") { - $where[] = "e.type='$entity_type'"; - } - - if ($entity_subtype) { - $where[] = "e.subtype=$entity_subtype"; - } - - if ($name != "") { - $where[] = "a.name_id='$name'"; - } - - if ($value != "") { - $where[] = "a.value_id='$value'"; - } - - if ($value_type != "") { - $where[] = "a.value_type='$value_type'"; - } - - if ($owner_guid) { - $where[] = "a.owner_guid='$owner_guid'"; - } - - if ($timelower) { - $where[] = "a.time_created >= {$timelower}"; - } - - if ($timeupper) { - $where[] = "a.time_created <= {$timeupper}"; - } - - if ($sum != "count") { - $where[] = "a.value_type='integer'"; // Limit on integer types - } - - $query = "SELECT $sum(ms.string) as sum - FROM {$CONFIG->dbprefix}annotations a - JOIN {$CONFIG->dbprefix}entities e on a.entity_guid = e.guid - JOIN {$CONFIG->dbprefix}metastrings ms on a.value_id=ms.id WHERE "; - - foreach ($where as $w) { - $query .= " $w and "; - } - - $query .= get_access_sql_suffix("a"); // now add access - $query .= ' and ' . get_access_sql_suffix("e"); // now add access - - $row = get_data_row($query); - if ($row) { - return $row->sum; - } - - return false; -} - -/** - * Get entities ordered by a mathematical calculation - * - * @param string $sum What sort of calculation to perform - * @param string $entity_type Type of Entity - * @param string $entity_subtype Subtype of Entity - * @param string $name Name of annotation - * @param string $mdname Metadata name - * @param string $mdvalue Metadata value - * @param int $owner_guid GUID of owner of annotation - * @param int $limit Limit of results - * @param int $offset Offset of results - * @param string $orderdir Order of results - * @param bool $count Return count or entities - * - * @return mixed - */ -function get_entities_from_annotations_calculate_x($sum = "sum", $entity_type = "", -$entity_subtype = "", $name = "", $mdname = '', $mdvalue = '', $owner_guid = 0, -$limit = 10, $offset = 0, $orderdir = 'desc', $count = false) { - global $CONFIG; - - $sum = sanitise_string($sum); - $entity_type = sanitise_string($entity_type); - - if ($entity_subtype) { - if (!$entity_subtype = get_subtype_id($entity_type, $entity_subtype)) { - // requesting a non-existing subtype: return false - return FALSE; - } - } - - $name = get_metastring_id($name); - $limit = (int) $limit; - $offset = (int) $offset; - $owner_guid = (int) $owner_guid; - if (!empty($mdname) && !empty($mdvalue)) { - $meta_n = get_metastring_id($mdname); - $meta_v = get_metastring_id($mdvalue); - } - - if (empty($name)) { - return 0; - } - - $where = array(); - - if ($entity_type != "") { - $where[] = "e.type='$entity_type'"; - } - - if ($owner_guid > 0) { - $where[] = "e.container_guid = $owner_guid"; - } - - if ($entity_subtype) { - $where[] = "e.subtype=$entity_subtype"; - } - - if ($name != "") { - $where[] = "a.name_id='$name'"; - } - - if (!empty($mdname) && !empty($mdvalue)) { - if ($mdname != "") { - $where[] = "m.name_id='$meta_n'"; - } - - if ($mdvalue != "") { - $where[] = "m.value_id='$meta_v'"; - } - } - - if ($sum != "count") { - // Limit on integer types - $where[] = "a.value_type='integer'"; - } - - if (!$count) { - $query = "SELECT distinct e.*, $sum(ms.string) as sum "; - } else { - $query = "SELECT count(distinct e.guid) as num, $sum(ms.string) as sum "; - } - $query .= " from {$CONFIG->dbprefix}entities e" - . " JOIN {$CONFIG->dbprefix}annotations a on a.entity_guid = e.guid" - . " JOIN {$CONFIG->dbprefix}metastrings ms on a.value_id=ms.id "; - - if (!empty($mdname) && !empty($mdvalue)) { - $query .= " JOIN {$CONFIG->dbprefix}metadata m on m.entity_guid = e.guid "; - } - - $query .= " WHERE "; - foreach ($where as $w) { - $query .= " $w and "; - } - - $query .= get_access_sql_suffix("a"); // now add access - $query .= ' and ' . get_access_sql_suffix("e"); // now add access - if (!$count) { - $query .= ' group by e.guid'; - } - - if (!$count) { - $query .= ' order by sum ' . $orderdir; - $query .= ' limit ' . $offset . ' , ' . $limit; - return get_data($query, "entity_row_to_elggstar"); - } else { - if ($row = get_data_row($query)) { - return $row->num; - } - } - return false; -} - -/** - * Returns entities ordered by the sum of an annotation - * - * @param string $entity_type Type of Entity - * @param string $entity_subtype Subtype of Entity - * @param string $name Name of annotation - * @param string $mdname Metadata name - * @param string $mdvalue Metadata value - * @param int $owner_guid GUID of owner of annotation - * @param int $limit Limit of results - * @param int $offset Offset of results - * @param string $orderdir Order of results - * @param bool $count Return count or entities - * - * @return unknown - */ -function get_entities_from_annotation_count($entity_type = "", $entity_subtype = "", $name = "", -$mdname = '', $mdvalue = '', $owner_guid = 0, $limit = 10, $offset = 0, $orderdir = 'desc', -$count = false) { - - return get_entities_from_annotations_calculate_x('sum', $entity_type, $entity_subtype, - $name, $mdname, $mdvalue, $owner_guid, $limit, $offset, $orderdir, $count); -} - -/** - * Lists entities by the totals of a particular kind of annotation - * - * @param string $entity_type Type of entity. - * @param string $entity_subtype Subtype of entity. - * @param string $name Name of annotation. - * @param int $limit Maximum number of results to return. - * @param int $owner_guid Owner. - * @param int $group_guid Group container. Currently only supported if entity_type is object - * @param boolean $asc Whether to list in ascending or descending order (default: desc) - * @param boolean $fullview Whether to display the entities in full - * @param boolean $listtypetoggle Can the 'gallery' view can be displayed (default: no) - * @param boolean $pagination Add pagination - * @param string $orderdir Order desc or asc - * - * @return string Formatted entity list - */ -function list_entities_from_annotation_count($entity_type = "", $entity_subtype = "", $name = "", -$limit = 10, $owner_guid = 0, $group_guid = 0, $asc = false, $fullview = true, -$listtypetoggle = false, $pagination = true, $orderdir = 'desc') { - if ($asc) { - $asc = "asc"; - } else { - $asc = "desc"; - } - - $offset = (int) get_input("offset", 0); - $count = get_entities_from_annotation_count($entity_type, $entity_subtype, $name, - '', '', $owner_guid, $limit, $offset, $orderdir, true); - - $entities = get_entities_from_annotation_count($entity_type, $entity_subtype, $name, - '', '', $owner_guid, $limit, $offset, $orderdir, false); - - return elgg_view_entity_list($entities, $count, $offset, $limit, - $fullview, $listtypetoggle, $pagination); -} - -/** - * Lists entities by the totals of a particular kind of annotation AND - * the value of a piece of metadata - * - * @param string $entity_type Type of entity. - * @param string $entity_subtype Subtype of entity. - * @param string $name Name of annotation. - * @param string $mdname Metadata name - * @param string $mdvalue Metadata value - * @param int $limit Maximum number of results to return. - * @param int $owner_guid Owner. - * @param int $group_guid Group container. Currently only supported if entity_type is object - * @param boolean $asc Whether to list in ascending or descending order (default: desc) - * @param boolean $fullview Whether to display the entities in full - * @param boolean $listtypetoggle Can the 'gallery' view can be displayed (default: no) - * @param boolean $pagination Display pagination - * @param string $orderdir 'desc' or 'asc' - * - * @return string Formatted entity list - */ -function list_entities_from_annotation_count_by_metadata($entity_type = "", $entity_subtype = "", -$name = "", $mdname = '', $mdvalue = '', $limit = 10, $owner_guid = 0, $group_guid = 0, -$asc = false, $fullview = true, $listtypetoggle = false, $pagination = true, $orderdir = 'desc') { +function elgg_get_entities_from_annotation_calculation($options) { + $db_prefix = elgg_get_config('dbprefix'); + $defaults = array( + 'calculation' => 'sum', + 'order_by' => 'annotation_calculation desc' + ); - if ($asc) { - $asc = "asc"; - } else { - $asc = "desc"; - } + $options = array_merge($defaults, $options); - $offset = (int) get_input("offset", 0); - $count = get_entities_from_annotation_count($entity_type, $entity_subtype, $name, $mdname, - $mdvalue, $owner_guid, $limit, $offset, $orderdir, true); - $entities = get_entities_from_annotation_count($entity_type, $entity_subtype, $name, $mdname, - $mdvalue, $owner_guid, $limit, $offset, $orderdir, false); + $function = sanitize_string(elgg_extract('calculation', $options, 'sum', false)); - return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, - $listtypetoggle, $pagination); -} + // you must cast this as an int or it sorts wrong. + $options['selects'][] = 'e.*'; + $options['selects'][] = "$function(cast(a_msv.string as signed)) as annotation_calculation"; -/** - * Delete a given annotation. - * - * @param int $id The annotation id - * - * @return bool - */ -function delete_annotation($id) { - global $CONFIG; + // need our own join to get the values because the lower level functions don't + // add all the joins if it's a different callback. + $options['joins'][] = "JOIN {$db_prefix}metastrings a_msv ON n_table.value_id = a_msv.id"; - $id = (int)$id; + // don't need access control because it's taken care of by elgg_get_annotations. + $options['group_by'] = 'n_table.entity_guid'; - $access = get_access_sql_suffix(); - $annotation = get_annotation($id); + $options['callback'] = 'entity_row_to_elggstar'; - if (elgg_trigger_event('delete', 'annotation', $annotation)) { - remove_from_river_by_annotation($id); - return delete_data("DELETE from {$CONFIG->dbprefix}annotations where id=$id and $access"); - } + // see #4393 + // @todo remove after the 'count' shortcut is removed from elgg_get_annotations() + $options['__egefac'] = true; - return FALSE; + return elgg_get_annotations($options); } /** - * Clear all the annotations for a given entity, assuming you have access to that metadata. + * List entities from an annotation calculation. * - * @param int $guid The entity guid - * @param string $name The name of the annotation to delete. - * - * @return int Number of annotations deleted or false if an error - */ -function clear_annotations($guid, $name = "") { - global $CONFIG; - - $guid = (int)$guid; - - if (!empty($name)) { - $name = get_metastring_id($name); - if ($name === false) { - // name doesn't exist so 0 rows were deleted - return 0; - } - } - - $entity_guid = (int) $guid; - if ($entity = get_entity($entity_guid)) { - if ($entity->canEdit()) { - $where = array(); - - if ($name != "") { - $where[] = " name_id='$name'"; - } - - $query = "DELETE from {$CONFIG->dbprefix}annotations where entity_guid=$guid "; - foreach ($where as $w) { - $query .= " and $w"; - } - - return delete_data($query); - } - } - - return FALSE; -} - -/** - * Clear all annotations belonging to a given owner_guid + * @see elgg_get_entities_from_annotation_calculation() * - * @param int $owner_guid The owner + * @param array $options An options array. * - * @return int Number of annotations deleted + * @return string */ -function clear_annotations_by_owner($owner_guid) { - global $CONFIG; - - $owner_guid = (int)$owner_guid; - - $query = "SELECT id from {$CONFIG->dbprefix}annotations WHERE owner_guid=$owner_guid"; - - $annotations = get_data($query); - $deleted = 0; - - if (!$annotations) { - return 0; - } - - foreach ($annotations as $id) { - // Is this the best way? - if (delete_annotation($id->id)) { - $deleted++; - } - } +function elgg_list_entities_from_annotation_calculation($options) { + $defaults = array( + 'calculation' => 'sum', + 'order_by' => 'annotation_calculation desc' + ); + $options = array_merge($defaults, $options); - return $deleted; + return elgg_list_entities($options, 'elgg_get_entities_from_annotation_calculation'); } /** - * Handler called by trigger_plugin_hook on the "export" event. + * Export the annotations for the specified entity * * @param string $hook 'export' - * @param string $entity_type 'all' + * @param string $type 'all' * @param mixed $returnvalue Default return value - * @param mixed $params List of params to export + * @param mixed $params Parameters determining what annotations to export * * @elgg_plugin_hook export all * - * @return mixed + * @return array + * @throws InvalidParameterException + * @access private */ -function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $params) { +function export_annotation_plugin_hook($hook, $type, $returnvalue, $params) { // Sanity check values if ((!is_array($params)) && (!isset($params['guid']))) { throw new InvalidParameterException(elgg_echo('InvalidParameterException:GUIDNotForExport')); @@ -1125,9 +495,12 @@ function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $param } $guid = (int)$params['guid']; - $name = $params['name']; + $options = array('guid' => $guid, 'limit' => 0); + if (isset($params['name'])) { + $options['annotation_name'] = $params['name']; + } - $result = get_annotations($guid); + $result = elgg_get_annotations($options); if ($result) { foreach ($result as $r) { @@ -1149,7 +522,7 @@ function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $param function get_annotation_url($id) { $id = (int)$id; - if ($extender = get_annotation($id)) { + if ($extender = elgg_get_annotation_from_id($id)) { return get_extender_url($extender); } return false; @@ -1162,24 +535,26 @@ function get_annotation_url($id) { * @param string $annotation_type Type of annotation * @param int $owner_guid Defaults to logged in user. * - * @return true | false + * @return bool + * @since 1.8.0 */ function elgg_annotation_exists($entity_guid, $annotation_type, $owner_guid = NULL) { global $CONFIG; - if (!$owner_guid && !($owner_guid = get_loggedin_userid())) { + if (!$owner_guid && !($owner_guid = elgg_get_logged_in_user_guid())) { return FALSE; } - $entity_guid = (int)$entity_guid; - $annotation_type = sanitise_string($annotation_type); + $entity_guid = sanitize_int($entity_guid); + $owner_guid = sanitize_int($owner_guid); + $annotation_type = sanitize_string($annotation_type); - $sql = "select a.id" . - " FROM {$CONFIG->dbprefix}annotations a, {$CONFIG->dbprefix}metastrings m " . - " WHERE a.owner_guid={$owner_guid} AND a.entity_guid={$entity_guid} " . - " AND a.name_id=m.id AND m.string='{$annotation_type}'"; + $sql = "SELECT a.id FROM {$CONFIG->dbprefix}annotations a" . + " JOIN {$CONFIG->dbprefix}metastrings m ON a.name_id = m.id" . + " WHERE a.owner_guid = $owner_guid AND a.entity_guid = $entity_guid" . + " AND m.string = '$annotation_type'"; - if ($check_annotation = get_data_row($sql)) { + if (get_data_row($sql)) { return TRUE; } @@ -1187,16 +562,57 @@ function elgg_annotation_exists($entity_guid, $annotation_type, $owner_guid = NU } /** + * Return the URL for a comment + * + * @param ElggAnnotation $comment The comment object + * @return string + * @access private + */ +function elgg_comment_url_handler(ElggAnnotation $comment) { + $entity = $comment->getEntity(); + if ($entity) { + return $entity->getURL() . '#item-annotation-' . $comment->id; + } + return ""; +} + +/** * Register an annotation url handler. * - * @param string $function_name The function. * @param string $extender_name The name, default 'all'. + * @param string $function_name The function. * * @return string */ -function register_annotation_url_handler($function_name, $extender_name = "all") { - return register_extender_url_handler($function_name, 'annotation', $extender_name); +function elgg_register_annotation_url_handler($extender_name = "all", $function_name) { + return elgg_register_extender_url_handler('annotation', $extender_name, $function_name); +} + +/** + * Register annotation unit tests + * + * @param string $hook + * @param string $type + * @param array $value + * @param array $params + * @return array + * @access private + */ +function annotations_test($hook, $type, $value, $params) { + global $CONFIG; + $value[] = $CONFIG->path . 'engine/tests/api/annotations.php'; + return $value; +} + +/** + * Initialize the annotation library + * @access private + */ +function elgg_annotations_init() { + elgg_register_annotation_url_handler('generic_comment', 'elgg_comment_url_handler'); + + elgg_register_plugin_hook_handler("export", "all", "export_annotation_plugin_hook", 2); + elgg_register_plugin_hook_handler('unit_test', 'system', 'annotations_test'); } -/** Register the hook */ -elgg_register_plugin_hook_handler("export", "all", "export_annotation_plugin_hook", 2); +elgg_register_event_handler('init', 'system', 'elgg_annotations_init'); diff --git a/engine/lib/cache.php b/engine/lib/cache.php index 8fe7d3562..3116c1a9b 100644 --- a/engine/lib/cache.php +++ b/engine/lib/cache.php @@ -7,16 +7,17 @@ * @subpackage Cache */ +/* Filepath Cache */ + /** - * Returns an ElggCache object suitable for caching view - * file load paths to disk under $CONFIG->dataroot. + * Returns an ElggCache object suitable for caching system information * * @todo Can this be done in a cleaner way? * @todo Swap to memcache etc? * - * @return ElggFileCache A cache object suitable for caching file load paths. + * @return ElggFileCache */ -function elgg_get_filepath_cache() { +function elgg_get_system_cache() { global $CONFIG; /** @@ -25,56 +26,55 @@ function elgg_get_filepath_cache() { static $FILE_PATH_CACHE; if (!$FILE_PATH_CACHE) { - $FILE_PATH_CACHE = new ElggFileCache($CONFIG->dataroot); + $FILE_PATH_CACHE = new ElggFileCache($CONFIG->dataroot . 'system_cache/'); } return $FILE_PATH_CACHE; } /** - * Deletes the view file paths cache from disk. + * Reset the system cache by deleting the caches * - * @return bool On success + * @return void */ -function elgg_filepath_cache_reset() { - $cache = elgg_get_filepath_cache(); - return $cache->delete('view_paths'); +function elgg_reset_system_cache() { + $cache = elgg_get_system_cache(); + $cache->clear(); } /** - * Saves $data to the views file paths disk cache as - * 'view_paths'. + * Saves a system cache. * - * @param mixed $data The data - * - * @return bool On success + * @param string $type The type or identifier of the cache + * @param string $data The data to be saved + * @return bool */ -function elgg_filepath_cache_save($data) { +function elgg_save_system_cache($type, $data) { global $CONFIG; - if ($CONFIG->viewpath_cache_enabled) { - $cache = elgg_get_filepath_cache(); - return $cache->save('view_paths', $data); + if ($CONFIG->system_cache_enabled) { + $cache = elgg_get_system_cache(); + return $cache->save($type, $data); } return false; } /** - * Returns the contents of the views file paths cache from disk. + * Retrieve the contents of a system cache. * - * @return mixed Null if simplecache isn't enabled, the contents of the - * views file paths cache if it is. + * @param string $type The type of cache to load + * @return string */ -function elgg_filepath_cache_load() { +function elgg_load_system_cache($type) { global $CONFIG; - if ($CONFIG->viewpath_cache_enabled) { - $cache = elgg_get_filepath_cache(); - $cached_view_paths = $cache->load('view_paths'); + if ($CONFIG->system_cache_enabled) { + $cache = elgg_get_system_cache(); + $cached_data = $cache->load($type); - if ($cached_view_paths) { - return $cached_view_paths; + if ($cached_data) { + return $cached_data; } } @@ -82,33 +82,372 @@ function elgg_filepath_cache_load() { } /** - * Enables the views file paths disk cache. + * Enables the system disk cache. * - * Uses the 'viewpath_cache_enabled' datalist with a boolean value. - * Resets the views paths cache. + * Uses the 'system_cache_enabled' datalist with a boolean value. + * Resets the system cache. * - * @return null + * @return void */ -function elgg_enable_filepath_cache() { +function elgg_enable_system_cache() { global $CONFIG; - datalist_set('viewpath_cache_enabled', 1); - $CONFIG->viewpath_cache_enabled = 1; - elgg_filepath_cache_reset(); + datalist_set('system_cache_enabled', 1); + $CONFIG->system_cache_enabled = 1; + elgg_reset_system_cache(); } /** - * Disables the views file paths disk cache. + * Disables the system disk cache. * - * Uses the 'viewpath_cache_enabled' datalist with a boolean value. - * Resets the views paths cache. + * Uses the 'system_cache_enabled' datalist with a boolean value. + * Resets the system cache. * - * @return null + * @return void + */ +function elgg_disable_system_cache() { + global $CONFIG; + + datalist_set('system_cache_enabled', 0); + $CONFIG->system_cache_enabled = 0; + elgg_reset_system_cache(); +} + +/** @todo deprecate in Elgg 1.9 **/ + +/** + * @access private + */ +function elgg_get_filepath_cache() { + return elgg_get_system_cache(); +} +/** + * @access private + */ +function elgg_filepath_cache_reset() { + elgg_reset_system_cache(); +} +/** + * @access private + */ +function elgg_filepath_cache_save($type, $data) { + return elgg_save_system_cache($type, $data); +} +/** + * @access private + */ +function elgg_filepath_cache_load($type) { + return elgg_load_system_cache($type); +} +/** + * @access private + */ +function elgg_enable_filepath_cache() { + elgg_enable_system_cache(); +} +/** + * @access private */ function elgg_disable_filepath_cache() { + elgg_disable_system_cache(); +} + +/* Simplecache */ + +/** + * Registers a view to simple cache. + * + * Simple cache is a caching mechanism that saves the output of + * views and its extensions into a file. If the view is called + * by the {@link simplecache/view.php} file, the Elgg framework will + * not be loaded and the contents of the view will returned + * from file. + * + * @warning Simple cached views must take no parameters and return + * the same content no matter who is logged in. + * + * @example + * $blog_js = elgg_get_simplecache_url('js', 'blog/save_draft'); + * elgg_register_simplecache_view('js/blog/save_draft'); + * elgg_register_js('elgg.blog', $blog_js); + * elgg_load_js('elgg.blog'); + * + * @param string $viewname View name + * + * @return void + * @link http://docs.elgg.org/Views/Simplecache + * @see elgg_regenerate_simplecache() + * @since 1.8.0 + */ +function elgg_register_simplecache_view($viewname) { global $CONFIG; - datalist_set('viewpath_cache_enabled', 0); - $CONFIG->viewpath_cache_enabled = 0; - elgg_filepath_cache_reset(); + if (!isset($CONFIG->views)) { + $CONFIG->views = new stdClass; + } + + if (!isset($CONFIG->views->simplecache)) { + $CONFIG->views->simplecache = array(); + } + + $CONFIG->views->simplecache[] = $viewname; } + +/** + * Get the URL for the cached file + * + * @warning You must register the view with elgg_register_simplecache_view() + * for caching to work. See elgg_register_simplecache_view() for a full example. + * + * @param string $type The file type: css or js + * @param string $view The view name + * @return string + * @since 1.8.0 + */ +function elgg_get_simplecache_url($type, $view) { + global $CONFIG; + $lastcache = (int)$CONFIG->lastcache; + $viewtype = elgg_get_viewtype(); + elgg_register_simplecache_view("$type/$view");// see #5302 + if (elgg_is_simplecache_enabled()) { + $url = elgg_get_site_url() . "cache/$type/$viewtype/$view.$lastcache.$type"; + } else { + $url = elgg_get_site_url() . "$type/$view.$lastcache.$type"; + $elements = array("view" => $viewtype); + $url = elgg_http_add_url_query_elements($url, $elements); + } + + return $url; +} + +/** + * Regenerates the simple cache. + * + * @warning This does not invalidate the cache, but actively rebuilds it. + * + * @param string $viewtype Optional viewtype to regenerate. Defaults to all valid viewtypes. + * + * @return void + * @see elgg_register_simplecache_view() + * @since 1.8.0 + */ +function elgg_regenerate_simplecache($viewtype = NULL) { + global $CONFIG; + + if (!isset($CONFIG->views->simplecache) || !is_array($CONFIG->views->simplecache)) { + return; + } + + $lastcached = time(); + + // @todo elgg_view() checks if the page set is done (isset($CONFIG->pagesetupdone)) and + // triggers an event if it's not. Calling elgg_view() here breaks submenus + // (at least) because the page setup hook is called before any + // contexts can be correctly set (since this is called before page_handler()). + // To avoid this, lie about $CONFIG->pagehandlerdone to force + // the trigger correctly when the first view is actually being output. + $CONFIG->pagesetupdone = TRUE; + + if (!file_exists($CONFIG->dataroot . 'views_simplecache')) { + mkdir($CONFIG->dataroot . 'views_simplecache'); + } + + if (isset($viewtype)) { + $viewtypes = array($viewtype); + } else { + $viewtypes = $CONFIG->view_types; + } + + $original_viewtype = elgg_get_viewtype(); + + // disable error reporting so we don't cache problems + $old_debug = elgg_get_config('debug'); + elgg_set_config('debug', null); + + foreach ($viewtypes as $viewtype) { + elgg_set_viewtype($viewtype); + foreach ($CONFIG->views->simplecache as $view) { + $viewcontents = elgg_view($view); + $viewname = md5(elgg_get_viewtype() . $view); + if ($handle = fopen($CONFIG->dataroot . 'views_simplecache/' . $viewname, 'w')) { + fwrite($handle, $viewcontents); + fclose($handle); + } + } + + datalist_set("simplecache_lastupdate_$viewtype", $lastcached); + datalist_set("simplecache_lastcached_$viewtype", $lastcached); + } + + elgg_set_config('debug', $old_debug); + elgg_set_viewtype($original_viewtype); + + // needs to be set for links in html head + $CONFIG->lastcache = $lastcached; + + unset($CONFIG->pagesetupdone); +} + +/** + * Is simple cache enabled + * + * @return bool + * @since 1.8.0 + */ +function elgg_is_simplecache_enabled() { + if (elgg_get_config('simplecache_enabled')) { + return true; + } + + return false; +} + +/** + * Enables the simple cache. + * + * @access private + * @see elgg_register_simplecache_view() + * @return void + * @since 1.8.0 + */ +function elgg_enable_simplecache() { + global $CONFIG; + + datalist_set('simplecache_enabled', 1); + $CONFIG->simplecache_enabled = 1; + elgg_regenerate_simplecache(); +} + +/** + * Disables the simple cache. + * + * @warning Simplecache is also purged when disabled. + * + * @access private + * @see elgg_register_simplecache_view() + * @return void + * @since 1.8.0 + */ +function elgg_disable_simplecache() { + global $CONFIG; + if ($CONFIG->simplecache_enabled) { + datalist_set('simplecache_enabled', 0); + $CONFIG->simplecache_enabled = 0; + + // purge simple cache + if ($handle = opendir($CONFIG->dataroot . 'views_simplecache')) { + while (false !== ($file = readdir($handle))) { + if ($file != "." && $file != "..") { + unlink($CONFIG->dataroot . 'views_simplecache/' . $file); + } + } + closedir($handle); + } + } +} + +/** + * Deletes all cached views in the simplecache and sets the lastcache and + * lastupdate time to 0 for every valid viewtype. + * + * @return bool + * @since 1.7.4 + */ +function elgg_invalidate_simplecache() { + global $CONFIG; + + if (!isset($CONFIG->views->simplecache) || !is_array($CONFIG->views->simplecache)) { + return false; + } + + $handle = opendir($CONFIG->dataroot . 'views_simplecache'); + + if (!$handle) { + return false; + } + + // remove files. + $return = true; + while (false !== ($file = readdir($handle))) { + if ($file != "." && $file != "..") { + $return &= unlink($CONFIG->dataroot . 'views_simplecache/' . $file); + } + } + closedir($handle); + + // reset cache times + $viewtypes = $CONFIG->view_types; + + if (!is_array($viewtypes)) { + return false; + } + + foreach ($viewtypes as $viewtype) { + $return &= datalist_set("simplecache_lastupdate_$viewtype", 0); + $return &= datalist_set("simplecache_lastcached_$viewtype", 0); + } + + return $return; +} + +/** + * @see elgg_reset_system_cache() + * @access private + */ +function _elgg_load_cache() { + global $CONFIG; + + $CONFIG->system_cache_loaded = false; + + $CONFIG->views = new stdClass(); + $data = elgg_load_system_cache('view_locations'); + if (!is_string($data)) { + return; + } + $CONFIG->views->locations = unserialize($data); + + $data = elgg_load_system_cache('view_types'); + if (!is_string($data)) { + return; + } + $CONFIG->view_types = unserialize($data); + + $CONFIG->system_cache_loaded = true; +} + +/** + * @access private + */ +function _elgg_cache_init() { + global $CONFIG; + + $viewtype = elgg_get_viewtype(); + + // Regenerate the simple cache if expired. + // Don't do it on upgrade because upgrade does it itself. + // @todo - move into function and perhaps run off init system event + if (!defined('UPGRADING')) { + $lastupdate = datalist_get("simplecache_lastupdate_$viewtype"); + $lastcached = datalist_get("simplecache_lastcached_$viewtype"); + if ($lastupdate == 0 || $lastcached < $lastupdate) { + elgg_regenerate_simplecache($viewtype); + $lastcached = datalist_get("simplecache_lastcached_$viewtype"); + } + $CONFIG->lastcache = $lastcached; + } + + // cache system data if enabled and not loaded + if ($CONFIG->system_cache_enabled && !$CONFIG->system_cache_loaded) { + elgg_save_system_cache('view_locations', serialize($CONFIG->views->locations)); + elgg_save_system_cache('view_types', serialize($CONFIG->view_types)); + } + + if ($CONFIG->system_cache_enabled && !$CONFIG->i18n_loaded_from_cache) { + reload_all_translations(); + foreach ($CONFIG->translations as $lang => $map) { + elgg_save_system_cache("$lang.lang", serialize($map)); + } + } +} + +elgg_register_event_handler('ready', 'system', '_elgg_cache_init'); diff --git a/engine/lib/calendar.php b/engine/lib/calendar.php index 6ee9980e5..e6f95934c 100644 --- a/engine/lib/calendar.php +++ b/engine/lib/calendar.php @@ -16,6 +16,7 @@ * @param int $year Year * * @return int + * @access private */ function get_day_start($day = null, $month = null, $year = null) { return mktime(0, 0, 0, $month, $day, $year); @@ -29,6 +30,7 @@ function get_day_start($day = null, $month = null, $year = null) { * @param int $year Year * * @return int + * @access private */ function get_day_end($day = null, $month = null, $year = null) { return mktime(23, 59, 59, $month, $day, $year); @@ -37,6 +39,8 @@ function get_day_end($day = null, $month = null, $year = null) { /** * Return the notable entities for a given time period. * + * @todo this function also accepts an array(type => subtypes) for 3rd arg. Should we document this? + * * @param int $start_time The start time as a unix timestamp. * @param int $end_time The end time as a unix timestamp. * @param string $type The type of entity (eg "user", "object" etc) @@ -50,6 +54,7 @@ function get_day_end($day = null, $month = null, $year = null) { * @param mixed $container_guid Container or containers to get entities from (default: any). * * @return array|false + * @access private */ function get_notable_entities($start_time, $end_time, $type = "", $subtype = "", $owner_guid = 0, $order_by = "asc", $limit = 10, $offset = 0, $count = false, $site_guid = 0, @@ -197,6 +202,7 @@ $container_guid = null) { * @param bool $count If true, returns count instead of entities. (Default: false) * * @return int|array A list of entities, or a count if $count is set to true + * @access private */ function get_notable_entities_from_metadata($start_time, $end_time, $meta_name, $meta_value = "", $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", @@ -326,6 +332,7 @@ $site_guid = 0, $count = false) { * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any * * @return array|int|false An array of entities, or the number of entities, or false on failure + * @access private */ function get_noteable_entities_from_relationship($start_time, $end_time, $relationship, $relationship_guid, $inverse_relationship = false, $type = "", $subtype = "", $owner_guid = 0, @@ -435,6 +442,7 @@ $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0) { * @param mixed $container_guid Container(s) to get entities from (default: any). * * @return array|false + * @access private */ function get_todays_entities($type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = null) { @@ -461,6 +469,7 @@ $limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = null * @param bool $count If true, returns count instead of entities. (Default: false) * * @return int|array A list of entities, or a count if $count is set to true + * @access private */ function get_todays_entities_from_metadata($meta_name, $meta_value = "", $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, @@ -491,6 +500,7 @@ $count = false) { * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any * * @return array|int|false An array of entities, or the number of entities, or false on failure + * @access private */ function get_todays_entities_from_relationship($relationship, $relationship_guid, $inverse_relationship = false, $type = "", $subtype = "", $owner_guid = 0, @@ -520,6 +530,7 @@ $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0) { * @param boolean $navigation Display pagination? Default: true * * @return string A viewable list of entities + * @access private */ function list_notable_entities($start_time, $end_time, $type= "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) { @@ -549,6 +560,7 @@ $limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) { * @param boolean $navigation Display pagination? Default: true * * @return string A viewable list of entities + * @access private */ function list_todays_entities($type= "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) { @@ -558,4 +570,4 @@ $fullview = true, $listtypetoggle = false, $navigation = true) { return list_notable_entities($day_start, $day_end, $type, $subtype, $owner_guid, $limit, $fullview, $listtypetoggle, $navigation); -}
\ No newline at end of file +} diff --git a/engine/lib/configuration.php b/engine/lib/configuration.php index 5adc4bddc..55e5bbd36 100644 --- a/engine/lib/configuration.php +++ b/engine/lib/configuration.php @@ -3,8 +3,9 @@ * Elgg configuration procedural code. * * Includes functions for manipulating the configuration values stored in the database - * Plugin authors should use the {@link get_config()}, {@link set_config()}, - * and {@unset_config()} functions to access or update config values. + * Plugin authors should use the {@link elgg_get_config()}, {@link elgg_set_config()}, + * {@link elgg_save_config()}, and {@unset_config()} functions to access or update + * config values. * * Elgg's configuration is split among 2 tables and 1 file: * - dbprefix_config @@ -35,6 +36,7 @@ function elgg_get_site_url($site_guid = 0) { if (!$site instanceof ElggSite) { return false; } + /* @var ElggSite $site */ return $site->url; } @@ -45,7 +47,7 @@ function elgg_get_site_url($site_guid = 0) { * @return string * @since 1.8.0 */ -function elgg_get_plugin_path() { +function elgg_get_plugins_path() { global $CONFIG; return $CONFIG->pluginspath; } @@ -62,12 +64,23 @@ function elgg_get_data_path() { } /** + * Get the root directory path for this installation + * + * @return string + * @since 1.8.0 + */ +function elgg_get_root_path() { + global $CONFIG; + return $CONFIG->path; +} + +/** * Get an Elgg configuration value * * @param string $name Name of the configuration value * @param int $site_guid NULL for installation setting, 0 for default site * - * @return mixed Configuration value or false if it does not exist + * @return mixed Configuration value or null if it does not exist * @since 1.8.0 */ function elgg_get_config($name, $site_guid = 0) { @@ -79,23 +92,29 @@ function elgg_get_config($name, $site_guid = 0) { return $CONFIG->$name; } - if ($site_guid === NULL) { + if ($site_guid === null) { // installation wide setting $value = datalist_get($name); } else { - // site specific setting - if ($site_guid == 0) { - $site_guid = (int) $CONFIG->site_id; + // hit DB only if we're not sure if value exists or not + if (!isset($CONFIG->site_config_loaded)) { + // site specific setting + if ($site_guid == 0) { + $site_guid = (int) $CONFIG->site_id; + } + $value = get_config($name, $site_guid); + } else { + $value = null; } - $value = get_config($name, $site_guid); } - if ($value !== false) { - $CONFIG->$name = $value; - return $value; + // @todo document why we don't cache false + if ($value === false) { + return null; } - - return false; + + $CONFIG->$name = $value; + return $value; } /** @@ -120,7 +139,7 @@ function elgg_set_config($name, $value) { /** * Save a configuration setting * - * @param string $name Configuration name + * @param string $name Configuration name (cannot be greater than 255 characters) * @param mixed $value Configuration value. Should be string for installation setting * @param int $site_guid NULL for installation setting, 0 for default site * @@ -132,6 +151,11 @@ function elgg_save_config($name, $value, $site_guid = 0) { $name = trim($name); + if (strlen($name) > 255) { + elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR"); + return false; + } + elgg_set_config($name, $value); if ($site_guid === NULL) { @@ -150,14 +174,15 @@ function elgg_save_config($name, $value, $site_guid = 0) { /** * Check that installation has completed and the database is populated. * - * @throws InstallationException + * @throws InstallationException|DatabaseException * @return void + * @access private */ function verify_installation() { global $CONFIG; if (isset($CONFIG->installed)) { - return $CONFIG->installed; + return; } try { @@ -194,13 +219,21 @@ $DATALIST_CACHE = array(); * * @tip Use datalists to store information common to a full installation. * - * @param string $name The name of the datalist element - * - * @return string|false The datalist value or false if it doesn't exist. + * @param string $name The name of the datalist + * @return string|null|false String if value exists, null if doesn't, false on error + * @access private */ function datalist_get($name) { global $CONFIG, $DATALIST_CACHE; + $name = trim($name); + + // cannot store anything longer than 255 characters in db, so catch here + if (elgg_strlen($name) > 255) { + elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR"); + return false; + } + $name = sanitise_string($name); if (isset($DATALIST_CACHE[$name])) { return $DATALIST_CACHE[$name]; @@ -239,7 +272,7 @@ function datalist_get($name) { } } - return false; + return null; } /** @@ -248,13 +281,20 @@ function datalist_get($name) { * @param string $name The name of the datalist * @param string $value The new value * - * @return true + * @return bool + * @access private */ function datalist_set($name, $value) { global $CONFIG, $DATALIST_CACHE; - $name = sanitise_string($name); - $value = sanitise_string($value); + // cannot store anything longer than 255 characters in db, so catch before we set + if (elgg_strlen($name) > 255) { + elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR"); + return false; + } + + $sanitised_name = sanitise_string($name); + $sanitised_value = sanitise_string($value); // If memcache is available then invalidate the cached copy static $datalist_memcache; @@ -266,13 +306,16 @@ function datalist_set($name, $value) { $datalist_memcache->delete($name); } - insert_data("INSERT into {$CONFIG->dbprefix}datalists" - . " set name = '{$name}', value = '{$value}'" - . " ON DUPLICATE KEY UPDATE value='{$value}'"); - - $DATALIST_CACHE[$name] = $value; + $success = insert_data("INSERT into {$CONFIG->dbprefix}datalists" + . " set name = '{$sanitised_name}', value = '{$sanitised_value}'" + . " ON DUPLICATE KEY UPDATE value='{$sanitised_value}'"); - return true; + if ($success !== FALSE) { + $DATALIST_CACHE[$name] = $value; + return true; + } else { + return false; + } } /** @@ -290,6 +333,9 @@ function datalist_set($name, $value) { * This will cause the run once function to be run on all installations. To perform * additional upgrades, create new functions for each release. * + * @warning The function name cannot be longer than 255 characters long due to + * the current schema for the datalist table. + * * @internal A datalist entry $functioname is created with the value of time(). * * @param string $functionname The name of the function you want to run. @@ -299,10 +345,14 @@ function datalist_set($name, $value) { * @return bool */ function run_function_once($functionname, $timelastupdatedcheck = 0) { - if ($lastupdated = datalist_get($functionname)) { + $lastupdated = datalist_get($functionname); + if ($lastupdated) { $lastupdated = (int) $lastupdated; - } else { + } elseif ($lastupdated !== false) { $lastupdated = 0; + } else { + // unable to check datalist + return false; } if (is_callable($functionname) && $lastupdated <= $timelastupdatedcheck) { $functionname(); @@ -358,15 +408,24 @@ function unset_config($name, $site_guid = 0) { * @param string $value Its value * @param int $site_guid Optionally, the GUID of the site (current site is assumed by default) * - * @return 0 + * @return bool * @todo The config table doens't have numeric primary keys so insert_data returns 0. * @todo Use "INSERT ... ON DUPLICATE KEY UPDATE" instead of trying to delete then add. * @see unset_config() * @see get_config() + * @access private */ function set_config($name, $value, $site_guid = 0) { global $CONFIG; + $name = trim($name); + + // cannot store anything longer than 255 characters in db, so catch before we set + if (elgg_strlen($name) > 255) { + elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR"); + return false; + } + // Unset existing unset_config($name, $site_guid); @@ -379,7 +438,8 @@ function set_config($name, $value, $site_guid = 0) { $query = "insert into {$CONFIG->dbprefix}config" . " set name = '{$name}', value = '{$value}', site_guid = {$site_guid}"; - return insert_data($query); + $result = insert_data($query); + return $result !== false; } /** @@ -392,30 +452,65 @@ function set_config($name, $value, $site_guid = 0) { * @param string $name The name of the config value * @param int $site_guid Optionally, the GUID of the site (current site is assumed by default) * - * @return mixed|false + * @return mixed|null * @see set_config() * @see unset_config() + * @access private */ function get_config($name, $site_guid = 0) { global $CONFIG; + $name = sanitise_string($name); + $site_guid = (int) $site_guid; + + // check for deprecated values. + // @todo might be a better spot to define this? + $new_name = false; + switch($name) { + case 'viewpath': + $new_name = 'view_path'; + $dep_version = 1.8; + break; + + case 'pluginspath': + $new_name = 'plugins_path'; + $dep_version = 1.8; + break; + + case 'sitename': + $new_name = 'site_name'; + $dep_version = 1.8; + break; + } + + // @todo these haven't really been implemented in Elgg 1.8. Complete in 1.9. + // show dep message + if ($new_name) { + // $msg = "Config value $name has been renamed as $new_name"; + $name = $new_name; + // elgg_deprecated_notice($msg, $dep_version); + } + + // decide from where to return the value if (isset($CONFIG->$name)) { return $CONFIG->$name; } - $name = sanitise_string($name); - $site_guid = (int) $site_guid; + if ($site_guid == 0) { $site_guid = (int) $CONFIG->site_id; } - if ($result = get_data_row("SELECT value FROM {$CONFIG->dbprefix}config - WHERE name = '{$name}' and site_guid = {$site_guid}")) { + + $result = get_data_row("SELECT value FROM {$CONFIG->dbprefix}config + WHERE name = '{$name}' and site_guid = {$site_guid}"); + + if ($result) { $result = $result->value; $result = unserialize($result->value); $CONFIG->$name = $result; return $result; } - return false; + return null; } /** @@ -424,6 +519,7 @@ function get_config($name, $site_guid = 0) { * @param int $site_guid Optionally, the GUID of the site (current site is assumed by default) * * @return bool + * @access private */ function get_all_config($site_guid = 0) { global $CONFIG; @@ -431,10 +527,10 @@ function get_all_config($site_guid = 0) { $site_guid = (int) $site_guid; if ($site_guid == 0) { - $site_guid = (int) $CONFIG->site_id; + $site_guid = (int) $CONFIG->site_guid; } - if ($result = get_data("SELECT * from {$CONFIG->dbprefix}config where site_guid = {$site_guid}")) { + if ($result = get_data("SELECT * FROM {$CONFIG->dbprefix}config WHERE site_guid = $site_guid")) { foreach ($result as $r) { $name = $r->name; $value = $r->value; @@ -447,72 +543,59 @@ function get_all_config($site_guid = 0) { } /** - * Sets defaults for or attempts to autodetect some common config values and - * loads them into $CONFIG. + * Loads configuration related to this site * - * @return void + * This loads from the config database table and the site entity + * @access private */ -function set_default_config() { +function _elgg_load_site_config() { global $CONFIG; - if (empty($CONFIG->path)) { - $CONFIG->path = str_replace("\\", "/", dirname(dirname(dirname(__FILE__)))) . "/"; - } - - if (empty($CONFIG->viewpath)) { - $CONFIG->viewpath = $CONFIG->path . "views/"; - } - - if (empty($CONFIG->pluginspath)) { - $CONFIG->pluginspath = $CONFIG->path . "mod/"; - } - - if (empty($CONFIG->wwwroot)) { - /* - $CONFIG->wwwroot = "http://" . $_SERVER['SERVER_NAME']; - - $request = $_SERVER['REQUEST_URI']; - - if (strripos($request,"/") < (strlen($request) - 1)) { - // addressing a file directly, not a dir - $request = substr($request, 0, strripos($request,"/")+1); - } - - $CONFIG->wwwroot .= $request; - */ - $pathpart = str_replace("//", "/", str_replace($_SERVER['DOCUMENT_ROOT'], "", $CONFIG->path)); - if (substr($pathpart, 0, 1) != "/") { - $pathpart = "/" . $pathpart; - } - $CONFIG->wwwroot = "http://" . $_SERVER['HTTP_HOST'] . $pathpart; - } - - if (empty($CONFIG->url)) { - $CONFIG->url = $CONFIG->wwwroot; + $CONFIG->site_guid = (int) datalist_get('default_site'); + $CONFIG->site_id = $CONFIG->site_guid; + $CONFIG->site = get_entity($CONFIG->site_guid); + if (!$CONFIG->site) { + throw new InstallationException(elgg_echo('InstallationException:SiteNotInstalled')); } - if (empty($CONFIG->sitename)) { - $CONFIG->sitename = "New Elgg site"; - } + $CONFIG->wwwroot = $CONFIG->site->url; + $CONFIG->sitename = $CONFIG->site->name; + $CONFIG->sitedescription = $CONFIG->site->description; + $CONFIG->siteemail = $CONFIG->site->email; + $CONFIG->url = $CONFIG->wwwroot; - if (empty($CONFIG->language)) { - $CONFIG->language = "en"; - } + get_all_config(); + // gives hint to elgg_get_config function how to approach missing values + $CONFIG->site_config_loaded = true; } /** - * Loads values into $CONFIG. + * Loads configuration related to Elgg as an application * - * If Elgg is installed, this function pulls all rows from dbprefix_config - * and cherry picks some values from dbprefix_datalists. This also extracts - * some commonly used values from the default site object. - * - * @elgg_event boot system - * @return true|null + * This loads from the datalists database table + * @access private */ -function configuration_boot() { +function _elgg_load_application_config() { global $CONFIG; + $install_root = str_replace("\\", "/", dirname(dirname(dirname(__FILE__)))); + $defaults = array( + 'path' => "$install_root/", + 'view_path' => "$install_root/views/", + 'plugins_path' => "$install_root/mod/", + 'language' => 'en', + + // compatibility with old names for plugins not using elgg_get_config() + 'viewpath' => "$install_root/views/", + 'pluginspath' => "$install_root/mod/", + ); + + foreach ($defaults as $name => $value) { + if (empty($CONFIG->$name)) { + $CONFIG->$name = $value; + } + } + $path = datalist_get('path'); if (!empty($path)) { $CONFIG->path = $path; @@ -527,22 +610,23 @@ function configuration_boot() { } else { $CONFIG->simplecache_enabled = 1; } - $viewpath_cache_enabled = datalist_get('viewpath_cache_enabled'); - if ($viewpath_cache_enabled !== false) { - $CONFIG->viewpath_cache_enabled = $viewpath_cache_enabled; + $system_cache_enabled = datalist_get('system_cache_enabled'); + if ($system_cache_enabled !== false) { + $CONFIG->system_cache_enabled = $system_cache_enabled; } else { - $CONFIG->viewpath_cache_enabled = 1; - } - if (isset($CONFIG->site) && ($CONFIG->site instanceof ElggSite)) { - $CONFIG->wwwroot = $CONFIG->site->url; - $CONFIG->sitename = $CONFIG->site->name; - $CONFIG->sitedescription = $CONFIG->site->description; - $CONFIG->siteemail = $CONFIG->site->email; + $CONFIG->system_cache_enabled = 1; } - $CONFIG->url = $CONFIG->wwwroot; - // Load default settings from database - get_all_config(); -} + // initialize context here so it is set before the get_input call + $CONFIG->context = array(); + + // needs to be set before system, init for links in html head + $viewtype = get_input('view', 'default'); + $lastcached = datalist_get("simplecache_lastcached_$viewtype"); + $CONFIG->lastcache = $lastcached; -elgg_register_event_handler('boot', 'system', 'configuration_boot', 10);
\ No newline at end of file + $CONFIG->i18n_loaded_from_cache = false; + + // this must be synced with the enum for the entities table + $CONFIG->entity_types = array('group', 'object', 'site', 'user'); +} diff --git a/engine/lib/cron.php b/engine/lib/cron.php index ff57a41f1..4f3d05b93 100644 --- a/engine/lib/cron.php +++ b/engine/lib/cron.php @@ -10,10 +10,11 @@ * Cron initialization * * @return void + * @access private */ function cron_init() { // Register a pagehandler for cron - register_page_handler('cron', 'cron_page_handler'); + elgg_register_page_handler('cron', 'cron_page_handler'); // register a hook for Walled Garden public pages elgg_register_plugin_hook_handler('public_pages', 'walled_garden', 'cron_public_pages'); @@ -24,11 +25,11 @@ function cron_init() { * * @param array $page Pages * - * @return void + * @return bool + * @throws CronException + * @access private */ function cron_page_handler($page) { - global $CONFIG; - if (!isset($page[0])) { forward(); } @@ -48,12 +49,7 @@ function cron_page_handler($page) { $params = array(); $params['time'] = time(); - foreach ($CONFIG->input as $k => $v) { - $params[$k] = $v; - } - // Data to return to - $std_out = ""; $old_stdout = ""; ob_start(); @@ -61,6 +57,7 @@ function cron_page_handler($page) { $std_out = ob_get_clean(); echo $std_out . $old_stdout; + return true; } /** @@ -72,20 +69,21 @@ function cron_page_handler($page) { * @param mixed $params Params * * @return array + * @access private */ function cron_public_pages($hook, $type, $return_value, $params) { - $return_value[] = 'pg/cron/minute'; - $return_value[] = 'pg/cron/fiveminute'; - $return_value[] = 'pg/cron/fifteenmin'; - $return_value[] = 'pg/cron/halfhour'; - $return_value[] = 'pg/cron/hourly'; - $return_value[] = 'pg/cron/daily'; - $return_value[] = 'pg/cron/weekly'; - $return_value[] = 'pg/cron/monthly'; - $return_value[] = 'pg/cron/yearly'; - $return_value[] = 'pg/cron/reboot'; + $return_value[] = 'cron/minute'; + $return_value[] = 'cron/fiveminute'; + $return_value[] = 'cron/fifteenmin'; + $return_value[] = 'cron/halfhour'; + $return_value[] = 'cron/hourly'; + $return_value[] = 'cron/daily'; + $return_value[] = 'cron/weekly'; + $return_value[] = 'cron/monthly'; + $return_value[] = 'cron/yearly'; + $return_value[] = 'cron/reboot'; return $return_value; } -elgg_register_event_handler('init', 'system', 'cron_init');
\ No newline at end of file +elgg_register_event_handler('init', 'system', 'cron_init'); diff --git a/engine/lib/database.php b/engine/lib/database.php index da068effd..a7949788d 100644 --- a/engine/lib/database.php +++ b/engine/lib/database.php @@ -12,14 +12,19 @@ /** * Query cache for all queries. * - * Each query and its results are stored in this array as: + * Each query and its results are stored in this cache as: * <code> - * $DB_QUERY_CACHE[$query] => array(result1, result2, ... resultN) + * $DB_QUERY_CACHE[query hash] => array(result1, result2, ... resultN) * </code> + * @see elgg_query_runner() for details on the hash. * - * @global array $DB_QUERY_CACHE + * @warning Elgg used to set this as an empty array to turn off the cache + * + * @global ElggLRUCache|null $DB_QUERY_CACHE + * @access private */ -$DB_QUERY_CACHE = array(); +global $DB_QUERY_CACHE; +$DB_QUERY_CACHE = null; /** * Queries to be executed upon shutdown. @@ -37,7 +42,9 @@ $DB_QUERY_CACHE = array(); * </code> * * @global array $DB_DELAYED_QUERIES + * @access private */ +global $DB_DELAYED_QUERIES; $DB_DELAYED_QUERIES = array(); /** @@ -46,8 +53,10 @@ $DB_DELAYED_QUERIES = array(); * Each database link created with establish_db_link($name) is stored in * $dblink as $dblink[$name] => resource. Use get_db_link($name) to retrieve it. * - * @global array $dblink + * @global resource[] $dblink + * @access private */ +global $dblink; $dblink = array(); /** @@ -56,7 +65,9 @@ $dblink = array(); * Each call to the database increments this counter. * * @global integer $dbcalls + * @access private */ +global $dbcalls; $dbcalls = 0; /** @@ -68,10 +79,12 @@ $dbcalls = 0; * resource. eg "read", "write", or "readwrite". * * @return void + * @throws DatabaseException + * @access private */ function establish_db_link($dblinkname = "readwrite") { // Get configuration, and globalise database link - global $CONFIG, $dblink, $DB_QUERY_CACHE, $dbcalls; + global $CONFIG, $dblink, $DB_QUERY_CACHE; if ($dblinkname != "readwrite" && isset($CONFIG->db[$dblinkname])) { if (is_array($CONFIG->db[$dblinkname])) { @@ -115,7 +128,8 @@ function establish_db_link($dblinkname = "readwrite") { // Set up cache if global not initialized and query cache not turned off if ((!$DB_QUERY_CACHE) && (!$db_cache_off)) { - $DB_QUERY_CACHE = new ElggStaticVariableCache('db_query_cache'); + // @todo if we keep this cache in 1.9, expose the size as a config parameter + $DB_QUERY_CACHE = new ElggLRUCache(200); } } @@ -126,9 +140,10 @@ function establish_db_link($dblinkname = "readwrite") { * links up separately; otherwise just create the one database link. * * @return void + * @access private */ function setup_db_connections() { - global $CONFIG, $dblink; + global $CONFIG; if (!empty($CONFIG->db->split)) { establish_db_link('read'); @@ -142,6 +157,7 @@ function setup_db_connections() { * Display profiling information about db at NOTICE debug level upon shutdown. * * @return void + * @access private */ function db_profiling_shutdown_hook() { global $dbcalls; @@ -154,15 +170,23 @@ function db_profiling_shutdown_hook() { * Execute any delayed queries upon shutdown. * * @return void + * @access private */ function db_delayedexecution_shutdown_hook() { global $DB_DELAYED_QUERIES; foreach ($DB_DELAYED_QUERIES as $query_details) { - // use one of our db functions so it is included in profiling. - $result = execute_query($query_details['q'], $query_details['l']); - try { + $link = $query_details['l']; + + if ($link == 'read' || $link == 'write') { + $link = get_db_link($link); + } elseif (!is_resource($link)) { + elgg_log("Link for delayed query not valid resource or db_link type. Query: {$query_details['q']}", 'WARNING'); + } + + $result = execute_query($query_details['q'], $link); + if ((isset($query_details['h'])) && (is_callable($query_details['h']))) { $query_details['h']($result); } @@ -174,21 +198,6 @@ function db_delayedexecution_shutdown_hook() { } /** - * Registers shutdown functions for database profiling and delayed queries. - * - * @note Database connections are established upon first call to database. - * - * @return true - * @elgg_event_handler boot system - */ -function init_db() { - register_shutdown_function('db_delayedexecution_shutdown_hook'); - register_shutdown_function('db_profiling_shutdown_hook'); - - return true; -} - -/** * Returns (if required, also creates) a database link resource. * * Database link resources are stored in the {@link $dblink} global. These @@ -197,7 +206,8 @@ function init_db() { * * @param string $dblinktype The type of link we want: "read", "write" or "readwrite". * - * @return object Database link + * @return resource Database link + * @access private */ function get_db_link($dblinktype) { global $dblink; @@ -215,10 +225,11 @@ function get_db_link($dblinktype) { /** * Execute an EXPLAIN for $query. * - * @param str $query The query to explain + * @param string $query The query to explain * @param mixed $link The database link resource to user. * * @return mixed An object of the query's result, or FALSE + * @access private */ function explain_query($query, $link) { if ($result = execute_query("explain " . $query, $link)) { @@ -238,20 +249,26 @@ function explain_query($query, $link) { * {@link $dbcalls} is incremented and the query is saved into the {@link $DB_QUERY_CACHE}. * * @param string $query The query - * @param link $dblink The DB link + * @param resource $dblink The DB link * - * @return The result of mysql_query() + * @return resource result of mysql_query() * @throws DatabaseException + * @access private */ function execute_query($query, $dblink) { - global $CONFIG, $dbcalls, $DB_QUERY_CACHE; + global $dbcalls; + + if ($query == NULL) { + throw new DatabaseException(elgg_echo('DatabaseException:InvalidQuery')); + } + + if (!is_resource($dblink)) { + throw new DatabaseException(elgg_echo('DatabaseException:InvalidDBLink')); + } $dbcalls++; $result = mysql_query($query, $dblink); - if ($DB_QUERY_CACHE) { - $DB_QUERY_CACHE[$query] = -1; // Set initial cache to -1 - } if (mysql_errno($dblink)) { throw new DatabaseException(mysql_error($dblink) . "\n\n QUERY: " . $query); @@ -267,10 +284,11 @@ function execute_query($query, $dblink) { * the raw result from {@link mysql_query()}. * * @param string $query The query to execute - * @param resource $dblink The database link to use + * @param resource|string $dblink The database link to use or the link type (read | write) * @param string $handler A callback function to pass the results array to * * @return true + * @access private */ function execute_delayed_query($query, $dblink, $handler = "") { global $DB_DELAYED_QUERIES; @@ -279,6 +297,10 @@ function execute_delayed_query($query, $dblink, $handler = "") { $DB_DELAYED_QUERIES = array(); } + if (!is_resource($dblink) && $dblink != 'read' && $dblink != 'write') { + return false; + } + // Construct delayed query $delayed_query = array(); $delayed_query['q'] = $query; @@ -299,9 +321,10 @@ function execute_delayed_query($query, $dblink, $handler = "") { * @return true * @uses execute_delayed_query() * @uses get_db_link() + * @access private */ function execute_delayed_write_query($query, $handler = "") { - return execute_delayed_query($query, get_db_link('write'), $handler); + return execute_delayed_query($query, 'write', $handler); } /** @@ -313,9 +336,10 @@ function execute_delayed_write_query($query, $handler = "") { * @return true * @uses execute_delayed_query() * @uses get_db_link() + * @access private */ function execute_delayed_read_query($query, $handler = "") { - return execute_delayed_query($query, get_db_link('read'), $handler); + return execute_delayed_query($query, 'read', $handler); } /** @@ -327,59 +351,15 @@ function execute_delayed_read_query($query, $handler = "") { * argument to $callback. If no callback function is defined, the * entire result set is returned as an array. * - * If no results are matched, FALSE is returned. - * * @param mixed $query The query being passed. * @param string $callback Optionally, the name of a function to call back to on each row * - * @return array|false An array of database result objects or callback function results or false + * @return array An array of database result objects or callback function results. If the query + * returned nothing, an empty array. + * @access private */ function get_data($query, $callback = "") { - global $CONFIG, $DB_QUERY_CACHE; - - // Is cached? - if ($DB_QUERY_CACHE) { - $cached_query = $DB_QUERY_CACHE[$query]; - } - - if ((isset($cached_query)) && ($cached_query)) { - elgg_log("$query results returned from cache"); - - if ($cached_query === -1) { - // Last time this query returned nothing, so return an empty array - return array(); - } - - return $cached_query; - } - - $dblink = get_db_link('read'); - $resultarray = array(); - - if ($result = execute_query("$query", $dblink)) { - while ($row = mysql_fetch_object($result)) { - if (!empty($callback) && is_callable($callback)) { - $row = $callback($row); - } - if ($row) { - $resultarray[] = $row; - } - } - } - - if (empty($resultarray)) { - elgg_log("DB query \"$query\" returned no results."); - // @todo consider changing this to return empty array #1242 - return FALSE; - } - - // Cache result - if ($DB_QUERY_CACHE) { - $DB_QUERY_CACHE[$query] = $resultarray; - elgg_log("$query results cached"); - } - - return $resultarray; + return elgg_query_runner($query, $callback, false); } /** @@ -393,49 +373,77 @@ function get_data($query, $callback = "") { * @param string $callback A callback function * * @return mixed A single database result object or the result of the callback function. + * @access private */ function get_data_row($query, $callback = "") { - global $CONFIG, $DB_QUERY_CACHE; + return elgg_query_runner($query, $callback, true); +} - // Is cached - if ($DB_QUERY_CACHE) { - $cached_query = $DB_QUERY_CACHE[$query]; - } +/** + * Handles returning data from a query, running it through a callback function, + * and caching the results. This is for R queries (from CRUD). + * + * @access private + * + * @param string $query The query to execute + * @param string $callback An optional callback function to run on each row + * @param bool $single Return only a single result? + * + * @return array An array of database result objects or callback function results. If the query + * returned nothing, an empty array. + * @since 1.8.0 + * @access private + */ +function elgg_query_runner($query, $callback = null, $single = false) { + global $DB_QUERY_CACHE; - if ((isset($cached_query)) && ($cached_query)) { - elgg_log("$query results returned from cache"); + // Since we want to cache results of running the callback, we need to + // need to namespace the query with the callback and single result request. + // https://github.com/elgg/elgg/issues/4049 + $hash = (string)$callback . (int)$single . $query; - if ($cached_query === -1) { - // Last time this query returned nothing, so return false - //@todo fix me this should return array(). - return FALSE; + // Is cached? + if ($DB_QUERY_CACHE) { + if (isset($DB_QUERY_CACHE[$hash])) { + elgg_log("DB query $query results returned from cache (hash: $hash)", 'NOTICE'); + return $DB_QUERY_CACHE[$hash]; } - - return $cached_query; } $dblink = get_db_link('read'); + $return = array(); if ($result = execute_query("$query", $dblink)) { - $row = mysql_fetch_object($result); - - // Cache result (even if query returned no data) - if ($DB_QUERY_CACHE) { - $DB_QUERY_CACHE[$query] = $row; - elgg_log("$query results cached"); - } - if (!empty($callback) && is_callable($callback)) { + // test for callback once instead of on each iteration. + // @todo check profiling to see if this needs to be broken out into + // explicit cases instead of checking in the iteration. + $is_callable = is_callable($callback); + while ($row = mysql_fetch_object($result)) { + if ($is_callable) { $row = $callback($row); - } + } - if ($row) { - return $row; + if ($single) { + $return = $row; + break; + } else { + $return[] = $row; + } } } - elgg_log("$query returned no results."); - return FALSE; + if (empty($return)) { + elgg_log("DB query $query returned no results.", 'NOTICE'); + } + + // Cache result + if ($DB_QUERY_CACHE) { + $DB_QUERY_CACHE[$hash] = $return; + elgg_log("DB query $query results cached (hash: $hash)", 'NOTICE'); + } + + return $return; } /** @@ -447,18 +455,15 @@ function get_data_row($query, $callback = "") { * * @return int|false The database id of the inserted row if a AUTO_INCREMENT field is * defined, 0 if not, and false on failure. + * @access private */ function insert_data($query) { - global $CONFIG, $DB_QUERY_CACHE; + elgg_log("DB query $query", 'NOTICE'); + $dblink = get_db_link('write'); - // Invalidate query cache - if ($DB_QUERY_CACHE) { - $DB_QUERY_CACHE->clear(); - } - - elgg_log("Query cache invalidated"); + _elgg_invalidate_query_cache(); if (execute_query("$query", $dblink)) { return mysql_insert_id($dblink); @@ -468,24 +473,22 @@ function insert_data($query) { } /** - * Update a row in the database. + * Update the database. * * @note Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}. * * @param string $query The query to run. * - * @return Bool + * @return bool + * @access private */ function update_data($query) { - global $CONFIG, $DB_QUERY_CACHE; + + elgg_log("DB query $query", 'NOTICE'); $dblink = get_db_link('write'); - // Invalidate query cache - if ($DB_QUERY_CACHE) { - $DB_QUERY_CACHE->clear(); - elgg_log("Query cache invalidated"); - } + _elgg_invalidate_query_cache(); if (execute_query("$query", $dblink)) { return TRUE; @@ -495,24 +498,22 @@ function update_data($query) { } /** - * Remove a row from the database. + * Remove data from the database. * * @note Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}. * * @param string $query The SQL query to run * * @return int|false The number of affected rows or false on failure + * @access private */ function delete_data($query) { - global $CONFIG, $DB_QUERY_CACHE; + + elgg_log("DB query $query", 'NOTICE'); $dblink = get_db_link('write'); - // Invalidate query cache - if ($DB_QUERY_CACHE) { - $DB_QUERY_CACHE->clear(); - elgg_log("Query cache invalidated"); - } + _elgg_invalidate_query_cache(); if (execute_query("$query", $dblink)) { return mysql_affected_rows($dblink); @@ -521,6 +522,22 @@ function delete_data($query) { return FALSE; } +/** + * Invalidate the query cache + * + * @access private + */ +function _elgg_invalidate_query_cache() { + global $DB_QUERY_CACHE; + if ($DB_QUERY_CACHE instanceof ElggLRUCache) { + $DB_QUERY_CACHE->clear(); + elgg_log("Query cache invalidated", 'NOTICE'); + } elseif ($DB_QUERY_CACHE) { + // In case someone sets the cache to an array and primes it with data + $DB_QUERY_CACHE = array(); + elgg_log("Query cache invalidated", 'NOTICE'); + } +} /** * Return tables matching the database prefix {@link $CONFIG->dbprefix}% in the currently @@ -528,6 +545,7 @@ function delete_data($query) { * * @return array|false List of tables or false on failure * @static array $tables Tables found matching the database prefix + * @access private */ function get_db_tables() { global $CONFIG; @@ -570,6 +588,7 @@ function get_db_tables() { * @param string $table The name of the table to optimise * * @return bool + * @access private */ function optimize_table($table) { $table = sanitise_string($table); @@ -582,6 +601,7 @@ function optimize_table($table) { * @param resource $dblink The DB link * * @return string Database error message + * @access private */ function get_db_error($dblink) { return mysql_error($dblink); @@ -606,6 +626,7 @@ function get_db_error($dblink) { * * @return void * @throws DatabaseException + * @access private */ function run_sql_script($scriptlocation) { if ($script = file_get_contents($scriptlocation)) { @@ -624,7 +645,7 @@ function run_sql_script($scriptlocation) { $statement = str_replace("prefix_", $CONFIG->dbprefix, $statement); if (!empty($statement)) { try { - $result = update_data($statement); + update_data($statement); } catch (DatabaseException $e) { $errors[] = $e->getMessage(); } @@ -646,67 +667,15 @@ function run_sql_script($scriptlocation) { } /** - * Upgrade the database schema in an ordered sequence. - * - * Executes all upgrade files in elgg/engine/schema/upgrades/ in sequential order. - * Upgrade files must be in the standard Elgg release format of YYYYMMDDII.sql - * where II is an incrementor starting from 01. - * - * Files that are < $version will be ignored. - * - * @warning Plugin authors should not call this function directly. - * - * @param int $version The version you are upgrading from in the format YYYYMMDDII. - * @param string $fromdir Optional directory to load upgrades from. default: engine/schema/upgrades/ - * @param bool $quiet If true, suppress all error messages. Only use for the upgrade from <=1.6. + * Format a query string for logging * - * @return bool - * @see upgrade.php - * @see version.php + * @param string $query Query string + * @return string + * @access private */ -function db_upgrade($version, $fromdir = "", $quiet = FALSE) { - global $CONFIG; - - $version = (int) $version; - - if (!$fromdir) { - $fromdir = $CONFIG->path . 'engine/schema/upgrades/'; - } - - if ($handle = opendir($fromdir)) { - $sqlupgrades = array(); - - while ($sqlfile = readdir($handle)) { - if (!is_dir($fromdir . $sqlfile)) { - if (preg_match('/^([0-9]{10})\.(sql)$/', $sqlfile, $matches)) { - $sql_version = (int) $matches[1]; - if ($sql_version > $version) { - $sqlupgrades[] = $sqlfile; - } - } - } - } - - asort($sqlupgrades); - - if (sizeof($sqlupgrades) > 0) { - foreach ($sqlupgrades as $sqlfile) { - - // hide all errors. - if ($quiet) { - try { - run_sql_script($fromdir . $sqlfile); - } catch (DatabaseException $e) { - error_log($e->getmessage()); - } - } else { - run_sql_script($fromdir . $sqlfile); - } - } - } - } - - return TRUE; +function elgg_format_query($query) { + // remove newlines and extra spaces so logs are easier to read + return preg_replace('/\s\s+/', ' ', $query); } /** @@ -754,26 +723,42 @@ function sanitize_string($string) { /** * Sanitises an integer for database use. * - * @param int $int Integer - * - * @return int Sanitised integer + * @param int $int Value to be sanitized + * @param bool $signed Whether negative values should be allowed (true) + * @return int */ -function sanitise_int($int) { +function sanitise_int($int, $signed = true) { + $int = (int) $int; + + if ($signed === false) { + if ($int < 0) { + $int = 0; + } + } + return (int) $int; } /** - * Wrapper function for alternate English spelling - * - * @param int $int Integer + * Sanitizes an integer for database use. + * Wrapper function for alternate English spelling (@see sanitise_int) * - * @return int Sanitised integer + * @param int $int Value to be sanitized + * @param bool $signed Whether negative values should be allowed (true) + * @return int */ -function sanitize_int($int) { - return (int) $int; +function sanitize_int($int, $signed = true) { + return sanitise_int($int, $signed); } /** - * @elgg_register_event boot system init_db + * Registers shutdown functions for database profiling and delayed queries. + * + * @access private */ -elgg_register_event_handler('boot', 'system', 'init_db', 0);
\ No newline at end of file +function init_db() { + register_shutdown_function('db_delayedexecution_shutdown_hook'); + register_shutdown_function('db_profiling_shutdown_hook'); +} + +elgg_register_event_handler('init', 'system', 'init_db'); diff --git a/engine/lib/deprecated-1.7.php b/engine/lib/deprecated-1.7.php new file mode 100644 index 000000000..ee95b5611 --- /dev/null +++ b/engine/lib/deprecated-1.7.php @@ -0,0 +1,1164 @@ +<?php +/** + * Get entities with the specified access collection id. + * + * @deprecated 1.7. Use elgg_get_entities_from_access_id() + * + * @param int $collection_id ID of collection + * @param string $entity_type Type of entities + * @param string $entity_subtype Subtype of entities + * @param int $owner_guid Guid of owner + * @param int $limit Limit of number of entities to return + * @param int $offset Skip this many entities + * @param string $order_by Column to order by + * @param int $site_guid The site guid + * @param bool $count Return a count or entities + * + * @return array + */ +function get_entities_from_access_id($collection_id, $entity_type = "", $entity_subtype = "", + $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) { + // log deprecated warning + elgg_deprecated_notice('get_entities_from_access_id() was deprecated by elgg_get_entities()', 1.7); + + if (!$collection_id) { + return FALSE; + } + + // build the options using given parameters + $options = array(); + $options['limit'] = $limit; + $options['offset'] = $offset; + $options['count'] = $count; + + if ($entity_type) { + $options['type'] = sanitise_string($entity_type); + } + + if ($entity_subtype) { + $options['subtype'] = $entity_subtype; + } + + if ($site_guid) { + $options['site_guid'] = $site_guid; + } + + if ($order_by) { + $options['order_by'] = sanitise_string("e.time_created, $order_by"); + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + if ($site_guid) { + $options['site_guid'] = $site_guid; + } + + $options['access_id'] = $collection_id; + + return elgg_get_entities_from_access_id($options); +} + +/** + * @deprecated 1.7 + */ +function get_entities_from_access_collection($collection_id, $entity_type = "", $entity_subtype = "", + $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) { + + elgg_deprecated_notice('get_entities_from_access_collection() was deprecated by elgg_get_entities()', 1.7); + + return get_entities_from_access_id($collection_id, $entity_type, $entity_subtype, + $owner_guid, $limit, $offset, $order_by, $site_guid, $count); +} + +/** + * Get entities from annotations + * + * No longer used. + * + * @deprecated 1.7 Use elgg_get_entities_from_annotations() + * + * @param mixed $entity_type Type of entity + * @param mixed $entity_subtype Subtype of entity + * @param string $name Name of annotation + * @param string $value Value of annotation + * @param int $owner_guid Guid of owner of annotation + * @param int $group_guid Guid of group + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by SQL order by string + * @param bool $count Count or return entities + * @param int $timelower Lower time limit + * @param int $timeupper Upper time limit + * + * @return unknown_type + */ +function get_entities_from_annotations($entity_type = "", $entity_subtype = "", $name = "", +$value = "", $owner_guid = 0, $group_guid = 0, $limit = 10, $offset = 0, $order_by = "asc", +$count = false, $timelower = 0, $timeupper = 0) { + $msg = 'get_entities_from_annotations() is deprecated by elgg_get_entities_from_annotations().'; + elgg_deprecated_notice($msg, 1.7); + + $options = array(); + + if ($entity_type) { + $options['types'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtypes'] = $entity_subtype; + } + + $options['annotation_names'] = $name; + + if ($value) { + $options['annotation_values'] = $value; + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['annotation_owner_guids'] = $owner_guid; + } else { + $options['annotation_owner_guid'] = $owner_guid; + } + } + + if ($group_guid) { + $options['container_guid'] = $group_guid; + } + + if ($limit) { + $options['limit'] = $limit; + } + + if ($offset) { + $options['offset'] = $offset; + } + + if ($order_by) { + $options['order_by'] = "maxtime $order_by"; + } + + if ($count) { + $options['count'] = $count; + } + + if ($timelower) { + $options['annotation_created_time_lower'] = $timelower; + } + + if ($timeupper) { + $options['annotation_created_time_upper'] = $timeupper; + } + + return elgg_get_entities_from_annotations($options); +} + +/** + * Lists entities + * + * @see elgg_view_entity_list + * + * @param string $entity_type Type of entity. + * @param string $entity_subtype Subtype of entity. + * @param string $name Name of annotation. + * @param string $value Value of annotation. + * @param int $limit Maximum number of results to return. + * @param int $owner_guid Owner. + * @param int $group_guid Group container. Currently only supported if entity_type is object + * @param boolean $asc Whether to list in ascending or descending order (default: desc) + * @param boolean $fullview Whether to display the entities in full + * @param boolean $listtypetoggle Can 'gallery' view can be displayed (default: no) + * + * @deprecated 1.7 Use elgg_list_entities_from_annotations() + * @return string Formatted entity list + */ +function list_entities_from_annotations($entity_type = "", $entity_subtype = "", $name = "", +$value = "", $limit = 10, $owner_guid = 0, $group_guid = 0, $asc = false, $fullview = true, +$listtypetoggle = false) { + + $msg = 'list_entities_from_annotations is deprecated by elgg_list_entities_from_annotations'; + elgg_deprecated_notice($msg, 1.8); + + $options = array(); + + if ($entity_type) { + $options['types'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtypes'] = $entity_subtype; + } + + if ($name) { + $options['annotation_names'] = $name; + } + + if ($value) { + $options['annotation_values'] = $value; + } + + if ($limit) { + $options['limit'] = $limit; + } + + if ($owner_guid) { + $options['annotation_owner_guid'] = $owner_guid; + } + + if ($group_guid) { + $options['container_guid'] = $group_guid; + } + + if ($asc) { + $options['order_by'] = 'maxtime desc'; + } + + if ($offset = sanitise_int(get_input('offset', null))) { + $options['offset'] = $offset; + } + + $options['full_view'] = $fullview; + $options['list_type_toggle'] = $listtypetoggle; + $options['pagination'] = $pagination; + + return elgg_list_entities_from_annotations($options); +} + +/** + * Returns all php files in a directory. + * + * @deprecated 1.7 Use elgg_get_file_list() instead + * + * @param string $directory Directory to look in + * @param array $exceptions Array of extensions (with .!) to ignore + * @param array $list A list files to include in the return + * + * @return array + */ +function get_library_files($directory, $exceptions = array(), $list = array()) { + elgg_deprecated_notice('get_library_files() deprecated by elgg_get_file_list()', 1.7); + return elgg_get_file_list($directory, $exceptions, $list, array('.php')); +} + +/** + * Add action tokens to URL. + * + * @param string $url URL + * + * @return string + * + * @deprecated 1.7 final + */ +function elgg_validate_action_url($url) { + elgg_deprecated_notice('elgg_validate_action_url() deprecated by elgg_add_action_tokens_to_url().', + 1.7); + + return elgg_add_action_tokens_to_url($url); +} + +/** + * Does nothing. + * + * @deprecated 1.7 + * @return 0 + */ +function test_ip() { + elgg_deprecated_notice('test_ip() was removed because of licensing issues.', 1.7); + + return 0; +} + +/** + * Does nothing. + * + * @return bool + * @deprecated 1.7 + */ +function is_ip_in_array() { + elgg_deprecated_notice('is_ip_in_array() was removed because of licensing issues.', 1.7); + + return false; +} + +/** + * Returns entities. + * + * @deprecated 1.7. Use elgg_get_entities(). + * + * @param string $type Entity type + * @param string $subtype Entity subtype + * @param int $owner_guid Owner GUID + * @param string $order_by Order by clause + * @param int $limit Limit + * @param int $offset Offset + * @param bool $count Return a count or an array of entities + * @param int $site_guid Site GUID + * @param int $container_guid Container GUID + * @param int $timelower Lower time limit + * @param int $timeupper Upper time limit + * + * @return array + */ +function get_entities($type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, +$offset = 0, $count = false, $site_guid = 0, $container_guid = null, $timelower = 0, +$timeupper = 0) { + + elgg_deprecated_notice('get_entities() was deprecated by elgg_get_entities().', 1.7); + + // rewrite owner_guid to container_guid to emulate old functionality + if ($owner_guid != "") { + if (is_null($container_guid)) { + $container_guid = $owner_guid; + $owner_guid = NULL; + } + } + + $options = array(); + if ($type) { + if (is_array($type)) { + $options['types'] = $type; + } else { + $options['type'] = $type; + } + } + + if ($subtype) { + if (is_array($subtype)) { + $options['subtypes'] = $subtype; + } else { + $options['subtype'] = $subtype; + } + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + if ($order_by) { + $options['order_by'] = $order_by; + } + + // need to pass 0 for all option + $options['limit'] = $limit; + + if ($offset) { + $options['offset'] = $offset; + } + + if ($count) { + $options['count'] = $count; + } + + if ($site_guid) { + $options['site_guids'] = $site_guid; + } + + if ($container_guid) { + $options['container_guids'] = $container_guid; + } + + if ($timeupper) { + $options['created_time_upper'] = $timeupper; + } + + if ($timelower) { + $options['created_time_lower'] = $timelower; + } + + $r = elgg_get_entities($options); + return $r; +} + +/** + * Delete multiple entities that match a given query. + * This function iterates through and calls delete_entity on + * each one, this is somewhat inefficient but lets + * the 'delete' event be called for each entity. + * + * @deprecated 1.7. This is a dangerous function as it defaults to deleting everything. + * + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * + * @return false + */ +function delete_entities($type = "", $subtype = "", $owner_guid = 0) { + elgg_deprecated_notice('delete_entities() was deprecated because no one should use it.', 1.7); + return false; +} + +/** + * Lists entities. + * + * @param int $owner_guid Owner GUID + * @param int $limit Limit + * @param bool $fullview Show entity full views + * @param bool $listtypetoggle Show list type toggle + * @param bool $allowedtypes A string of the allowed types + * + * @return string + * @deprecated 1.7. Use elgg_list_registered_entities(). + */ +function list_registered_entities($owner_guid = 0, $limit = 10, $fullview = true, +$listtypetoggle = false, $allowedtypes = true) { + + elgg_deprecated_notice('list_registered_entities() was deprecated by elgg_list_registered_entities().', 1.7); + + $options = array(); + + // don't want to send anything if not being used. + if ($owner_guid) { + $options['owner_guid'] = $owner_guid; + } + + if ($limit) { + $options['limit'] = $limit; + } + + if ($allowedtypes) { + $options['allowed_types'] = $allowedtypes; + } + + // need to send because might be BOOL + $options['full_view'] = $fullview; + $options['list_type_toggle'] = $listtypetoggle; + + $options['offset'] = get_input('offset', 0); + + return elgg_list_registered_entities($options); +} + +/** + * Lists entities + * + * @deprecated 1.7. Use elgg_list_entities(). + * + * @param string $type Entity type + * @param string $subtype Entity subtype + * @param int $owner_guid Owner GUID + * @param int $limit Limit + * @param bool $fullview Display entity full views? + * @param bool $listtypetoggle Allow switching to gallery mode? + * @param bool $pagination Show pagination? + * + * @return string + */ +function list_entities($type= "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, +$listtypetoggle = false, $pagination = true) { + + elgg_deprecated_notice('list_entities() was deprecated by elgg_list_entities()!', 1.7); + + $options = array(); + + // rewrite owner_guid to container_guid to emulate old functionality + if ($owner_guid) { + $options['container_guids'] = $owner_guid; + } + + if ($type) { + $options['types'] = $type; + } + + if ($subtype) { + $options['subtypes'] = $subtype; + } + + if ($limit) { + $options['limit'] = $limit; + } + + if ($offset = sanitise_int(get_input('offset', null))) { + $options['offset'] = $offset; + } + + $options['full_view'] = $fullview; + $options['list_type_toggle'] = $listtypetoggle; + $options['pagination'] = $pagination; + + return elgg_list_entities($options); +} + +/** + * Searches for a group based on a complete or partial name or description + * + * @param string $criteria The partial or full name or description + * @param int $limit Limit of the search. + * @param int $offset Offset. + * @param string $order_by The order. + * @param boolean $count Whether to return the count of results or just the results. + * + * @return mixed + * @deprecated 1.7 + */ +function search_for_group($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) { + elgg_deprecated_notice('search_for_group() was deprecated by new search plugin.', 1.7); + global $CONFIG; + + $criteria = sanitise_string($criteria); + $limit = (int)$limit; + $offset = (int)$offset; + $order_by = sanitise_string($order_by); + + $access = get_access_sql_suffix("e"); + + if ($order_by == "") { + $order_by = "e.time_created desc"; + } + + if ($count) { + $query = "SELECT count(e.guid) as total "; + } else { + $query = "SELECT e.* "; + } + $query .= "from {$CONFIG->dbprefix}entities e" + . " JOIN {$CONFIG->dbprefix}groups_entity g on e.guid=g.guid where "; + + $query .= "(g.name like \"%{$criteria}%\" or g.description like \"%{$criteria}%\")"; + $query .= " and $access"; + + if (!$count) { + $query .= " order by $order_by limit $offset, $limit"; // Add order and limit + return get_data($query, "entity_row_to_elggstar"); + } else { + if ($count = get_data_row($query)) { + return $count->total; + } + } + return false; +} + +/** + * Returns a formatted list of groups suitable for injecting into search. + * + * @deprecated 1.7 + * + * @param string $hook Hook name + * @param string $user User + * @param mixed $returnvalue Previous hook's return value + * @param string $tag Tag to search on + * + * @return string + */ +function search_list_groups_by_name($hook, $user, $returnvalue, $tag) { + elgg_deprecated_notice('search_list_groups_by_name() was deprecated by new search plugin', 1.7); + // Change this to set the number of groups that display on the search page + $threshold = 4; + + $object = get_input('object'); + + if (!get_input('offset') && (empty($object) || $object == 'group')) { + if ($groups = search_for_group($tag, $threshold)) { + $countgroups = search_for_group($tag, 0, 0, "", true); + + $return = elgg_view('group/search/startblurb', array('count' => $countgroups, 'tag' => $tag)); + foreach ($groups as $group) { + $return .= elgg_view_entity($group); + } + $vars = array('count' => $countgroups, 'threshold' => $threshold, 'tag' => $tag); + $return .= elgg_view('group/search/finishblurb', $vars); + return $return; + } + } +} + +/** + * Displays a list of group objects that have been searched for. + * + * @see elgg_view_entity_list + * + * @param string $tag Search criteria + * @param int $limit The number of entities to display on a page + * + * @return string The list in a form suitable to display + * @deprecated 1.7 + */ +function list_group_search($tag, $limit = 10) { + elgg_deprecated_notice('list_group_search() was deprecated by new search plugin.', 1.7); + $offset = (int) get_input('offset'); + $limit = (int) $limit; + $count = (int) search_for_group($tag, 10, 0, '', true); + $entities = search_for_group($tag, $limit, $offset); + + return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, false); + +} + +/** + * Return a list of entities based on the given search criteria. + * + * @deprecated 1.7 use elgg_get_entities_from_metadata(). + * + * @param mixed $meta_name Metadat name + * @param mixed $meta_value Metadata value + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity. + * @param int $owner_guid Owner GUID + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by Optional ordering. + * @param int $site_guid Site GUID. 0 for current, -1 for any. + * @param bool $count Return a count instead of entities + * @param bool $case_sensitive Metadata names case sensitivity + * + * @return int|array A list of entities, or a count if $count is set to true + */ +function get_entities_from_metadata($meta_name, $meta_value = "", $entity_type = "", +$entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", +$site_guid = 0, $count = FALSE, $case_sensitive = TRUE) { + + elgg_deprecated_notice('get_entities_from_metadata() was deprecated by elgg_get_entities_from_metadata()!', 1.7); + + $options = array(); + + $options['metadata_names'] = $meta_name; + + if ($meta_value) { + $options['metadata_values'] = $meta_value; + } + + if ($entity_type) { + $options['types'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtypes'] = $entity_subtype; + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + if ($limit) { + $options['limit'] = $limit; + } + + if ($offset) { + $options['offset'] = $offset; + } + + if ($order_by) { + $options['order_by']; + } + + if ($site_guid) { + $options['site_guid']; + } + + if ($count) { + $options['count'] = $count; + } + + // need to be able to pass false + $options['metadata_case_sensitive'] = $case_sensitive; + + return elgg_get_entities_from_metadata($options); +} + +/** + * Return entities from metadata + * + * @deprecated 1.7. Use elgg_get_entities_from_metadata(). + * + * @param mixed $meta_array Metadata name + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity. + * @param int $owner_guid Owner GUID + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by Optional ordering. + * @param int $site_guid Site GUID. 0 for current, -1 for any. + * @param bool $count Return a count instead of entities + * @param bool $meta_array_operator Operator for metadata values + * + * @return int|array A list of entities, or a count if $count is set to true + */ +function get_entities_from_metadata_multi($meta_array, $entity_type = "", $entity_subtype = "", +$owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, +$count = false, $meta_array_operator = 'and') { + + elgg_deprecated_notice('get_entities_from_metadata_multi() was deprecated by elgg_get_entities_from_metadata()!', 1.7); + + if (!is_array($meta_array) || sizeof($meta_array) == 0) { + return false; + } + + $options = array(); + + $options['metadata_name_value_pairs'] = $meta_array; + + if ($entity_type) { + $options['types'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtypes'] = $entity_subtype; + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + if ($limit) { + $options['limit'] = $limit; + } + + if ($offset) { + $options['offset'] = $offset; + } + + if ($order_by) { + $options['order_by']; + } + + if ($site_guid) { + $options['site_guid']; + } + + if ($count) { + $options['count'] = $count; + } + + $options['metadata_name_value_pairs_operator'] = $meta_array_operator; + + return elgg_get_entities_from_metadata($options); +} + +/** + * Returns a menu item for use in the children section of add_menu() + * This is not currently used in the Elgg core. + * + * @param string $menu_name The name of the menu item + * @param string $menu_url Its URL + * + * @return stdClass|false Depending on success + * @deprecated 1.7 + */ +function menu_item($menu_name, $menu_url) { + elgg_deprecated_notice('menu_item() is deprecated by add_submenu_item', 1.7); + return make_register_object($menu_name, $menu_url); +} + +/** + * Searches for an object based on a complete or partial title + * or description using full text searching. + * + * IMPORTANT NOTE: With MySQL's default setup: + * 1) $criteria must be 4 or more characters long + * 2) If $criteria matches greater than 50% of results NO RESULTS ARE RETURNED! + * + * @param string $criteria The partial or full name or username. + * @param int $limit Limit of the search. + * @param int $offset Offset. + * @param string $order_by The order. + * @param boolean $count Whether to return the count of results or just the results. + * + * @return int|false + * @deprecated 1.7 + */ +function search_for_object($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) { + elgg_deprecated_notice('search_for_object() was deprecated by new search plugin.', 1.7); + global $CONFIG; + + $criteria = sanitise_string($criteria); + $limit = (int)$limit; + $offset = (int)$offset; + $order_by = sanitise_string($order_by); + $container_guid = (int)$container_guid; + + $access = get_access_sql_suffix("e"); + + if ($order_by == "") { + $order_by = "e.time_created desc"; + } + + if ($count) { + $query = "SELECT count(e.guid) as total "; + } else { + $query = "SELECT e.* "; + } + $query .= "from {$CONFIG->dbprefix}entities e + join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid + where match(o.title,o.description) against ('$criteria') and $access"; + + if (!$count) { + $query .= " order by $order_by limit $offset, $limit"; // Add order and limit + return get_data($query, "entity_row_to_elggstar"); + } else { + if ($count = get_data_row($query)) { + return $count->total; + } + } + return false; +} + +/** + * Returns a formatted list of objects suitable for injecting into search. + * + * @deprecated 1.7 + * + * @param sting $hook Hook + * @param string $user user + * @param mixed $returnvalue Previous return value + * @param mixed $tag Search term + * + * @return array + */ +function search_list_objects_by_name($hook, $user, $returnvalue, $tag) { + elgg_deprecated_notice('search_list_objects_by_name was deprecated by new search plugin.', 1.7); + + // Change this to set the number of users that display on the search page + $threshold = 4; + + $object = get_input('object'); + + if (!get_input('offset') && (empty($object) || $object == 'user')) { + if ($users = search_for_user($tag, $threshold)) { + $countusers = search_for_user($tag, 0, 0, "", true); + + $return = elgg_view('user/search/startblurb', array('count' => $countusers, 'tag' => $tag)); + foreach ($users as $user) { + $return .= elgg_view_entity($user); + } + $return .= elgg_view('user/search/finishblurb', + array('count' => $countusers, 'threshold' => $threshold, 'tag' => $tag)); + + return $return; + + } + } +} + +/** + * Return entities from relationships + * + * @deprecated 1.7 Use elgg_get_entities_from_relationship() + * + * @param string $relationship The relationship type + * @param int $relationship_guid The GUID of the relationship owner + * @param bool $inverse_relationship Invert relationship? + * @param string $type Entity type + * @param string $subtype Entity subtype + * @param int $owner_guid Entity owner GUID + * @param string $order_by Order by clause + * @param int $limit Limit + * @param int $offset Offset + * @param bool $count Return a count instead of entities? + * @param int $site_guid Site GUID + * + * @return mixed + */ +function get_entities_from_relationship($relationship, $relationship_guid, +$inverse_relationship = false, $type = "", $subtype = "", $owner_guid = 0, +$order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0) { + + elgg_deprecated_notice('get_entities_from_relationship() was deprecated by elgg_get_entities_from_relationship()!', 1.7); + + $options = array(); + + $options['relationship'] = $relationship; + $options['relationship_guid'] = $relationship_guid; + $options['inverse_relationship'] = $inverse_relationship; + + if ($type) { + $options['types'] = $type; + } + + if ($subtype) { + $options['subtypes'] = $subtype; + } + + if ($owner_guid) { + $options['owner_guid'] = $owner_guid; + } + + $options['limit'] = $limit; + + if ($offset) { + $options['offset'] = $offset; + } + + if ($order_by) { + $options['order_by']; + } + + if ($site_guid) { + $options['site_guid']; + } + + if ($count) { + $options['count'] = $count; + } + + return elgg_get_entities_from_relationship($options); +} + +/** + * Searches for a site based on a complete or partial name + * or description or url using full text searching. + * + * IMPORTANT NOTE: With MySQL's default setup: + * 1) $criteria must be 4 or more characters long + * 2) If $criteria matches greater than 50% of results NO RESULTS ARE RETURNED! + * + * @param string $criteria The partial or full name or username. + * @param int $limit Limit of the search. + * @param int $offset Offset. + * @param string $order_by The order. + * @param boolean $count Whether to return the count of results or just the results. + * + * @return mixed + * @deprecated 1.7 + */ +function search_for_site($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) { + elgg_deprecated_notice('search_for_site() was deprecated by new search plugin.', 1.7); + global $CONFIG; + + $criteria = sanitise_string($criteria); + $limit = (int)$limit; + $offset = (int)$offset; + $order_by = sanitise_string($order_by); + + $access = get_access_sql_suffix("e"); + + if ($order_by == "") { + $order_by = "e.time_created desc"; + } + + if ($count) { + $query = "SELECT count(e.guid) as total "; + } else { + $query = "SELECT e.* "; + } + $query .= "from {$CONFIG->dbprefix}entities e + join {$CONFIG->dbprefix}sites_entity s on e.guid=s.guid + where match(s.name, s.description, s.url) against ('$criteria') and $access"; + + if (!$count) { + $query .= " order by $order_by limit $offset, $limit"; // Add order and limit + return get_data($query, "entity_row_to_elggstar"); + } else { + if ($count = get_data_row($query)) { + return $count->total; + } + } + return false; +} + +/** + * Searches for a user based on a complete or partial name or username. + * + * @param string $criteria The partial or full name or username. + * @param int $limit Limit of the search. + * @param int $offset Offset. + * @param string $order_by The order. + * @param boolean $count Whether to return the count of results or just the results. + * + * @return mixed + * @deprecated 1.7 + */ +function search_for_user($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) { + elgg_deprecated_notice('search_for_user() was deprecated by new search.', 1.7); + global $CONFIG; + + $criteria = sanitise_string($criteria); + $limit = (int)$limit; + $offset = (int)$offset; + $order_by = sanitise_string($order_by); + + $access = get_access_sql_suffix("e"); + + if ($order_by == "") { + $order_by = "e.time_created desc"; + } + + if ($count) { + $query = "SELECT count(e.guid) as total "; + } else { + $query = "SELECT e.* "; + } + $query .= "from {$CONFIG->dbprefix}entities e + join {$CONFIG->dbprefix}users_entity u on e.guid=u.guid where "; + + $query .= "(u.name like \"%{$criteria}%\" or u.username like \"%{$criteria}%\")"; + $query .= " and $access"; + + if (!$count) { + $query .= " order by $order_by limit $offset, $limit"; + return get_data($query, "entity_row_to_elggstar"); + } else { + if ($count = get_data_row($query)) { + return $count->total; + } + } + return false; +} + +/** + * Displays a list of user objects that have been searched for. + * + * @see elgg_view_entity_list + * + * @param string $tag Search criteria + * @param int $limit The number of entities to display on a page + * + * @return string The list in a form suitable to display + * + * @deprecated 1.7 + */ +function list_user_search($tag, $limit = 10) { + elgg_deprecated_notice('list_user_search() deprecated by new search', 1.7); + $offset = (int) get_input('offset'); + $limit = (int) $limit; + $count = (int) search_for_user($tag, 10, 0, '', true); + $entities = search_for_user($tag, $limit, $offset); + + return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, false); +} + +/** + * Returns a formatted list of users suitable for injecting into search. + * + * @deprecated 1.7 + * + * @param string $hook Hook name + * @param string $user User? + * @param mixed $returnvalue Previous hook's return value + * @param mixed $tag Tag to search against + * + * @return void + */ +function search_list_users_by_name($hook, $user, $returnvalue, $tag) { + elgg_deprecated_notice('search_list_users_by_name() was deprecated by new search', 1.7); + // Change this to set the number of users that display on the search page + $threshold = 4; + + $object = get_input('object'); + + if (!get_input('offset') && (empty($object) || $object == 'user')) { + if ($users = search_for_user($tag, $threshold)) { + $countusers = search_for_user($tag, 0, 0, "", true); + + $return = elgg_view('user/search/startblurb', array('count' => $countusers, 'tag' => $tag)); + foreach ($users as $user) { + $return .= elgg_view_entity($user); + } + + $vars = array('count' => $countusers, 'threshold' => $threshold, 'tag' => $tag); + $return .= elgg_view('user/search/finishblurb', $vars); + return $return; + + } + } +} + +/** + * Extend a view + * + * @deprecated 1.7. Use elgg_extend_view(). + * + * @param string $view The view to extend. + * @param string $view_name This view is added to $view + * @param int $priority The priority, from 0 to 1000, + * to add at (lowest numbers displayed first) + * @param string $viewtype Not used + * + * @return void + */ +function extend_view($view, $view_name, $priority = 501, $viewtype = '') { + elgg_deprecated_notice('extend_view() was deprecated by elgg_extend_view()!', 1.7); + elgg_extend_view($view, $view_name, $priority, $viewtype); +} + +/** + * Get views in a dir + * + * @deprecated 1.7. Use elgg_get_views(). + * + * @param string $dir Dir + * @param string $base Base view + * + * @return array + */ +function get_views($dir, $base) { + elgg_deprecated_notice('get_views() was deprecated by elgg_get_views()!', 1.7); + elgg_get_views($dir, $base); +} + +/** + * Constructs and returns a register object. + * + * @param string $register_name The name of the register + * @param mixed $register_value The value of the register + * @param array $children_array Optionally, an array of children + * + * @return false|stdClass Depending on success + * @deprecated 1.7 Use {@link add_submenu_item()} + */ +function make_register_object($register_name, $register_value, $children_array = array()) { + elgg_deprecated_notice('make_register_object() is deprecated by add_submenu_item()', 1.7); + if (empty($register_name) || empty($register_value)) { + return false; + } + + $register = new stdClass; + $register->name = $register_name; + $register->value = $register_value; + $register->children = $children_array; + + return $register; +} + +/** + * THIS FUNCTION IS DEPRECATED. + * + * Delete a object's extra data. + * + * @todo - this should be removed - was deprecated in 1.5 or earlier + * + * @param int $guid GUID + * + * @return 1 + * @deprecated 1.7 + */ +function delete_object_entity($guid) { + system_message(elgg_echo('deprecatedfunction', array('delete_user_entity'))); + + return 1; // Always return that we have deleted one row in order to not break existing code. +} + +/** + * THIS FUNCTION IS DEPRECATED. + * + * Delete a user's extra data. + * + * @todo remove + * + * @param int $guid User GUID + * + * @return 1 + * @deprecated 1.7 + */ +function delete_user_entity($guid) { + system_message(elgg_echo('deprecatedfunction', array('delete_user_entity'))); + + return 1; // Always return that we have deleted one row in order to not break existing code. +}
\ No newline at end of file diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php new file mode 100644 index 000000000..91068d047 --- /dev/null +++ b/engine/lib/deprecated-1.8.php @@ -0,0 +1,4820 @@ +<?php +/** + * *************************************************************************** + * NOTE: If this is ever removed from Elgg, sites lose the ability to upgrade + * from 1.7.x and earlier to the latest version of Elgg without upgrading to + * 1.8 first. + * *************************************************************************** + * + * Upgrade the database schema in an ordered sequence. + * + * Executes all upgrade files in elgg/engine/schema/upgrades/ in sequential order. + * Upgrade files must be in the standard Elgg release format of YYYYMMDDII.sql + * where II is an incrementor starting from 01. + * + * Files that are < $version will be ignored. + * + * @warning Plugin authors should not call this function directly. + * + * @param int $version The version you are upgrading from in the format YYYYMMDDII. + * @param string $fromdir Optional directory to load upgrades from. default: engine/schema/upgrades/ + * @param bool $quiet If true, suppress all error messages. Only use for the upgrade from <=1.6. + * + * @return int The number of upgrades run. + * @see upgrade.php + * @see version.php + * @deprecated 1.8 Use PHP upgrades for sql changes. + */ +function db_upgrade($version, $fromdir = "", $quiet = FALSE) { + global $CONFIG; + + elgg_deprecated_notice('db_upgrade() is deprecated by using PHP upgrades.', 1.8); + + $version = (int) $version; + + if (!$fromdir) { + $fromdir = $CONFIG->path . 'engine/schema/upgrades/'; + } + + $i = 0; + + if ($handle = opendir($fromdir)) { + $sqlupgrades = array(); + + while ($sqlfile = readdir($handle)) { + if (!is_dir($fromdir . $sqlfile)) { + if (preg_match('/^([0-9]{10})\.(sql)$/', $sqlfile, $matches)) { + $sql_version = (int) $matches[1]; + if ($sql_version > $version) { + $sqlupgrades[] = $sqlfile; + } + } + } + } + + asort($sqlupgrades); + + if (sizeof($sqlupgrades) > 0) { + foreach ($sqlupgrades as $sqlfile) { + + // hide all errors. + if ($quiet) { + try { + run_sql_script($fromdir . $sqlfile); + } catch (DatabaseException $e) { + error_log($e->getmessage()); + } + } else { + run_sql_script($fromdir . $sqlfile); + } + $i++; + } + } + } + + return $i; +} + +/** + * Lists entities from an access collection + * + * @deprecated 1.8 Use elgg_list_entities_from_access_id() + * + * @return str + */ +function list_entities_from_access_id($access_id, $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true) { + + elgg_deprecated_notice("All list_entities* functions were deprecated in 1.8. Use elgg_list_entities* instead.", 1.8); + + echo elgg_list_entities_from_access_id(array('access_id' => $access_id, + 'type' => $entity_type, 'subtype' => $entity_subtype, 'owner_guids' => $owner_guid, + 'limit' => $limit, 'full_view' => $fullview, 'list_type_toggle' => $listtypetoggle, + 'pagination' => $pagination,)); +} + +/** + * Registers a particular action in memory + * + * @deprecated 1.8 Use {@link elgg_register_action()} instead + * + * @param string $action The name of the action (eg "register", "account/settings/save") + * @param boolean $public Can this action be accessed by people not logged into the system? + * @param string $filename Optionally, the filename where this action is located + * @param boolean $admin_only Whether this action is only available to admin users. + */ +function register_action($action, $public = false, $filename = "", $admin_only = false) { + elgg_deprecated_notice("register_action() was deprecated by elgg_register_action()", 1.8); + + if ($admin_only) { + $access = 'admin'; + } elseif ($public) { + $access = 'public'; + } else { + $access = 'logged_in'; + } + + return elgg_register_action($action, $filename, $access); +} + +/** + * Register an admin page with the admin panel. + * This function extends the view "admin/main" with the provided view. + * This view should provide a description and either a control or a link to. + * + * @deprecated 1.8 Extend admin views manually + * + * Usage: + * - To add a control to the main admin panel then extend admin/main + * - To add a control to a new page create a page which renders a view admin/subpage + * (where subpage is your new page - + * nb. some pages already exist that you can extend), extend the main view to point to it, + * and add controls to your new view. + * + * At the moment this is essentially a wrapper around elgg_extend_view(). + * + * @param string $new_admin_view The view associated with the control you're adding + * @param string $view The view to extend, by default this is 'admin/main'. + * @param int $priority Optional priority to govern the appearance in the list. + * + * @return void + */ +function extend_elgg_admin_page($new_admin_view, $view = 'admin/main', $priority = 500) { + elgg_deprecated_notice('extend_elgg_admin_page() does nothing. Extend admin views manually.', 1.8); +} + +/** + * Get entities ordered by a mathematical calculation + * + * @deprecated 1.8 Use elgg_get_entities_from_annotation_calculation() + * + * @param string $sum What sort of calculation to perform + * @param string $entity_type Type of Entity + * @param string $entity_subtype Subtype of Entity + * @param string $name Name of annotation + * @param string $mdname Metadata name + * @param string $mdvalue Metadata value + * @param int $owner_guid GUID of owner of annotation + * @param int $limit Limit of results + * @param int $offset Offset of results + * @param string $orderdir Order of results + * @param bool $count Return count or entities + * + * @return mixed + */ +function get_entities_from_annotations_calculate_x($sum = "sum", $entity_type = "", $entity_subtype = "", $name = "", $mdname = '', $mdvalue = '', $owner_guid = 0, $limit = 10, $offset = 0, $orderdir = 'desc', $count = false) { + + $msg = 'get_entities_from_annotations_calculate_x() is deprecated by elgg_get_entities_from_annotation_calculation().'; + + elgg_deprecated_notice($msg, 1.8); + + $options = array(); + + $options['calculation'] = $sum; + + if ($entity_type) { + $options['types'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtypes'] = $entity_subtype; + } + + $options['annotation_names'] = $name; + + if ($mdname) { + $options['metadata_names'] = $mdname; + } + + if ($mdvalue) { + $options['metadata_values'] = $mdvalue; + } + + // original function rewrote this to container guid. + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['container_guids'] = $owner_guid; + } else { + $options['container_guid'] = $owner_guid; + } + } + + $options['limit'] = $limit; + $options['offset'] = $offset; + + $options['order_by'] = "annotation_calculation $orderdir"; + + $options['count'] = $count; + + return elgg_get_entities_from_annotation_calculation($options); +} + +/** + * Returns entities ordered by the sum of an annotation + * + * @warning This is function uses sum instead of count. THIS IS SLOW. See #3366. + * This should be used when you have annotations with different values and you + * want a list of entities ordered by the sum of all of those values. + * If you want a list of entities ordered by the number of annotations on each entity, + * use __get_entities_from_annotations_calculate_x() and pass 'count' as the first param. + * + * @deprecated 1.8 Use elgg_get_entities_from_annotation_calculation() + * + * @param string $entity_type Type of Entity + * @param string $entity_subtype Subtype of Entity + * @param string $name Name of annotation + * @param string $mdname Metadata name + * @param string $mdvalue Metadata value + * @param int $owner_guid GUID of owner of annotation + * @param int $limit Limit of results + * @param int $offset Offset of results + * @param string $orderdir Order of results + * @param bool $count Return count or entities + * + * @return unknown + */ +function get_entities_from_annotation_count($entity_type = "", $entity_subtype = "", $name = "", $mdname = '', $mdvalue = '', $owner_guid = 0, $limit = 10, $offset = 0, $orderdir = 'desc', $count = false) { + + $msg = 'get_entities_from_annotation_count() is deprecated by elgg_get_entities_from_annotation_calculation().'; + + elgg_deprecated_notice($msg, 1.8); + + $options = array(); + + $options['calculation'] = 'sum'; + + if ($entity_type) { + $options['types'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtypes'] = $entity_subtype; + } + + $options['annotation_names'] = $name; + + if ($mdname) { + $options['metadata_names'] = $mdname; + } + + if ($mdvalue) { + $options['metadata_values'] = $mdvalue; + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + $options['limit'] = $limit; + $options['offset'] = $offset; + + $options['order_by'] = "annotation_calculation $orderdir"; + + $options['count'] = $count; + + return elgg_get_entities_from_annotation_calculation($options); +} + +/** + * Lists entities by the totals of a particular kind of annotation + * + * @deprecated 1.8 Use elgg_list_entities_from_annotation_calculation() + * + * @param string $entity_type Type of entity. + * @param string $entity_subtype Subtype of entity. + * @param string $name Name of annotation. + * @param int $limit Maximum number of results to return. + * @param int $owner_guid Owner. + * @param int $group_guid Group container. Currently only supported if entity_type is object + * @param boolean $asc Whether to list in ascending or descending order (default: desc) + * @param boolean $fullview Whether to display the entities in full + * @param boolean $listtypetoggle Can the 'gallery' view can be displayed (default: no) + * @param boolean $pagination Add pagination + * @param string $orderdir Order desc or asc + * + * @return string Formatted entity list + */ +function list_entities_from_annotation_count($entity_type = "", $entity_subtype = "", $name = "", $limit = 10, $owner_guid = 0, $group_guid = 0, $asc = false, $fullview = true, $listtypetoggle = false, $pagination = true, $orderdir = 'desc') { + + $msg = 'list_entities_from_annotation_count() is deprecated by elgg_list_entities_from_annotation_calculation().'; + + elgg_deprecated_notice($msg, 1.8); + + $options = array(); + + $options['calculation'] = 'sum'; + + if ($entity_type) { + $options['types'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtypes'] = $entity_subtype; + } + + $options['annotation_names'] = $name; + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + $options['full_view'] = $fullview; + + $options['list_type_toggle'] = $listtypetoggle; + + $options['pagination'] = $pagination; + + $options['limit'] = $limit; + + $options['order_by'] = "annotation_calculation $orderdir"; + + return elgg_get_entities_from_annotation_calculation($options); +} + +/** + * Adds an entry in $CONFIG[$register_name][$subregister_name]. + * + * @deprecated 1.8 Use the new menu system. + * + * This is only used for the site-wide menu. See {@link add_menu()}. + * + * @param string $register_name The name of the top-level register + * @param string $subregister_name The name of the subregister + * @param mixed $subregister_value The value of the subregister + * @param array $children_array Optionally, an array of children + * + * @return true|false Depending on success + */ +function add_to_register($register_name, $subregister_name, $subregister_value, $children_array = array()) { + elgg_deprecated_notice("add_to_register() has been deprecated", 1.8); + global $CONFIG; + + if (empty($register_name) || empty($subregister_name)) { + return false; + } + + if (!isset($CONFIG->registers)) { + $CONFIG->registers = array(); + } + + if (!isset($CONFIG->registers[$register_name])) { + $CONFIG->registers[$register_name] = array(); + } + + $subregister = new stdClass; + $subregister->name = $subregister_name; + $subregister->value = $subregister_value; + + if (is_array($children_array)) { + $subregister->children = $children_array; + } + + $CONFIG->registers[$register_name][$subregister_name] = $subregister; + return true; +} + +/** + * Removes a register entry from $CONFIG[register_name][subregister_name] + * + * @deprecated 1.8 Use the new menu system. + * + * This is used to by {@link remove_menu()} to remove site-wide menu items. + * + * @param string $register_name The name of the top-level register + * @param string $subregister_name The name of the subregister + * + * @return true|false Depending on success + * @since 1.7.0 + */ +function remove_from_register($register_name, $subregister_name) { + elgg_deprecated_notice("remove_from_register() has been deprecated", 1.8); + global $CONFIG; + + if (empty($register_name) || empty($subregister_name)) { + return false; + } + + if (!isset($CONFIG->registers)) { + return false; + } + + if (!isset($CONFIG->registers[$register_name])) { + return false; + } + + if (isset($CONFIG->registers[$register_name][$subregister_name])) { + unset($CONFIG->registers[$register_name][$subregister_name]); + return true; + } + + return false; +} + +/** + * If it exists, returns a particular register as an array + * + * @deprecated 1.8 Use the new menu system + * + * @param string $register_name The name of the register + * + * @return array|false Depending on success + */ +function get_register($register_name) { + elgg_deprecated_notice("get_register() has been deprecated", 1.8); + global $CONFIG; + + if ($register_name == 'menu') { + // backward compatible code for site menu + $menu = $CONFIG->menus['site']; + $builder = new ElggMenuBuilder($menu); + $menu_items = $builder->getMenu('text'); + $menu_items = $menu_items['default']; + + $menu = array(); + foreach ($menu_items as $item) { + $subregister = new stdClass; + $subregister->name = $item->getText(); + $subregister->value = $item->getHref(); + $menu[$subregister->name] = $subregister; + } + return $menu; + } + + if (isset($CONFIG->registers[$register_name])) { + return $CONFIG->registers[$register_name]; + } + + return false; +} + +/** + * Deprecated events core function. Code divided between elgg_register_event_handler() + * and trigger_elgg_event(). + * + * @deprecated 1.8 Use explicit register/trigger event functions + * + * @param string $event The type of event (eg 'init', 'update', 'delete') + * @param string $object_type The type of object (eg 'system', 'blog', 'user') + * @param string $function The name of the function that will handle the event + * @param int $priority Priority to call handler. Lower numbers called first (default 500) + * @param boolean $call Set to true to call the event rather than add to it (default false) + * @param mixed $object Optionally, the object the event is being performed on (eg a user) + * + * @return true|false Depending on success + */ +function events($event = "", $object_type = "", $function = "", $priority = 500, $call = false, $object = null) { + + elgg_deprecated_notice('events() has been deprecated.', 1.8); + + // leaving this here just in case someone was directly calling this internal function + if (!$call) { + return elgg_register_event_handler($event, $object_type, $function, $priority); + } else { + return trigger_elgg_event($event, $object_type, $object); + } +} + +/** + * Alias function for events, that registers a function to a particular kind of event + * + * @deprecated 1.8 Use elgg_register_event_handler() instead + * + * @param string $event The event type + * @param string $object_type The object type + * @param string $function The function name + * @return true|false Depending on success + */ +function register_elgg_event_handler($event, $object_type, $callback, $priority = 500) { + elgg_deprecated_notice("register_elgg_event_handler() was deprecated by elgg_register_event_handler()", 1.8); + return elgg_register_event_handler($event, $object_type, $callback, $priority); +} + +/** + * Unregisters a function to a particular kind of event + * + * @deprecated 1.8 Use elgg_unregister_event_handler instead + * + * @param string $event The event type + * @param string $object_type The object type + * @param string $function The function name + * @since 1.7.0 + */ +function unregister_elgg_event_handler($event, $object_type, $callback) { + elgg_deprecated_notice('unregister_elgg_event_handler => elgg_unregister_event_handler', 1.8); + elgg_unregister_event_handler($event, $object_type, $callback); +} + +/** + * Alias function for events, that triggers a particular kind of event + * + * @deprecated 1.8 Use elgg_trigger_event() instead + * + * @param string $event The event type + * @param string $object_type The object type + * @param string $function The function name + * @return true|false Depending on success + */ +function trigger_elgg_event($event, $object_type, $object = null) { + elgg_deprecated_notice('trigger_elgg_event() was deprecated by elgg_trigger_event()', 1.8); + return elgg_trigger_event($event, $object_type, $object); +} + +/** + * Register a function to a plugin hook for a particular entity type, with a given priority. + * + * @deprecated 1.8 Use elgg_register_plugin_hook_handler() instead + * + * eg if you want the function "export_user" to be called when the hook "export" for "user" entities + * is run, use: + * + * register_plugin_hook("export", "user", "export_user"); + * + * "all" is a valid value for both $hook and $entity_type. "none" is a valid value for $entity_type. + * + * The export_user function would then be defined as: + * + * function export_user($hook, $entity_type, $returnvalue, $params); + * + * Where $returnvalue is the return value returned by the last function returned by the hook, and + * $params is an array containing a set of parameters (or nothing). + * + * @param string $hook The name of the hook + * @param string $entity_type The name of the type of entity (eg "user", "object" etc) + * @param string $function The name of a valid function to be run + * @param string $priority The priority - 0 is first, 1000 last, default is 500 + * @return true|false Depending on success + */ +function register_plugin_hook($hook, $type, $callback, $priority = 500) { + elgg_deprecated_notice("register_plugin_hook() was deprecated by elgg_register_plugin_hook_handler()", 1.8); + return elgg_register_plugin_hook_handler($hook, $type, $callback, $priority); +} + +/** + * Unregister a function to a plugin hook for a particular entity type + * + * @deprecated 1.8 Use elgg_unregister_plugin_hook_handler() instead + * + * @param string $hook The name of the hook + * @param string $entity_type The name of the type of entity (eg "user", "object" etc) + * @param string $function The name of a valid function to be run + * @since 1.7.0 + */ +function unregister_plugin_hook($hook, $entity_type, $callback) { + elgg_deprecated_notice("unregister_plugin_hook() was deprecated by elgg_unregister_plugin_hook_handler()", 1.8); + elgg_unregister_plugin_hook_handler($hook, $entity_type, $callback); +} + +/** + * Triggers a plugin hook, with various parameters as an array. For example, to provide + * a 'foo' hook that concerns an entity of type 'bar', with a parameter called 'param1' + * with value 'value1', that by default returns true, you'd call: + * + * @deprecated 1.8 Use elgg_trigger_plugin_hook() instead + * + * trigger_plugin_hook('foo', 'bar', array('param1' => 'value1'), true); + * + * @see register_plugin_hook + * @param string $hook The name of the hook to trigger + * @param string $entity_type The name of the entity type to trigger it for (or "all", or "none") + * @param array $params Any parameters. It's good practice to name the keys, i.e. by using array('name' => 'value', 'name2' => 'value2') + * @param mixed $returnvalue An initial return value + * @return mixed|null The cumulative return value for the plugin hook functions + */ +function trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null) { + elgg_deprecated_notice("trigger_plugin_hook() was deprecated by elgg_trigger_plugin_hook()", 1.8); + return elgg_trigger_plugin_hook($hook, $type, $params, $returnvalue); +} + +/** + * Checks if code is being called from a certain function. + * + * To use, call this function with the function name (and optional + * file location) that it has to be called from, it will either + * return true or false. + * + * e.g. + * + * function my_secure_function() + * { + * if (!call_gatekeeper("my_call_function")) + * return false; + * + * ... do secure stuff ... + * } + * + * function my_call_function() + * { + * // will work + * my_secure_function(); + * } + * + * function bad_function() + * { + * // Will not work + * my_secure_function(); + * } + * + * @param mixed $function The function that this function must have in its call stack, + * to test against a method pass an array containing a class and + * method name. + * @param string $file Optional file that the function must reside in. + * + * @return bool + * + * @deprecated 1.8 A neat but pointless function + */ +function call_gatekeeper($function, $file = "") { + elgg_deprecated_notice("call_gatekeeper() is neat but pointless", 1.8); + // Sanity check + if (!$function) { + return false; + } + + // Check against call stack to see if this is being called from the correct location + $callstack = debug_backtrace(); + $stack_element = false; + + foreach ($callstack as $call) { + if (is_array($function)) { + if ((strcmp($call['class'], $function[0]) == 0) && (strcmp($call['function'], $function[1]) == 0)) { + $stack_element = $call; + } + } else { + if (strcmp($call['function'], $function) == 0) { + $stack_element = $call; + } + } + } + + if (!$stack_element) { + return false; + } + + // If file then check that this it is being called from this function + if ($file) { + $mirror = null; + + if (is_array($function)) { + $mirror = new ReflectionMethod($function[0], $function[1]); + } else { + $mirror = new ReflectionFunction($function); + } + + if ((!$mirror) || (strcmp($file, $mirror->getFileName()) != 0)) { + return false; + } + } + + return true; +} + +/** + * This function checks to see if it is being called at somepoint by a function defined somewhere + * on a given path (optionally including subdirectories). + * + * This function is similar to call_gatekeeper() but returns true if it is being called + * by a method or function which has been defined on a given path or by a specified file. + * + * @param string $path The full path and filename that this function must have + * in its call stack If a partial path is given and + * $include_subdirs is true, then the function will return + * true if called by any function in or below the specified path. + * @param bool $include_subdirs Are subdirectories of the path ok, or must you specify an + * absolute path and filename. + * @param bool $strict_mode If true then the calling method or function must be directly + * called by something on $path, if false the whole call stack is + * searched. + * + * @return void + * + * @deprecated 1.8 A neat but pointless function + */ +function callpath_gatekeeper($path, $include_subdirs = true, $strict_mode = false) { + elgg_deprecated_notice("callpath_gatekeeper() is neat but pointless", 1.8); + + global $CONFIG; + + $path = sanitise_string($path); + + if ($path) { + $callstack = debug_backtrace(); + + foreach ($callstack as $call) { + $call['file'] = str_replace("\\", "/", $call['file']); + + if ($include_subdirs) { + if (strpos($call['file'], $path) === 0) { + + if ($strict_mode) { + $callstack[1]['file'] = str_replace("\\", "/", $callstack[1]['file']); + if ($callstack[1] === $call) { + return true; + } + } else { + return true; + } + } + } else { + if (strcmp($path, $call['file']) == 0) { + if ($strict_mode) { + if ($callstack[1] === $call) { + return true; + } + } else { + return true; + } + } + } + + } + return false; + } + + if (isset($CONFIG->debug)) { + system_message("Gatekeeper'd function called from {$callstack[1]['file']}:" . "{$callstack[1]['line']}\n\nStack trace:\n\n" . print_r($callstack, true)); + } + + return false; +} + +/** + * Returns SQL where clause for owner and containers. + * + * @deprecated 1.8 Use elgg_get_guid_based_where_sql(); + * + * @param string $table Entity table prefix as defined in SELECT...FROM entities $table + * @param NULL|array $owner_guids Owner GUIDs + * + * @return FALSE|str + * @since 1.7.0 + * @access private + */ +function elgg_get_entity_owner_where_sql($table, $owner_guids) { + elgg_deprecated_notice('elgg_get_entity_owner_where_sql() is deprecated by elgg_get_guid_based_where_sql().', 1.8); + + return elgg_get_guid_based_where_sql("{$table}.owner_guid", $owner_guids); +} + +/** + * Returns SQL where clause for containers. + * + * @deprecated 1.8 Use elgg_get_guid_based_where_sql(); + * + * @param string $table Entity table prefix as defined in + * SELECT...FROM entities $table + * @param NULL|array $container_guids Array of container guids + * + * @return FALSE|string + * @since 1.7.0 + * @access private + */ +function elgg_get_entity_container_where_sql($table, $container_guids) { + elgg_deprecated_notice('elgg_get_entity_container_where_sql() is deprecated by elgg_get_guid_based_where_sql().', 1.8); + + return elgg_get_guid_based_where_sql("{$table}.container_guid", $container_guids); +} + +/** + * Returns SQL where clause for site entities + * + * @deprecated 1.8 Use elgg_get_guid_based_where_sql() + * + * @param string $table Entity table prefix as defined in SELECT...FROM entities $table + * @param NULL|array $site_guids Array of site guids + * + * @return FALSE|string + * @since 1.7.0 + * @access private + */ +function elgg_get_entity_site_where_sql($table, $site_guids) { + elgg_deprecated_notice('elgg_get_entity_site_where_sql() is deprecated by elgg_get_guid_based_where_sql().', 1.8); + + return elgg_get_guid_based_where_sql("{$table}.site_guid", $site_guids); +} + +/** + * Return an array of objects in a given container. + * + * @see get_entities() + * + * @param int $group_guid The container (defaults to current page owner) + * @param string $subtype The subtype + * @param int $owner_guid Owner + * @param int $site_guid The site + * @param string $order_by Order + * @param int $limit Limit on number of elements to return, by default 10. + * @param int $offset Where to start, by default 0. + * @param bool $count Whether to return the entities or a count of them. + * + * @return array|false + * @deprecated 1.8 Use elgg_get_entities() instead + */ +function get_objects_in_group($group_guid, $subtype = "", $owner_guid = 0, $site_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = FALSE) { + elgg_deprecated_notice("get_objects_in_group was deprected in 1.8. Use elgg_get_entities() instead", 1.8); + + global $CONFIG; + + if ($subtype === FALSE || $subtype === null || $subtype === 0) { + return FALSE; + } + + if ($order_by == "") { + $order_by = "e.time_created desc"; + } + $order_by = sanitise_string($order_by); + $limit = (int)$limit; + $offset = (int)$offset; + $site_guid = (int)$site_guid; + if ($site_guid == 0) { + $site_guid = $CONFIG->site_guid; + } + + $container_guid = (int)$group_guid; + if ($container_guid == 0) { + $container_guid = elgg_get_page_owner_guid(); + } + + $where = array(); + + $where[] = "e.type='object'"; + + if (!empty($subtype)) { + if (!$subtype = get_subtype_id('object', $subtype)) { + return FALSE; + } + $where[] = "e.subtype=$subtype"; + } + if ($owner_guid != "") { + if (!is_array($owner_guid)) { + $owner_guid = (int)$owner_guid; + $where[] = "e.container_guid = '$owner_guid'"; + } else if (sizeof($owner_guid) > 0) { + // Cast every element to the owner_guid array to int + $owner_guid = array_map("sanitise_int", $owner_guid); + $owner_guid = implode(",", $owner_guid); + $where[] = "e.container_guid in ({$owner_guid})"; + } + } + if ($site_guid > 0) { + $where[] = "e.site_guid = {$site_guid}"; + } + + if ($container_guid > 0) { + $where[] = "e.container_guid = {$container_guid}"; + } + + if (!$count) { + $query = "SELECT * from {$CONFIG->dbprefix}entities e" . " join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid where "; + } else { + $query = "SELECT count(e.guid) as total from {$CONFIG->dbprefix}entities e" . " join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid where "; + } + foreach ($where as $w) { + $query .= " $w and "; + } + + // Add access controls + $query .= get_access_sql_suffix('e'); + if (!$count) { + $query .= " order by $order_by"; + + // Add order and limit + if ($limit) { + $query .= " limit $offset, $limit"; + } + + $dt = get_data($query, "entity_row_to_elggstar"); + return $dt; + } else { + $total = get_data_row($query); + return $total->total; + } +} + +/** + * Lists entities that belong to a group. + * + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param int $container_guid The GUID of the containing group + * @param int $limit The number of entities to display per page (default: 10) + * @param bool $fullview Whether or not to display the full view (default: true) + * @param bool $listtypetoggle Whether or not to allow gallery view (default: true) + * @param bool $pagination Whether to display pagination (default: true) + * + * @return string List of parsed entities + * + * @see elgg_list_entities() + * @deprecated 1.8 Use elgg_list_entities() instead + */ +function list_entities_groups($subtype = "", $owner_guid = 0, $container_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true) { + elgg_deprecated_notice("list_entities_groups was deprecated in 1.8. Use elgg_list_entities() instead.", 1.8); + $offset = (int)get_input('offset'); + $count = get_objects_in_group($container_guid, $subtype, $owner_guid, 0, "", $limit, $offset, true); + $entities = get_objects_in_group($container_guid, $subtype, $owner_guid, 0, "", $limit, $offset); + + return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $listtypetoggle, $pagination); +} + +/** + * Get all the entities from metadata from a group. + * + * @param int $group_guid The ID of the group. + * @param mixed $meta_name Metadata name + * @param mixed $meta_value Metadata value + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity. + * @param int $owner_guid Owner guid + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by Optional ordering. + * @param int $site_guid Site GUID. 0 for current, -1 for any + * @param bool $count Return count instead of entities + * + * @return array|false + * @deprecated 1.8 Use elgg_get_entities_from_metadata() + */ +function get_entities_from_metadata_groups($group_guid, $meta_name, $meta_value = "", $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) { + elgg_deprecated_notice("get_entities_from_metadata_groups was deprecated in 1.8.", 1.8); + global $CONFIG; + + $meta_n = get_metastring_id($meta_name); + $meta_v = get_metastring_id($meta_value); + + $entity_type = sanitise_string($entity_type); + $entity_subtype = get_subtype_id($entity_type, $entity_subtype); + $limit = (int)$limit; + $offset = (int)$offset; + if ($order_by == "") { + $order_by = "e.time_created desc"; + } + $order_by = sanitise_string($order_by); + $site_guid = (int)$site_guid; + if (is_array($owner_guid)) { + foreach ($owner_guid as $key => $guid) { + $owner_guid[$key] = (int)$guid; + } + } else { + $owner_guid = (int)$owner_guid; + } + if ($site_guid == 0) { + $site_guid = $CONFIG->site_guid; + } + + $container_guid = (int)$group_guid; + if ($container_guid == 0) { + $container_guid = elgg_get_page_owner_guid(); + } + + $where = array(); + + if ($entity_type != "") { + $where[] = "e.type='$entity_type'"; + } + if ($entity_subtype) { + $where[] = "e.subtype=$entity_subtype"; + } + if ($meta_name != "") { + $where[] = "m.name_id='$meta_n'"; + } + if ($meta_value != "") { + $where[] = "m.value_id='$meta_v'"; + } + if ($site_guid > 0) { + $where[] = "e.site_guid = {$site_guid}"; + } + if ($container_guid > 0) { + $where[] = "e.container_guid = {$container_guid}"; + } + + if (is_array($owner_guid)) { + $where[] = "e.container_guid in (" . implode(",", $owner_guid) . ")"; + } else if ($owner_guid > 0) { + $where[] = "e.container_guid = {$owner_guid}"; + } + + if (!$count) { + $query = "SELECT distinct e.* "; + } else { + $query = "SELECT count(e.guid) as total "; + } + + $query .= "from {$CONFIG->dbprefix}entities e" . " JOIN {$CONFIG->dbprefix}metadata m on e.guid = m.entity_guid " . " JOIN {$CONFIG->dbprefix}objects_entity o on e.guid = o.guid where"; + + foreach ($where as $w) { + $query .= " $w and "; + } + + // Add access controls + $query .= get_access_sql_suffix("e"); + + if (!$count) { + $query .= " order by $order_by limit $offset, $limit"; // Add order and limit + return get_data($query, "entity_row_to_elggstar"); + } else { + if ($row = get_data_row($query)) { + return $row->total; + } + } + return false; +} + +/** + * As get_entities_from_metadata_groups() but with multiple entities. + * + * @param int $group_guid The ID of the group. + * @param array $meta_array Array of 'name' => 'value' pairs + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity. + * @param int $owner_guid Owner GUID + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by Optional ordering. + * @param int $site_guid Site GUID. 0 for current, -1 for any + * @param bool $count Return count of entities instead of entities + * + * @return int|array List of ElggEntities, or the total number if count is set to false + * @deprecated 1.8 Use elgg_get_entities_from_metadata() + */ +function get_entities_from_metadata_groups_multi($group_guid, $meta_array, $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) { + elgg_deprecated_notice("get_entities_from_metadata_groups_multi was deprecated in 1.8.", 1.8); + + global $CONFIG; + + if (!is_array($meta_array) || sizeof($meta_array) == 0) { + return false; + } + + $where = array(); + + $mindex = 1; + $join = ""; + foreach ($meta_array as $meta_name => $meta_value) { + $meta_n = get_metastring_id($meta_name); + $meta_v = get_metastring_id($meta_value); + $join .= " JOIN {$CONFIG->dbprefix}metadata m{$mindex} on e.guid = m{$mindex}.entity_guid" . " JOIN {$CONFIG->dbprefix}objects_entity o on e.guid = o.guid "; + + if ($meta_name != "") { + $where[] = "m{$mindex}.name_id='$meta_n'"; + } + + if ($meta_value != "") { + $where[] = "m{$mindex}.value_id='$meta_v'"; + } + + $mindex++; + } + + $entity_type = sanitise_string($entity_type); + $entity_subtype = get_subtype_id($entity_type, $entity_subtype); + $limit = (int)$limit; + $offset = (int)$offset; + if ($order_by == "") { + $order_by = "e.time_created desc"; + } + $order_by = sanitise_string($order_by); + $owner_guid = (int)$owner_guid; + + $site_guid = (int)$site_guid; + if ($site_guid == 0) { + $site_guid = $CONFIG->site_guid; + } + + //$access = get_access_list(); + + if ($entity_type != "") { + $where[] = "e.type = '{$entity_type}'"; + } + + if ($entity_subtype) { + $where[] = "e.subtype = {$entity_subtype}"; + } + + if ($site_guid > 0) { + $where[] = "e.site_guid = {$site_guid}"; + } + + if ($owner_guid > 0) { + $where[] = "e.owner_guid = {$owner_guid}"; + } + + if ($container_guid > 0) { + $where[] = "e.container_guid = {$container_guid}"; + } + + if ($count) { + $query = "SELECT count(e.guid) as total "; + } else { + $query = "SELECT distinct e.* "; + } + + $query .= " from {$CONFIG->dbprefix}entities e {$join} where"; + foreach ($where as $w) { + $query .= " $w and "; + } + $query .= get_access_sql_suffix("e"); // Add access controls + + if (!$count) { + $query .= " order by $order_by limit $offset, $limit"; // Add order and limit + return get_data($query, "entity_row_to_elggstar"); + } else { + if ($count = get_data_row($query)) { + return $count->total; + } + } + return false; +} + +/** + * List items within a given geographic area. + * + * @param real $lat Latitude + * @param real $long Longitude + * @param real $radius The radius + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param int $limit The number of entities to display per page (default: 10) + * @param bool $fullview Whether or not to display the full view (default: true) + * @param bool $listtypetoggle Whether or not to allow gallery view + * @param bool $navigation Display pagination? Default: true + * + * @return string A viewable list of entities + * @deprecated 1.8 Use elgg_get_entities_from_location() + */ +function list_entities_in_area($lat, $long, $radius, $type = "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) { + elgg_deprecated_notice('list_entities_in_area() was deprecated. Use elgg_list_entities_from_location()', 1.8); + + $options = array(); + + $options['latitude'] = $lat; + $options['longitude'] = $long; + $options['distance'] = $radius; + + if ($type) { + $options['types'] = $type; + } + + if ($subtype) { + $options['subtypes'] = $subtype; + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + $options['limit'] = $limit; + + $options['full_view'] = $fullview; + $options['list_type_toggle'] = $listtypetoggle; + $options['pagination'] = $pagination; + + return elgg_list_entities_from_location($options); +} + +/** + * List entities in a given location + * + * @param string $location Location + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param int $limit The number of entities to display per page (default: 10) + * @param bool $fullview Whether or not to display the full view (default: true) + * @param bool $listtypetoggle Whether or not to allow gallery view + * @param bool $navigation Display pagination? Default: true + * + * @return string A viewable list of entities + * @deprecated 1.8 Use elgg_list_entities_from_location() + */ +function list_entities_location($location, $type = "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) { + elgg_deprecated_notice('list_entities_location() was deprecated. Use elgg_list_entities_from_metadata()', 1.8); + + return list_entities_from_metadata('location', $location, $type, $subtype, $owner_guid, $limit, $fullview, $listtypetoggle, $navigation); +} + +/** + * Return entities within a given geographic area. + * + * @param float $lat Latitude + * @param float $long Longitude + * @param float $radius The radius + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param string $order_by The field to order by; by default, time_created desc + * @param int $limit The number of entities to return; 10 by default + * @param int $offset The indexing offset, 0 by default + * @param boolean $count Count entities + * @param int $site_guid Site GUID. 0 for current, -1 for any + * @param int|array $container_guid Container GUID + * + * @return array A list of entities. + * @deprecated 1.8 Use elgg_get_entities_from_location() + */ +function get_entities_in_area($lat, $long, $radius, $type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = NULL) { + elgg_deprecated_notice('get_entities_in_area() was deprecated by elgg_get_entities_from_location()!', 1.8); + + $options = array(); + + $options['latitude'] = $lat; + $options['longitude'] = $long; + $options['distance'] = $radius; + + // set container_guid to owner_guid to emulate old functionality + if ($owner_guid != "") { + if (is_null($container_guid)) { + $container_guid = $owner_guid; + } + } + + if ($type) { + $options['types'] = $type; + } + + if ($subtype) { + $options['subtypes'] = $subtype; + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + if ($container_guid) { + if (is_array($container_guid)) { + $options['container_guids'] = $container_guid; + } else { + $options['container_guid'] = $container_guid; + } + } + + $options['limit'] = $limit; + + if ($offset) { + $options['offset'] = $offset; + } + + if ($order_by) { + $options['order_by']; + } + + if ($site_guid) { + $options['site_guid']; + } + + if ($count) { + $options['count'] = $count; + } + + return elgg_get_entities_from_location($options); +} + +/** + * Return a list of entities suitable for display based on the given search criteria. + * + * @see elgg_view_entity_list + * + * @deprecated 1.8 Use elgg_list_entities_from_metadata + * + * @param mixed $meta_name Metadata name to search on + * @param mixed $meta_value The value to match, optionally + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity + * @param int $owner_guid Owner GUID + * @param int $limit Number of entities to display per page + * @param bool $fullview WDisplay the full view (default: true) + * @param bool $listtypetoggle Allow users to toggle to the gallery view. Default: true + * @param bool $pagination Display pagination? Default: true + * @param bool $case_sensitive Case sensitive metadata names? + * + * @return string + * + * @return string A list of entities suitable for display + */ +function list_entities_from_metadata($meta_name, $meta_value = "", $entity_type = ELGG_ENTITIES_ANY_VALUE, $entity_subtype = ELGG_ENTITIES_ANY_VALUE, $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true, $case_sensitive = true) { + + elgg_deprecated_notice('list_entities_from_metadata() was deprecated by elgg_list_entities_from_metadata()!', 1.8); + + $offset = (int)get_input('offset'); + $limit = (int)$limit; + $options = array( + 'metadata_name' => $meta_name, + 'metadata_value' => $meta_value, + 'type' => $entity_type, + 'subtype' => $entity_subtype, + 'limit' => $limit, + 'offset' => $offset, + 'count' => TRUE, + 'metadata_case_sensitive' => $case_sensitive + ); + + // previous function allowed falsy $owner_guid for anything + if ($owner_guid) { + $options['owner_guid'] = $owner_guid; + } + + $count = elgg_get_entities_from_metadata($options); + + $options['count'] = FALSE; + $entities = elgg_get_entities_from_metadata($options); + + return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $listtypetoggle, $pagination); +} + +/** + * Returns a viewable list of entities based on the given search criteria. + * + * @see elgg_view_entity_list + * + * @param array $meta_array Array of 'name' => 'value' pairs + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity. + * @param int $owner_guid Owner GUID + * @param int $limit Limit + * @param bool $fullview WDisplay the full view (default: true) + * @param bool $listtypetoggle Allow users to toggle to the gallery view. Default: true + * @param bool $pagination Display pagination? Default: true + * + * @return string List of ElggEntities suitable for display + * + * @deprecated 1.8 Use elgg_list_entities_from_metadata() instead + */ +function list_entities_from_metadata_multi($meta_array, $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true) { + elgg_deprecated_notice(elgg_echo('deprecated:function', array( + 'list_entities_from_metadata_multi', 'elgg_get_entities_from_metadata')), 1.8); + + $offset = (int)get_input('offset'); + $limit = (int)$limit; + $count = get_entities_from_metadata_multi($meta_array, $entity_type, $entity_subtype, $owner_guid, $limit, $offset, "", $site_guid, true); + $entities = get_entities_from_metadata_multi($meta_array, $entity_type, $entity_subtype, $owner_guid, $limit, $offset, "", $site_guid, false); + + return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $listtypetoggle, $pagination); +} + +/** + * Deprecated by elgg_register_menu_item(). Set $menu_name to 'page'. + * + * @see elgg_register_menu_item() + * @deprecated 1.8 Use the new menu system + * + * @param string $label The label + * @param string $link The link + * @param string $group The group to store item in + * @param boolean $onclick Add a confirmation when clicked? + * @param boolean $selected Is menu item selected + * + * @return bool + */ +function add_submenu_item($label, $link, $group = 'default', $onclick = false, $selected = NULL) { + elgg_deprecated_notice('add_submenu_item was deprecated by elgg_register_menu_item', 1.8); + + // submenu items were added in the page setup hook usually by checking + // the context. We'll pass in the current context here, which will + // emulate that effect. + // if context == 'main' (default) it probably means they always wanted + // the menu item to show up everywhere. + $context = elgg_get_context(); + + if ($context == 'main') { + $context = 'all'; + } + + $item = array('name' => $label, 'text' => $label, 'href' => $link, 'context' => $context, + 'section' => $group,); + + if ($selected) { + $item['selected'] = true; + } + + if ($onclick) { + $js = "onclick=\"javascript:return confirm('" . elgg_echo('deleteconfirm') . "')\""; + $item['vars'] = array('js' => $js); + } + + return elgg_register_menu_item('page', $item); +} + +/** + * Remove an item from submenu by label + * + * @deprecated 1.8 Use the new menu system + * @see elgg_unregister_menu_item() + * + * @param string $label The item label + * @param string $group The submenu group (default "a") + * @return bool whether the item was removed or not + * @since 1.7.8 + */ +function remove_submenu_item($label, $group = 'a') { + elgg_deprecated_notice('remove_submenu_item was deprecated by elgg_unregister_menu_item', 1.8); + + return elgg_unregister_menu_item('page', $label); +} + +/** + * Use elgg_view_menu(). Set $menu_name to 'owner_block'. + * + * @see elgg_view_menu() + * @deprecated 1.8 Use the new menu system. elgg_view_menu() + * + * @return string + */ +function get_submenu() { + elgg_deprecated_notice("get_submenu() has been deprecated by elgg_view_menu()", 1.8); + return elgg_view_menu('owner_block', array('entity' => $owner, + 'class' => 'elgg-menu-owner-block',)); +} + +/** + * Adds an item to the site-wide menu. + * + * You can obtain the menu array by calling {@link get_register('menu')} + * + * @param string $menu_name The name of the menu item + * @param string $menu_url The URL of the page + * @param array $menu_children Optionally, an array of submenu items (not used) + * @param string $context (not used) + * + * @return true|false Depending on success + * @deprecated 1.8 use elgg_register_menu_item() for the menu 'site' + */ +function add_menu($menu_name, $menu_url, $menu_children = array(), $context = "") { + elgg_deprecated_notice('add_menu() deprecated by elgg_register_menu_item()', 1.8); + + return elgg_register_menu_item('site', array('name' => $menu_name, 'text' => $menu_name, + 'href' => $menu_url,)); +} + +/** + * Removes an item from the menu register + * + * @param string $menu_name The name of the menu item + * + * @return true|false Depending on success + * @deprecated 1.8 Use the new menu system + */ +function remove_menu($menu_name) { + elgg_deprecated_notice("remove_menu() deprecated by elgg_unregister_menu_item()", 1.8); + return elgg_unregister_menu_item('site', $menu_name); +} + +/** + * When given a title, returns a version suitable for inclusion in a URL + * + * @param string $title The title + * + * @return string The optimised title + * @deprecated 1.8 Use elgg_get_friendly_title() + */ +function friendly_title($title) { + elgg_deprecated_notice('friendly_title was deprecated by elgg_get_friendly_title', 1.8); + return elgg_get_friendly_title($title); +} + +/** + * Displays a UNIX timestamp in a friendly way (eg "less than a minute ago") + * + * @param int $time A UNIX epoch timestamp + * + * @return string The friendly time + * @deprecated 1.8 Use elgg_view_friendly_time() + */ +function friendly_time($time) { + elgg_deprecated_notice('friendly_time was deprecated by elgg_view_friendly_time', 1.8); + return elgg_view_friendly_time($time); +} + +/** + * Filters a string into an array of significant words + * + * @deprecated 1.8 Don't use this. + * + * @param string $string A string + * + * @return array + */ +function filter_string($string) { + elgg_deprecated_notice('filter_string() was deprecated!', 1.8); + + // Convert it to lower and trim + $string = strtolower($string); + $string = trim($string); + + // Remove links and email addresses + // match protocol://address/path/file.extension?some=variable&another=asf% + $string = preg_replace("/\s([a-zA-Z]+:\/\/[a-z][a-z0-9\_\.\-]*[a-z]{2,6}" . "[a-zA-Z0-9\/\*\-\?\&\%\=]*)([\s|\.|\,])/iu", " ", $string); + + // match www.something.domain/path/file.extension?some=variable&another=asf% + $string = preg_replace("/\s(www\.[a-z][a-z0-9\_\.\-]*[a-z]{2,6}" . "[a-zA-Z0-9\/\*\-\?\&\%\=]*)([\s|\.|\,])/iu", " ", $string); + + // match name@address + $string = preg_replace("/\s([a-zA-Z][a-zA-Z0-9\_\.\-]*[a-zA-Z]" . "*\@[a-zA-Z][a-zA-Z0-9\_\.\-]*[a-zA-Z]{2,6})([\s|\.|\,])/iu", " ", $string); + + // Sanitise the string; remove unwanted characters + $string = preg_replace('/\W/ui', ' ', $string); + + // Explode it into an array + $terms = explode(' ', $string); + + // Remove any blacklist terms + //$terms = array_filter($terms, 'remove_blacklist'); + + return $terms; +} + +/** + * Returns true if the word in $input is considered significant + * + * @deprecated 1.8 Don't use this. + * + * @param string $input A word + * + * @return true|false + */ +function remove_blacklist($input) { + elgg_deprecated_notice('remove_blacklist() was deprecated!', 1.8); + + global $CONFIG; + + if (!is_array($CONFIG->wordblacklist)) { + return $input; + } + + if (strlen($input) < 3 || in_array($input, $CONFIG->wordblacklist)) { + return false; + } + + return true; +} + +/** + * Gets the guid of the entity that owns the current page. + * + * @deprecated 1.8 Use elgg_get_page_owner_guid() + * + * @return int The current page owner guid (0 if none). + */ +function page_owner() { + elgg_deprecated_notice('page_owner() was deprecated by elgg_get_page_owner_guid().', 1.8); + return elgg_get_page_owner_guid(); +} + +/** + * Gets the owner entity for the current page. + * + * @deprecated 1.8 Use elgg_get_page_owner_entity() + * @return ElggEntity|false The current page owner or false if none. + */ +function page_owner_entity() { + elgg_deprecated_notice('page_owner_entity() was deprecated by elgg_get_page_owner_entity().', 1.8); + return elgg_get_page_owner_entity(); +} + +/** + * Registers a page owner handler function + * + * @param string $functionname The callback function + * + * @deprecated 1.8 Use the 'page_owner', 'system' plugin hook + * @return void + */ +function add_page_owner_handler($functionname) { + elgg_deprecated_notice("add_page_owner_handler() was deprecated by the plugin hook 'page_owner', 'system'.", 1.8); +} + +/** + * Set a page owner entity + * + * @param int $entitytoset The GUID of the entity + * + * @deprecated 1.8 Use elgg_set_page_owner_guid() + * @return void + */ +function set_page_owner($entitytoset = -1) { + elgg_deprecated_notice('set_page_owner() was deprecated by elgg_set_page_owner_guid().', 1.8); + elgg_set_page_owner_guid($entitytoset); +} + +/** + * Sets the functional context of a page + * + * @deprecated 1.8 Use elgg_set_context() + * + * @param string $context The context of the page + * + * @return mixed Either the context string, or false on failure + */ +function set_context($context) { + elgg_deprecated_notice('set_context() was deprecated by elgg_set_context().', 1.8); + elgg_set_context($context); + if (empty($context)) { + return false; + } + return $context; +} + +/** + * Returns the functional context of a page + * + * @deprecated 1.8 Use elgg_get_context() + * + * @return string The context, or 'main' if no context has been provided + */ +function get_context() { + elgg_deprecated_notice('get_context() was deprecated by elgg_get_context().', 1.8); + return elgg_get_context(); + + // @todo - used to set context based on calling script + // $context = get_plugin_name(true) +} + +/** + * Returns a list of plugins to load, in the order that they should be loaded. + * + * @deprecated 1.8 Use elgg_get_plugin_ids_in_dir() or elgg_get_plugins() + * + * @return array List of plugins + */ +function get_plugin_list() { + elgg_deprecated_notice('get_plugin_list() is deprecated by elgg_get_plugin_ids_in_dir() or elgg_get_plugins()', 1.8); + + $plugins = elgg_get_plugins('any'); + + $list = array(); + if ($plugins) { + foreach ($plugins as $i => $plugin) { + // in <=1.7 this returned indexed by multiples of 10. + // uh...sure...why not. + $index = ($i + 1) * 10; + $list[$index] = $plugin->getID(); + } + } + + return $list; +} + +/** + * Regenerates the list of known plugins and saves it to the current site + * + * Important: You should regenerate simplecache and the viewpath cache after executing this function + * otherwise you may experience view display artifacts. Do this with the following code: + * + * elgg_regenerate_simplecache(); + * elgg_reset_system_cache(); + * + * @deprecated 1.8 Use elgg_generate_plugin_entities() and elgg_set_plugin_priorities() + * + * @param array $pluginorder Optionally, a list of existing plugins and their orders + * + * @return array The new list of plugins and their orders + */ +function regenerate_plugin_list($pluginorder = FALSE) { + $msg = 'regenerate_plugin_list() is (sorta) deprecated by elgg_generate_plugin_entities() and' + . ' elgg_set_plugin_priorities().'; + elgg_deprecated_notice($msg, 1.8); + + // they're probably trying to set it? + if ($pluginorder) { + if (elgg_generate_plugin_entities()) { + // sort the plugins by the index numerically since we used + // weird indexes in the old system. + ksort($pluginorder, SORT_NUMERIC); + return elgg_set_plugin_priorities($pluginorder); + } + return false; + } else { + // they're probably trying to regenerate from disk? + return elgg_generate_plugin_entities(); + } +} + +/** + * Get the name of the most recent plugin to be called in the + * call stack (or the plugin that owns the current page, if any). + * + * i.e., if the last plugin was in /mod/foobar/, get_plugin_name would return foo_bar. + * + * @deprecated 1.8 Use elgg_get_calling_plugin_id() + * + * @param boolean $mainfilename If set to true, this will instead determine the + * context from the main script filename called by + * the browser. Default = false. + * + * @return string|false Plugin name, or false if no plugin name was called + */ +function get_plugin_name($mainfilename = false) { + elgg_deprecated_notice('get_plugin_name() is deprecated by elgg_get_calling_plugin_id()', 1.8); + + return elgg_get_calling_plugin_id($mainfilename); +} + +/** + * Load and parse a plugin manifest from a plugin XML file. + * + * @example plugins/manifest.xml Example 1.8-style manifest file. + * + * @deprecated 1.8 Use ElggPlugin->getManifest() + * + * @param string $plugin Plugin name. + * @return array of values + */ +function load_plugin_manifest($plugin) { + elgg_deprecated_notice('load_plugin_manifest() is deprecated by ElggPlugin->getManifest()', 1.8); + + $xml_file = elgg_get_plugins_path() . "$plugin/manifest.xml"; + + try { + $manifest = new ElggPluginManifest($xml_file, $plugin); + } catch(Exception $e) { + return false; + } + + return $manifest->getManifest(); +} + +/** + * This function checks a plugin manifest 'elgg_version' value against the current install + * returning TRUE if the elgg_version is >= the current install's version. + * + * @deprecated 1.8 Use ElggPlugin->canActivate() + * + * @param string $manifest_elgg_version_string The build version (eg 2009010201). + * @return bool + */ +function check_plugin_compatibility($manifest_elgg_version_string) { + elgg_deprecated_notice('check_plugin_compatibility() is deprecated by ElggPlugin->canActivate()', 1.8); + + $version = get_version(); + + if (strpos($manifest_elgg_version_string, '.') === false) { + // Using version + $req_version = (int)$manifest_elgg_version_string; + + return ($version >= $req_version); + } + + return false; +} + +/** + * Shorthand function for finding the plugin settings. + * + * @deprecated 1.8 Use elgg_get_calling_plugin_entity() or elgg_get_plugin_from_id() + * + * @param string $plugin_id Optional plugin id, if not specified + * then it is detected from where you are calling. + * + * @return mixed + */ +function find_plugin_settings($plugin_id = null) { + elgg_deprecated_notice('find_plugin_setting() is deprecated by elgg_get_calling_plugin_entity() or elgg_get_plugin_from_id()', 1.8); + if ($plugin_id) { + return elgg_get_plugin_from_id($plugin_id); + } else { + return elgg_get_calling_plugin_entity(); + } +} + +/** + * Return an array of installed plugins. + * + * @deprecated 1.8 use elgg_get_plugins() + * + * @param string $status any|enabled|disabled + * @return array + */ +function get_installed_plugins($status = 'all') { + global $CONFIG; + + elgg_deprecated_notice('get_installed_plugins() was deprecated by elgg_get_plugins()', 1.8); + + $plugins = elgg_get_plugins($status); + + if (!$plugins) { + return array(); + } + + $installed_plugins = array(); + + foreach ($plugins as $plugin) { + if (!$plugin->isValid()) { + continue; + } + + $include = true; + + if ($status == 'enabled' && !$plugin->isActive()) { + $include = false; + } elseif ($status == 'disabled' && $plugin->isActive()) { + $include = true; + } + + if ($include) { + $installed_plugins[$plugin->getID()] = array( + 'active' => $plugin->isActive(), + 'manifest' => $plugin->getManifest()->getManifest() + ); + } + } + + return $installed_plugins; +} + +/** + * Enable a plugin for a site (default current site) + * + * Important: You should regenerate simplecache and the viewpath cache after executing this function + * otherwise you may experience view display artifacts. Do this with the following code: + * + * elgg_regenerate_simplecache(); + * elgg_reset_system_cache(); + * + * @deprecated 1.8 Use ElggPlugin->activate() + * + * @param string $plugin The plugin name. + * @param int $site_guid The site id, if not specified then this is detected. + * + * @return array + * @throws InvalidClassException + */ +function enable_plugin($plugin, $site_guid = null) { + elgg_deprecated_notice('enable_plugin() was deprecated by ElggPlugin->activate()', 1.8); + + $plugin = sanitise_string($plugin); + + $site_guid = (int) $site_guid; + if (!$site_guid) { + $site = get_config('site'); + $site_guid = $site->guid; + } + + try { + $plugin = new ElggPlugin($plugin); + } catch(Exception $e) { + return false; + } + + if (!$plugin->canActivate($site_guid)) { + return false; + } + + return $plugin->activate($site_guid); +} + +/** + * Disable a plugin for a site (default current site) + * + * Important: You should regenerate simplecache and the viewpath cache after executing this function + * otherwise you may experience view display artifacts. Do this with the following code: + * + * elgg_regenerate_simplecache(); + * elgg_reset_system_cache(); + * + * @deprecated 1.8 Use ElggPlugin->deactivate() + * + * @param string $plugin The plugin name. + * @param int $site_guid The site id, if not specified then this is detected. + * + * @return bool + * @throws InvalidClassException + */ +function disable_plugin($plugin, $site_guid = 0) { + elgg_deprecated_notice('disable_plugin() was deprecated by ElggPlugin->deactivate()', 1.8); + + $plugin = sanitise_string($plugin); + + $site_guid = (int) $site_guid; + if (!$site_guid) { + $site = get_config('site'); + $site_guid = $site->guid; + } + + try { + $plugin = new ElggPlugin($plugin); + } catch(Exception $e) { + return false; + } + + return $plugin->deactivate($site_guid); +} + +/** + * Return whether a plugin is enabled or not. + * + * @deprecated 1.8 Use elgg_is_active_plugin() + * + * @param string $plugin The plugin name. + * @param int $site_guid The site id, if not specified then this is detected. + * + * @return bool + */ +function is_plugin_enabled($plugin, $site_guid = 0) { + elgg_deprecated_notice('is_plugin_enabled() was deprecated by elgg_is_active_plugin()', 1.8); + return elgg_is_active_plugin($plugin, $site_guid); +} + +/** + * Get entities based on their private data. + * + * @param string $name The name of the setting + * @param string $value The value of the setting + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param string $order_by The field to order by; by default, time_created desc + * @param int $limit The number of entities to return; 10 by default + * @param int $offset The indexing offset, 0 by default + * @param boolean $count Return a count of entities + * @param int $site_guid The site to get entities for. 0 for current, -1 for any + * @param mixed $container_guid The container(s) GUIDs + * + * @return array A list of entities. + * @deprecated 1.8 Use elgg_get_entities_from_private_settings() + */ +function get_entities_from_private_setting($name = "", $value = "", $type = "", $subtype = "", +$owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, +$container_guid = null) { + elgg_deprecated_notice('get_entities_from_private_setting() was deprecated by elgg_get_entities_from_private_setting()!', 1.8); + + $options = array(); + + $options['private_setting_name'] = $name; + $options['private_setting_value'] = $value; + + // set container_guid to owner_guid to emulate old functionality + if ($owner_guid != "") { + if (is_null($container_guid)) { + $container_guid = $owner_guid; + } + } + + if ($type) { + $options['types'] = $type; + } + + if ($subtype) { + $options['subtypes'] = $subtype; + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + if ($container_guid) { + if (is_array($container_guid)) { + $options['container_guids'] = $container_guid; + } else { + $options['container_guid'] = $container_guid; + } + } + + $options['limit'] = $limit; + + if ($offset) { + $options['offset'] = $offset; + } + + if ($order_by) { + $options['order_by']; + } + + if ($site_guid) { + $options['site_guid']; + } + + if ($count) { + $options['count'] = $count; + } + + return elgg_get_entities_from_private_settings($options); +} + +/** + * Get entities based on their private data by multiple keys. + * + * @param string $name The name of the setting + * @param mixed $type Entity type + * @param string $subtype Entity subtype + * @param int $owner_guid The GUID of the owning user + * @param string $order_by The field to order by; by default, time_created desc + * @param int $limit The number of entities to return; 10 by default + * @param int $offset The indexing offset, 0 by default + * @param bool $count Count entities + * @param int $site_guid Site GUID. 0 for current, -1 for any. + * @param mixed $container_guid Container GUID + * + * @return array A list of entities. + * @deprecated 1.8 Use elgg_get_entities_from_private_settings() + */ +function get_entities_from_private_setting_multi(array $name, $type = "", $subtype = "", +$owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, +$site_guid = 0, $container_guid = null) { + + elgg_deprecated_notice('get_entities_from_private_setting_multi() was deprecated by elgg_get_entities_from_private_settings()!', 1.8); + + $options = array(); + + $pairs = array(); + foreach ($name as $setting_name => $setting_value) { + $pairs[] = array('name' => $setting_name, 'value' => $setting_value); + } + $options['private_setting_name_value_pairs'] = $pairs; + + // set container_guid to owner_guid to emulate old functionality + if ($owner_guid != "") { + if (is_null($container_guid)) { + $container_guid = $owner_guid; + } + } + + if ($type) { + $options['types'] = $type; + } + + if ($subtype) { + $options['subtypes'] = $subtype; + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + if ($container_guid) { + if (is_array($container_guid)) { + $options['container_guids'] = $container_guid; + } else { + $options['container_guid'] = $container_guid; + } + } + + $options['limit'] = $limit; + + if ($offset) { + $options['offset'] = $offset; + } + + if ($order_by) { + $options['order_by']; + } + + if ($site_guid) { + $options['site_guid']; + } + + if ($count) { + $options['count'] = $count; + } + + return elgg_get_entities_from_private_settings($options); +} + +/** + * Returns a viewable list of entities by relationship + * + * @see elgg_view_entity_list + * + * @deprecated 1.8 Use elgg_list_entities_from_relationship() + * + * @param string $relationship The relationship eg "friends_of" + * @param int $relationship_guid The guid of the entity to use query + * @param bool $inverse_relationship Reverse the normal function of the query to instead say "give me all entities for whome $relationship_guid is a $relationship of" + * @param string $type The type of entity (eg 'object') + * @param string $subtype The entity subtype + * @param int $owner_guid The owner (default: all) + * @param int $limit The number of entities to display on a page + * @param true|false $fullview Whether or not to display the full view (default: true) + * @param true|false $viewtypetoggle Whether or not to allow gallery view + * @param true|false $pagination Whether to display pagination (default: true) + * @param bool $order_by SQL order by clause + * @return string The viewable list of entities + */ +function list_entities_from_relationship($relationship, $relationship_guid, +$inverse_relationship = false, $type = ELGG_ENTITIES_ANY_VALUE, +$subtype = ELGG_ENTITIES_ANY_VALUE, $owner_guid = 0, $limit = 10, +$fullview = true, $listtypetoggle = false, $pagination = true, $order_by = '') { + + elgg_deprecated_notice("list_entities_from_relationship was deprecated by elgg_list_entities_from_relationship()!", 1.8); + return elgg_list_entities_from_relationship(array( + 'relationship' => $relationship, + 'relationship_guid' => $relationship_guid, + 'inverse_relationship' => $inverse_relationship, + 'type' => $type, + 'subtype' => $subtype, + 'owner_guid' => $owner_guid, + 'order_by' => $order_by, + 'limit' => $limit, + 'full_view' => $fullview, + 'list_type_toggle' => $listtypetoggle, + 'pagination' => $pagination, + )); +} + +/** + * Gets the number of entities by a the number of entities related to them in a particular way. + * This is a good way to get out the users with the most friends, or the groups with the + * most members. + * + * @deprecated 1.8 Use elgg_get_entities_from_relationship_count() + * + * @param string $relationship The relationship eg "friends_of" + * @param bool $inverse_relationship Inverse relationship owners + * @param string $type The type of entity (default: all) + * @param string $subtype The entity subtype (default: all) + * @param int $owner_guid The owner of the entities (default: none) + * @param int $limit Limit + * @param int $offset Offset + * @param bool $count Return a count instead of entities + * @param int $site_guid Site GUID + * + * @return array|int|false An array of entities, or the number of entities, or false on failure + */ +function get_entities_by_relationship_count($relationship, $inverse_relationship = true, $type = "", +$subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $count = false, $site_guid = 0) { + elgg_deprecated_notice('get_entities_by_relationship_count() is deprecated by elgg_get_entities_from_relationship_count()', 1.8); + + $options = array(); + + $options['relationship'] = $relationship; + + // this used to default to true, which is wrong. + // flip it for the new function + $options['inverse_relationship'] = !$inverse_relationship; + + if ($type) { + $options['types'] = $type; + } + + if ($subtype) { + $options['subtypes'] = $subtype; + } + + if ($owner_guid) { + $options['owner_guid'] = $owner_guid; + } + + $options['limit'] = $limit; + + if ($offset) { + $options['offset'] = $offset; + } + + if ($site_guid) { + $options['site_guid']; + } + + if ($count) { + $options['count'] = $count; + } + + return elgg_get_entities_from_relationship_count($options); +} + +/** + * Displays a human-readable list of entities + * + * @deprecated 1.8 Use elgg_list_entities_from_relationship_count() + * + * @param string $relationship The relationship eg "friends_of" + * @param bool $inverse_relationship Inverse relationship owners + * @param string $type The type of entity (eg 'object') + * @param string $subtype The entity subtype + * @param int $owner_guid The owner (default: all) + * @param int $limit The number of entities to display on a page + * @param bool $fullview Whether or not to display the full view (default: true) + * @param bool $listtypetoggle Whether or not to allow gallery view + * @param bool $pagination Whether to display pagination (default: true) + * + * @return string The viewable list of entities + */ +function list_entities_by_relationship_count($relationship, $inverse_relationship = true, +$type = "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, +$listtypetoggle = false, $pagination = true) { + + elgg_deprecated_notice('list_entities_by_relationship_count() was deprecated by elgg_list_entities_from_relationship_count()', 1.8); + + $options = array(); + + $options['relationship'] = $relationship; + + // this used to default to true, which is wrong. + // flip it for the new function + $options['inverse_relationship'] = !$inverse_relationship; + + if ($type) { + $options['types'] = $type; + } + + if ($subtype) { + $options['subtypes'] = $subtype; + } + + if ($owner_guid) { + $options['owner_guid'] = $owner_guid; + } + + $options['limit'] = $limit; + + $options['full_view'] = $fullview; + + return elgg_list_entities_from_relationship_count($options); +} + +/** + * Gets the number of entities by a the number of entities related to + * them in a particular way also constrained by metadata. + * + * @deprecated 1.8 Use elgg_get_entities_from_relationship() + * + * @param string $relationship The relationship eg "friends_of" + * @param int $relationship_guid The guid of the entity to use query + * @param bool $inverse_relationship Inverse relationship owner + * @param String $meta_name The metadata name + * @param String $meta_value The metadata value + * @param string $type The type of entity (default: all) + * @param string $subtype The entity subtype (default: all) + * @param int $owner_guid The owner of the entities (default: none) + * @param int $limit Limit + * @param int $offset Offset + * @param bool $count Return a count instead of entities + * @param int $site_guid Site GUID + * + * @return array|int|false An array of entities, or the number of entities, or false on failure + */ +function get_entities_from_relationships_and_meta($relationship, $relationship_guid, +$inverse_relationship = false, $meta_name = "", $meta_value = "", $type = "", +$subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $count = false, $site_guid = 0) { + + elgg_deprecated_notice('get_entities_from_relationship_and_meta() was deprecated by elgg_get_entities_from_relationship()!', 1.7); + + $options = array(); + + $options['relationship'] = $relationship; + $options['relationship_guid'] = $relationship_guid; + $options['inverse_relationship'] = $inverse_relationship; + + if ($meta_value) { + $options['values'] = $meta_value; + } + + if ($entity_type) { + $options['types'] = $entity_type; + } + + if ($type) { + $options['types'] = $type; + } + + if ($subtype) { + $options['subtypes'] = $subtype; + } + + if ($owner_guid) { + $options['owner_guid'] = $owner_guid; + } + + if ($limit) { + $options['limit'] = $limit; + } + + if ($offset) { + $options['offset'] = $offset; + } + + if ($order_by) { + $options['order_by']; + } + + if ($site_guid) { + $options['site_guid']; + } + + if ($count) { + $options['count'] = $count; + } + + return elgg_get_entities_from_relationship($options); +} + + +/** + * Retrieves items from the river. All parameters are optional. + * + * @param int|array $subject_guid Acting entity to restrict to. Default: all + * @param int|array $object_guid Entity being acted on to restrict to. Default: all + * @param string $subject_relationship If set to a relationship type, this will use + * $subject_guid as the starting point and set the + * subjects to be all users this + * entity has this relationship with (eg 'friend'). + * Default: blank + * @param string $type The type of entity to restrict to. Default: all + * @param string $subtype The subtype of entity to restrict to. Default: all + * @param string $action_type The type of river action to restrict to. Default: all + * @param int $limit The number of items to retrieve. Default: 20 + * @param int $offset The page offset. Default: 0 + * @param int $posted_min The minimum time period to look at. Default: none + * @param int $posted_max The maximum time period to look at. Default: none + * + * @return array|false Depending on success + * @deprecated 1.8 Use elgg_get_river() + */ +function get_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '', +$type = '', $subtype = '', $action_type = '', $limit = 20, $offset = 0, $posted_min = 0, +$posted_max = 0) { + elgg_deprecated_notice("get_river_items deprecated by elgg_get_river", 1.8); + + $options = array(); + + if ($subject_guid) { + $options['subject_guid'] = $subject_guid; + } + + if ($object_guid) { + $options['object_guid'] = $object_guid; + } + + if ($subject_relationship) { + $options['relationship'] = $subject_relationship; + unset($options['subject_guid']); + $options['relationship_guid'] = $subject_guid; + } + + if ($type) { + $options['type'] = $type; + } + + if ($subtype) { + $options['subtype'] = $subtype; + } + + if ($action_type) { + $options['action_type'] = $action_type; + } + + $options['limit'] = $limit; + $options['offset'] = $offset; + + if ($posted_min) { + $options['posted_time_lower'] = $posted_min; + } + + if ($posted_max) { + $options['posted_time_upper'] = $posted_max; + } + + return elgg_get_river($options); +} + +/** + * Returns a human-readable version of the river. + * + * @param int|array $subject_guid Acting entity to restrict to. Default: all + * @param int|array $object_guid Entity being acted on to restrict to. Default: all + * @param string $subject_relationship If set to a relationship type, this will use + * $subject_guid as the starting point and set + * the subjects to be all users this entity has this + * relationship with (eg 'friend'). Default: blank + * @param string $type The type of entity to restrict to. Default: all + * @param string $subtype The subtype of entity to restrict to. Default: all + * @param string $action_type The type of river action to restrict to. Default: all + * @param int $limit The number of items to retrieve. Default: 20 + * @param int $posted_min The minimum time period to look at. Default: none + * @param int $posted_max The maximum time period to look at. Default: none + * @param bool $pagination Show pagination? + * + * @return string Human-readable river. + * @deprecated 1.8 Use elgg_list_river() + */ +function elgg_view_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '', +$type = '', $subtype = '', $action_type = '', $limit = 20, $posted_min = 0, +$posted_max = 0, $pagination = true) { + elgg_deprecated_notice("elgg_view_river_items deprecated for elgg_list_river", 1.8); + + $river_items = get_river_items($subject_guid, $object_guid, $subject_relationship, + $type, $subtype, $action_type, $limit + 1, $posted_min, $posted_max); + + // Get input from outside world and sanitise it + $offset = (int) get_input('offset', 0); + + // view them + $params = array( + 'items' => $river_items, + 'count' => count($river_items), + 'offset' => $offset, + 'limit' => $limit, + 'pagination' => $pagination, + 'list-class' => 'elgg-list-river', + ); + + return elgg_view('page/components/list', $params); +} + +/** + * Construct and execute the query required for the activity stream. + * + * @deprecated 1.8 This is outdated and uses the systemlog table instead of the river table. + * Don't use it. + */ +function get_activity_stream_data($limit = 10, $offset = 0, $type = "", $subtype = "", +$owner_guid = "", $owner_relationship = "") { + elgg_deprecated_notice("get_activity_stream_data was deprecated", 1.8); + + global $CONFIG; + + $limit = (int)$limit; + $offset = (int)$offset; + + if ($type) { + if (!is_array($type)) { + $type = array(sanitise_string($type)); + } else { + foreach ($type as $k => $v) { + $type[$k] = sanitise_string($v); + } + } + } + + if ($subtype) { + if (!is_array($subtype)) { + $subtype = array(sanitise_string($subtype)); + } else { + foreach ($subtype as $k => $v) { + $subtype[$k] = sanitise_string($v); + } + } + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + foreach ($owner_guid as $k => $v) { + $owner_guid[$k] = (int)$v; + } + } else { + $owner_guid = array((int)$owner_guid); + } + } + + $owner_relationship = sanitise_string($owner_relationship); + + // Get a list of possible views + $activity_events = array(); + $activity_views = array_merge(elgg_view_tree('activity', 'default'), + elgg_view_tree('river', 'default')); + + $done = array(); + + foreach ($activity_views as $view) { + $fragments = explode('/', $view); + $tmp = explode('/', $view, 2); + $tmp = $tmp[1]; + + if ((isset($fragments[0])) && (($fragments[0] == 'river') || ($fragments[0] == 'activity')) + && (!in_array($tmp, $done))) { + + if (isset($fragments[1])) { + $f = array(); + for ($n = 1; $n < count($fragments); $n++) { + $val = sanitise_string($fragments[$n]); + switch($n) { + case 1: $key = 'type'; break; + case 2: $key = 'subtype'; break; + case 3: $key = 'event'; break; + } + $f[$key] = $val; + } + + // Filter result based on parameters + $add = true; + if ($type) { + if (!in_array($f['type'], $type)) { + $add = false; + } + } + if (($add) && ($subtype)) { + if (!in_array($f['subtype'], $subtype)) { + $add = false; + } + } + if (($add) && ($event)) { + if (!in_array($f['event'], $event)) { + $add = false; + } + } + + if ($add) { + $activity_events[] = $f; + } + } + + $done[] = $tmp; + } + } + + $n = 0; + foreach ($activity_events as $details) { + // Get what we're talking about + if ($details['subtype'] == 'default') { + $details['subtype'] = ''; + } + + if (($details['type']) && ($details['event'])) { + if ($n > 0) { + $obj_query .= " or "; + } + + $access = ""; + if ($details['type'] != 'relationship') { + $access = " and " . get_access_sql_suffix('sl'); + } + + $obj_query .= "( sl.object_type='{$details['type']}' + AND sl.object_subtype='{$details['subtype']}' + AND sl.event='{$details['event']}' $access )"; + + $n++; + } + } + + // User + if ((count($owner_guid)) && ($owner_guid[0] != 0)) { + $user = " and sl.performed_by_guid in (" . implode(',', $owner_guid) . ")"; + + if ($owner_relationship) { + $friendsarray = ""; + if ($friends = elgg_get_entities_from_relationship(array( + 'relationship' => $owner_relationship, + 'relationship_guid' => $owner_guid[0], + 'inverse_relationship' => FALSE, + 'type' => 'user', + 'subtype' => $subtype, + 'limit' => false)) + ) { + + $friendsarray = array(); + foreach ($friends as $friend) { + $friendsarray[] = $friend->getGUID(); + } + + $user = " and sl.performed_by_guid in (" . implode(',', $friendsarray) . ")"; + } + } + } + + $query = "SELECT sl.* FROM {$CONFIG->dbprefix}system_log sl + WHERE 1 $user AND ($obj_query) + ORDER BY sl.time_created desc limit $offset, $limit"; + return get_data($query); +} + +/** + * Perform standard authentication with a given username and password. + * Returns an ElggUser object for use with login. + * + * @see login + * + * @param string $username The username, optionally (for standard logins) + * @param string $password The password, optionally (for standard logins) + * + * @return ElggUser|false The authenticated user object, or false on failure. + * + * @deprecated 1.8 Use elgg_authenticate + */ +function authenticate($username, $password) { + elgg_deprecated_notice('authenticate() has been deprecated for elgg_authenticate()', 1.8); + $pam = new ElggPAM('user'); + $credentials = array('username' => $username, 'password' => $password); + $result = $pam->authenticate($credentials); + if ($result) { + return get_user_by_username($username); + } + return false; +} + + +/** + * Get the members of a site. + * + * @param int $site_guid Site GUID + * @param int $limit User GUID + * @param int $offset Offset + * + * @return mixed + * @deprecated 1.8 Use ElggSite::getMembers() + */ +function get_site_members($site_guid, $limit = 10, $offset = 0) { + elgg_deprecated_notice("get_site_members() deprecated. + Use ElggSite::getMembers()", 1.8); + + $site = get_entity($site_guid); + if ($site) { + return $site->getMembers($limit, $offset); + } + + return false; +} + +/** + * Display a list of site members + * + * @param int $site_guid The GUID of the site + * @param int $limit The number of members to display on a page + * @param bool $fullview Whether or not to display the full view (default: true) + * + * @return string A displayable list of members + * @deprecated 1.8 Use ElggSite::listMembers() + */ +function list_site_members($site_guid, $limit = 10, $fullview = true) { + elgg_deprecated_notice("list_site_members() deprecated. + Use ElggSite::listMembers()", 1.8); + + $options = array( + 'limit' => $limit, + 'full_view' => $full_view, + ); + + $site = get_entity($site_guid); + if ($site) { + return $site->listMembers($options); + } + + return ''; +} + + +/** + * Add a collection to a site. + * + * @param int $site_guid Site GUID + * @param int $collection_guid Collection GUID + * + * @return mixed + * @deprecated 1.8 Don't use this. + */ +function add_site_collection($site_guid, $collection_guid) { + elgg_deprecated_notice("add_site_collection has been deprecated", 1.8); + global $CONFIG; + + $site_guid = (int)$site_guid; + $collection_guid = (int)$collection_guid; + + return add_entity_relationship($collection_guid, "member_of_site", $site_guid); +} + +/** + * Remove a collection from a site. + * + * @param int $site_guid Site GUID + * @param int $collection_guid Collection GUID + * + * @return mixed + * @deprecated 1.8 Don't use this. + */ +function remove_site_collection($site_guid, $collection_guid) { + elgg_deprecated_notice("remove_site_collection has been deprecated", 1.8); + $site_guid = (int)$site_guid; + $collection_guid = (int)$collection_guid; + + return remove_entity_relationship($collection_guid, "member_of_site", $site_guid); +} + +/** + * Get the collections belonging to a site. + * + * @param int $site_guid Site GUID + * @param string $subtype Subtype + * @param int $limit Limit + * @param int $offset Offset + * + * @return mixed + * @deprecated 1.8 Don't use this. + */ +function get_site_collections($site_guid, $subtype = "", $limit = 10, $offset = 0) { + elgg_deprecated_notice("get_site_collections has been deprecated", 1.8); + $site_guid = (int)$site_guid; + $subtype = sanitise_string($subtype); + $limit = (int)$limit; + $offset = (int)$offset; + + // collection isn't a valid type. This won't work. + return elgg_get_entities_from_relationship(array( + 'relationship' => 'member_of_site', + 'relationship_guid' => $site_guid, + 'inverse_relationship' => TRUE, + 'type' => 'collection', + 'subtype' => $subtype, + 'limit' => $limit, + 'offset' => $offset + )); +} + +/** + * Get an array of tags with weights for use with the output/tagcloud view. + * + * @deprecated 1.8 Use elgg_get_tags(). + * + * @param int $threshold Get the threshold of minimum number of each tags to + * bother with (ie only show tags where there are more + * than $threshold occurances) + * @param int $limit Number of tags to return + * @param string $metadata_name Optionally, the name of the field you want to grab for + * @param string $entity_type Optionally, the entity type ('object' etc) + * @param string $entity_subtype The entity subtype, optionally + * @param int $owner_guid The GUID of the tags owner, optionally + * @param int $site_guid Optionally, the site to restrict to (default is the current site) + * @param int $start_ts Optionally specify a start timestamp for tags used to + * generate cloud. + * @param int $end_ts Optionally specify an end timestamp for tags used to generate cloud + * + * @return array|false Array of objects with ->tag and ->total values, or false on failure + */ +function get_tags($threshold = 1, $limit = 10, $metadata_name = "", $entity_type = "object", +$entity_subtype = "", $owner_guid = "", $site_guid = -1, $start_ts = "", $end_ts = "") { + + elgg_deprecated_notice('get_tags() has been replaced by elgg_get_tags()', 1.8); + + if (is_array($metadata_name)) { + return false; + } + + $options = array(); + if ($metadata_name === '') { + $options['tag_names'] = array(); + } else { + $options['tag_names'] = array($metadata_name); + } + + $options['threshold'] = $threshold; + $options['limit'] = $limit; + + // rewrite owner_guid to container_guid to emulate old functionality + $container_guid = $owner_guid; + if ($container_guid) { + $options['container_guids'] = $container_guid; + } + + if ($entity_type) { + $options['type'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtype'] = $entity_subtype; + } + + if ($site_guid != -1) { + $options['site_guids'] = $site_guid; + } + + if ($end_ts) { + $options['created_time_upper'] = $end_ts; + } + + if ($start_ts) { + $options['created_time_lower'] = $start_ts; + } + + $r = elgg_get_tags($options); + return $r; +} + +/** + * Loads and displays a tagcloud given particular criteria. + * + * @deprecated 1.8 use elgg_view_tagcloud() + * + * @param int $threshold Get the threshold of minimum number of each tags + * to bother with (ie only show tags where there are + * more than $threshold occurances) + * @param int $limit Number of tags to return + * @param string $metadata_name Optionally, the name of the field you want to grab for + * @param string $entity_type Optionally, the entity type ('object' etc) + * @param string $entity_subtype The entity subtype, optionally + * @param int $owner_guid The GUID of the tags owner, optionally + * @param int $site_guid Optionally, the site to restrict to (default is the current site) + * @param int $start_ts Optionally specify a start timestamp for tags used to + * generate cloud. + * @param int $end_ts Optionally specify an end timestamp for tags used to generate + * cloud. + * + * @return string The HTML (or other, depending on view type) of the tagcloud. + */ +function display_tagcloud($threshold = 1, $limit = 10, $metadata_name = "", $entity_type = "object", +$entity_subtype = "", $owner_guid = "", $site_guid = -1, $start_ts = "", $end_ts = "") { + + elgg_deprecated_notice('display_tagcloud() was deprecated by elgg_view_tagcloud()!', 1.8); + + $tags = get_tags($threshold, $limit, $metadata_name, $entity_type, + $entity_subtype, $owner_guid, $site_guid, $start_ts, $end_ts); + + return elgg_view('output/tagcloud', array( + 'value' => $tags, + 'type' => $entity_type, + 'subtype' => $entity_subtype, + )); +} + + +/** + * Obtains a list of objects owned by a user + * + * @param int $user_guid The GUID of the owning user + * @param string $subtype Optionally, the subtype of objects + * @param int $limit The number of results to return (default 10) + * @param int $offset Indexing offset, if any + * @param int $timelower The earliest time the entity can have been created. Default: all + * @param int $timeupper The latest time the entity can have been created. Default: all + * + * @return false|array An array of ElggObjects or false, depending on success + * @deprecated 1.8 Use elgg_get_entities() instead + */ +function get_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, +$offset = 0, $timelower = 0, $timeupper = 0) { + elgg_deprecated_notice("get_user_objects() was deprecated in favor of elgg_get_entities()", 1.8); + $ntt = elgg_get_entities(array( + 'type' => 'object', + 'subtype' => $subtype, + 'owner_guid' => $user_guid, + 'limit' => $limit, + 'offset' => $offset, + 'container_guid' => $user_guid, + 'created_time_lower' => $timelower, + 'created_time_upper' => $timeupper + )); + return $ntt; +} + +/** + * Counts the objects (optionally of a particular subtype) owned by a user + * + * @param int $user_guid The GUID of the owning user + * @param string $subtype Optionally, the subtype of objects + * @param int $timelower The earliest time the entity can have been created. Default: all + * @param int $timeupper The latest time the entity can have been created. Default: all + * + * @return int The number of objects the user owns (of this subtype) + * @deprecated 1.8 Use elgg_get_entities() instead + */ +function count_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $timelower = 0, +$timeupper = 0) { + elgg_deprecated_notice("count_user_objects() was deprecated in favor of elgg_get_entities()", 1.8); + $total = elgg_get_entities(array( + 'type' => 'object', + 'subtype' => $subtype, + 'owner_guid' => $user_guid, + 'count' => TRUE, + 'container_guid' => $user_guid, + 'created_time_lower' => $timelower, + 'created_time_upper' => $timeupper + )); + return $total; +} + +/** + * Displays a list of user objects of a particular subtype, with navigation. + * + * @see elgg_view_entity_list + * + * @param int $user_guid The GUID of the user + * @param string $subtype The object subtype + * @param int $limit The number of entities to display on a page + * @param bool $fullview Whether or not to display the full view (default: true) + * @param bool $listtypetoggle Whether or not to allow gallery view (default: true) + * @param bool $pagination Whether to display pagination (default: true) + * @param int $timelower The earliest time the entity can have been created. Default: all + * @param int $timeupper The latest time the entity can have been created. Default: all + * + * @return string The list in a form suitable to display + * @deprecated 1.8 Use elgg_list_entities() instead + */ +function list_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, +$fullview = true, $listtypetoggle = true, $pagination = true, $timelower = 0, $timeupper = 0) { + elgg_deprecated_notice("list_user_objects() was deprecated in favor of elgg_list_entities()", 1.8); + + $offset = (int) get_input('offset'); + $limit = (int) $limit; + $count = (int) count_user_objects($user_guid, $subtype, $timelower, $timeupper); + $entities = get_user_objects($user_guid, $subtype, $limit, $offset, $timelower, $timeupper); + + return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $listtypetoggle, + $pagination); +} + + +/** + * Get user objects by an array of metadata + * + * @param int $user_guid The GUID of the owning user + * @param string $subtype Optionally, the subtype of objects + * @param array $metadata An array of metadata + * @param int $limit The number of results to return (default 10) + * @param int $offset Indexing offset, if any + * + * @return false|array An array of ElggObjects or false, depending on success + * @deprecated 1.8 Use elgg_get_entities_from_metadata() instead + */ +function get_user_objects_by_metadata($user_guid, $subtype = "", $metadata = array(), +$limit = 0, $offset = 0) { + elgg_deprecated_notice("get_user_objects_by_metadata() was deprecated in favor of elgg_get_entities_from_metadata()", 1.8); + return get_entities_from_metadata_multi($metadata, "object", $subtype, $user_guid, + $limit, $offset); +} + +/** + * Set the validation status for a user. + * + * @param bool $status Validated (true) or false + * @param string $method Optional method to say how a user was validated + * @return bool + * @deprecated 1.8 Use elgg_set_user_validation_status() + */ +function set_user_validation_status($user_guid, $status, $method = '') { + elgg_deprecated_notice("set_user_validation_status() is deprecated", 1.8); + return elgg_set_user_validation_status($user_guid, $status, $method); +} + +/** + * Trigger an event requesting that a user guid be validated somehow - either by email address or some other way. + * + * This function invalidates any existing validation value. + * + * @param int $user_guid User's GUID + * @deprecated 1.8 Hook into the register, user plugin hook and request validation. + */ +function request_user_validation($user_guid) { + elgg_deprecated_notice("request_user_validation() is deprecated. + Plugins should register for the 'register, user' plugin hook", 1.8); + $user = get_entity($user_guid); + + if (($user) && ($user instanceof ElggUser)) { + // invalidate any existing validations + set_user_validation_status($user_guid, false); + + // request validation + trigger_elgg_event('validate', 'user', $user); + } +} + +/** + * Register a user settings page with the admin panel. + * This function extends the view "usersettings/main" with the provided view. + * This view should provide a description and either a control or a link to. + * + * Usage: + * - To add a control to the main admin panel then extend usersettings/main + * - To add a control to a new page create a page which renders a view + * usersettings/subpage (where subpage is your new page - + * nb. some pages already exist that you can extend), extend the main view + * to point to it, and add controls to your new view. + * + * At the moment this is essentially a wrapper around elgg_extend_view(). + * + * @param string $new_settings_view The view associated with the control you're adding + * @param string $view The view to extend, by default this is 'usersettings/main'. + * @param int $priority Optional priority to govern the appearance in the list. + * + * @return bool + * @deprecated 1.8 Extend one of the views in core/settings + */ +function extend_elgg_settings_page($new_settings_view, $view = 'usersettings/main', +$priority = 500) { + // see views: /core/settings + elgg_deprecated_notice("extend_elgg_settings_page has been deprecated. Extend one of the settings views instead", 1.8); + + return elgg_extend_view($view, $new_settings_view, $priority); +} + +/** + * Returns a representation of a full 'page' (which might be an HTML page, + * RSS file, etc, depending on the current viewtype) + * + * @param string $title + * @param string $body + * @return string + * + * @deprecated 1.8 Use elgg_view_page() + */ +function page_draw($title, $body, $sidebar = "") { + elgg_deprecated_notice("page_draw() was deprecated in favor of elgg_view_page() in 1.8.", 1.8); + + $vars = array( + 'sidebar' => $sidebar + ); + echo elgg_view_page($title, $body, 'default', $vars); +} + +/** + * Wrapper function to display search listings. + * + * @param string $icon The icon for the listing + * @param string $info Any information that needs to be displayed. + * + * @return string The HTML (etc) representing the listing + * @deprecated 1.8 use elgg_view_image_block() + */ +function elgg_view_listing($icon, $info) { + elgg_deprecated_notice('elgg_view_listing deprecated by elgg_view_image_block', 1.8); + return elgg_view('page/components/image_block', array('image' => $icon, 'body' => $info)); +} + +/** + * Return the icon URL for an entity. + * + * @tip Can be overridden by registering a plugin hook for entity:icon:url, $entity_type. + * + * @internal This is passed an entity rather than a guid to handle non-created entities. + * + * @param ElggEntity $entity The entity + * @param string $size Icon size + * + * @return string URL to the entity icon. + * @deprecated 1.8 Use $entity->getIconURL() + */ +function get_entity_icon_url(ElggEntity $entity, $size = 'medium') { + elgg_deprecated_notice("get_entity_icon_url() deprecated for getIconURL()", 1.8); + global $CONFIG; + + $size = sanitise_string($size); + switch (strtolower($size)) { + case 'master': + $size = 'master'; + break; + + case 'large' : + $size = 'large'; + break; + + case 'topbar' : + $size = 'topbar'; + break; + + case 'tiny' : + $size = 'tiny'; + break; + + case 'small' : + $size = 'small'; + break; + + case 'medium' : + default: + $size = 'medium'; + } + + $url = false; + + $viewtype = elgg_get_viewtype(); + + // Step one, see if anyone knows how to render this in the current view + $params = array('entity' => $entity, 'viewtype' => $viewtype, 'size' => $size); + $url = elgg_trigger_plugin_hook('entity:icon:url', $entity->getType(), $params, $url); + + // Fail, so use default + if (!$url) { + $type = $entity->getType(); + $subtype = $entity->getSubtype(); + + if (!empty($subtype)) { + $overrideurl = elgg_view("icon/{$type}/{$subtype}/{$size}", array('entity' => $entity)); + if (!empty($overrideurl)) { + return $overrideurl; + } + } + + $overrideurl = elgg_view("icon/{$type}/default/{$size}", array('entity' => $entity)); + if (!empty($overrideurl)) { + return $overrideurl; + } + + $url = "_graphics/icons/default/$size.png"; + } + + return elgg_normalize_url($url); +} + +/** + * Return the current logged in user, or NULL if no user is logged in. + * + * If no user can be found in the current session, a plugin + * hook - 'session:get' 'user' to give plugin authors another + * way to provide user details to the ACL system without touching the session. + * + * @deprecated 1.8 Use elgg_get_logged_in_user_entity() + * @return ElggUser|NULL + */ +function get_loggedin_user() { + elgg_deprecated_notice('get_loggedin_user() is deprecated by elgg_get_logged_in_user_entity()', 1.8); + return elgg_get_logged_in_user_entity(); +} + +/** + * Return the current logged in user by id. + * + * @deprecated 1.8 Use elgg_get_logged_in_user_guid() + * @see elgg_get_logged_in_user_entity() + * @return int + */ +function get_loggedin_userid() { + elgg_deprecated_notice('get_loggedin_userid() is deprecated by elgg_get_logged_in_user_guid()', 1.8); + return elgg_get_logged_in_user_guid(); +} + + +/** + * Returns whether or not the user is currently logged in + * + * @deprecated 1.8 Use elgg_is_logged_in(); + * @return bool + */ +function isloggedin() { + elgg_deprecated_notice('isloggedin() is deprecated by elgg_is_logged_in()', 1.8); + return elgg_is_logged_in(); +} + +/** + * Returns whether or not the user is currently logged in and that they are an admin user. + * + * @deprecated 1.8 Use elgg_is_admin_logged_in() + * @return bool + */ +function isadminloggedin() { + elgg_deprecated_notice('isadminloggedin() is deprecated by elgg_is_admin_logged_in()', 1.8); + return elgg_is_admin_logged_in(); +} + + +/** + * Loads plugins + * + * @deprecated 1.8 Use elgg_load_plugins() + * + * @return bool + */ +function load_plugins() { + elgg_deprecated_notice('load_plugins() is deprecated by elgg_load_plugins()', 1.8); + return elgg_load_plugins(); +} + +/** + * Find the plugin settings for a user. + * + * @param string $plugin_id Plugin name. + * @param int $user_guid The guid who's settings to retrieve. + * + * @deprecated 1.8 Use elgg_get_all_plugin_user_settings() or ElggPlugin->getAllUserSettings() + * @return StdClass Object with all user settings. + */ +function find_plugin_usersettings($plugin_id = null, $user_guid = 0) { + elgg_deprecated_notice('find_plugin_usersettings() is deprecated by elgg_get_all_plugin_user_settings()', 1.8); + return elgg_get_all_plugin_user_settings($user_guid, $plugin_id, true); +} + +/** + * Set a user specific setting for a plugin. + * + * @param string $name The name - note, can't be "title". + * @param mixed $value The value. + * @param int $user_guid Optional user. + * @param string $plugin_id Optional plugin name, if not specified then it + * is detected from where you are calling from. + * + * @return bool + * @deprecated 1.8 Use elgg_set_plugin_user_setting() or ElggPlugin->setUserSetting() + */ +function set_plugin_usersetting($name, $value, $user_guid = 0, $plugin_id = "") { + elgg_deprecated_notice('find_plugin_usersettings() is deprecated by elgg_get_all_plugin_user_settings()', 1.8); + return elgg_set_plugin_user_setting($name, $value, $user_guid, $plugin_id); +} + +/** + * Clears a user-specific plugin setting + * + * @param str $name Name of the plugin setting + * @param int $user_guid Defaults to logged in user + * @param str $plugin_id Defaults to contextual plugin name + * + * @deprecated 1.8 Use elgg_unset_plugin_user_setting or ElggPlugin->unsetUserSetting(). + * @return bool Success + */ +function clear_plugin_usersetting($name, $user_guid = 0, $plugin_id = '') { + elgg_deprecated_notice('clear_plugin_usersetting() is deprecated by elgg_unset_plugin_usersetting()', 1.8); + return elgg_unset_plugin_user_setting($name, $user_guid, $plugin_id); +} + +/** + * Get a user specific setting for a plugin. + * + * @param string $name The name. + * @param int $user_guid Guid of owning user + * @param string $plugin_id Optional plugin name, if not specified + * it is detected from where you are calling. + * + * @deprecated 1.8 Use elgg_get_plugin_user_setting() or ElggPlugin->getUserSetting() + * @return mixed + */ +function get_plugin_usersetting($name, $user_guid = 0, $plugin_id = "") { + elgg_deprecated_notice('get_plugin_usersetting() is deprecated by elgg_get_plugin_user_setting()', 1.8); + return elgg_get_plugin_user_setting($name, $user_guid, $plugin_id); +} + +/** + * Set a setting for a plugin. + * + * @param string $name The name - note, can't be "title". + * @param mixed $value The value. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. + * + * @deprecated 1.8 Use elgg_set_plugin_setting() or ElggPlugin->setSetting() + * @return int|false + */ +function set_plugin_setting($name, $value, $plugin_id = null) { + elgg_deprecated_notice('set_plugin_setting() is deprecated by elgg_set_plugin_setting()', 1.8); + return elgg_set_plugin_setting($name, $value, $plugin_id); +} + +/** + * Get setting for a plugin. + * + * @param string $name The name. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. + * + * @deprecated 1.8 Use elgg_get_plugin_setting() or ElggPlugin->getSetting() + * @return mixed + */ +function get_plugin_setting($name, $plugin_id = "") { + elgg_deprecated_notice('get_plugin_setting() is deprecated by elgg_get_plugin_setting()', 1.8); + return elgg_get_plugin_setting($name, $plugin_id); +} + +/** + * Clear a plugin setting. + * + * @param string $name The name. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. + * + * @deprecated 1.8 Use elgg_unset_plugin_setting() or ElggPlugin->unsetSetting() + * @return bool + */ +function clear_plugin_setting($name, $plugin_id = "") { + elgg_deprecated_notice('clear_plugin_setting() is deprecated by elgg_unset_plugin_setting()', 1.8); + return elgg_unset_plugin_setting($name, $plugin_id); +} + +/** + * Unsets all plugin settings for a plugin. + * + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. + * + * @return bool + * @deprecated 1.8 Use elgg_unset_all_plugin_settings() or ElggPlugin->unsetAllSettings() + * @since 1.7.0 + */ +function clear_all_plugin_settings($plugin_id = "") { + elgg_deprecated_notice('clear_all_plugin_settings() is deprecated by elgg_unset_all_plugin_setting()', 1.8); + return elgg_unset_all_plugin_settings($plugin_id); +} + + +/** + * Get a list of annotations for a given object/user/annotation type. + * + * @param int|array $entity_guid GUID to return annotations of (falsey for any) + * @param string $entity_type Type of entity + * @param string $entity_subtype Subtype of entity + * @param string $name Name of annotation + * @param mixed $value Value of annotation + * @param int|array $owner_guid Owner(s) of annotation + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by Order annotations by SQL + * @param int $timelower Lower time limit + * @param int $timeupper Upper time limit + * @param int $entity_owner_guid Owner guid for the entity + * + * @return array + * @deprecated 1.8 Use elgg_get_annotations() + */ +function get_annotations($entity_guid = 0, $entity_type = "", $entity_subtype = "", $name = "", +$value = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "asc", $timelower = 0, +$timeupper = 0, $entity_owner_guid = 0) { + + elgg_deprecated_notice('get_annotations() is deprecated by elgg_get_annotations()', 1.8); + $options = array(); + + if ($entity_guid) { + $options['guid'] = $entity_guid; + } + + if ($entity_type) { + $options['type'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtype'] = $entity_subtype; + } + + if ($name) { + $options['annotation_name'] = $name; + } + + if ($value) { + $options['annotation_value'] = $value; + } + + if ($owner_guid) { + $options['annotation_owner_guid'] = $owner_guid; + } + + $options['limit'] = $limit; + $options['offset'] = $offset; + + if ($order_by == 'desc') { + $options['order_by'] = 'n_table.time_created desc'; + } + + if ($timelower) { + $options['annotation_time_lower'] = $timelower; + } + + if ($timeupper) { + $options['annotation_time_upper'] = $timeupper; + } + + if ($entity_owner_guid) { + $options['owner_guid'] = $entity_owner_guid; + } + + return elgg_get_annotations($options); +} + + +/** + * Returns a human-readable list of annotations on a particular entity. + * + * @param int $entity_guid The entity GUID + * @param string $name The name of the kind of annotation + * @param int $limit The number of annotations to display at once + * @param true|false $asc Display annotations in ascending order. (Default: true) + * + * @return string HTML (etc) version of the annotation list + * @deprecated 1.8 Use elgg_list_annotations() + */ +function list_annotations($entity_guid, $name = "", $limit = 25, $asc = true) { + elgg_deprecated_notice('list_annotations() is deprecated by elgg_list_annotations()', 1.8); + + if ($asc) { + $asc = "asc"; + } else { + $asc = "desc"; + } + + $options = array( + 'guid' => $entity_guid, + 'limit' => $limit, + 'order_by' => "n_table.time_created $asc" + ); + + return elgg_list_annotations($options); +} + +/** + * Helper function to deprecate annotation calculation functions. Don't use. + * + * @param unknown_type $entity_guid + * @param unknown_type $entity_type + * @param unknown_type $entity_subtype + * @param unknown_type $name + * @param unknown_type $value + * @param unknown_type $value_type + * @param unknown_type $owner_guid + * @param unknown_type $timelower + * @param unknown_type $timeupper + * @param unknown_type $calculation + * @internal Don't use this at all. + * @deprecated 1.8 Use elgg_get_annotations() + */ +function elgg_deprecated_annotation_calculation($entity_guid = 0, $entity_type = "", $entity_subtype = "", +$name = "", $value = "", $value_type = "", $owner_guid = 0, $timelower = 0, +$timeupper = 0, $calculation = '') { + + $options = array('annotation_calculation' => $calculation); + + if ($entity_guid) { + $options['guid'] = $entity_guid; + } + + if ($entity_type) { + $options['type'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtype'] = $entity_subtype; + } + + if ($name) { + $options['annotation_name'] = $name; + } + + if ($value) { + $options['annotation_value'] = $value; + } + + if ($owner_guid) { + $options['annotation_owner_guid'] = $owner_guid; + } + + if ($order_by == 'desc') { + $options['order_by'] = 'n_table.time_created desc'; + } + + if ($timelower) { + $options['annotation_time_lower'] = $timelower; + } + + if ($timeupper) { + $options['annotation_time_upper'] = $timeupper; + } + + return elgg_get_annotations($options); +} + +/** + * Count the number of annotations based on search parameters + * + * @param int $entity_guid Guid of Entity + * @param string $entity_type Type of Entity + * @param string $entity_subtype Subtype of Entity + * @param string $name Name of annotation + * @param string $value Value of annotation + * @param string $value_type Type of value + * @param int $owner_guid GUID of owner of annotation + * @param int $timelower Lower time limit + * @param int $timeupper Upper time limit + * + * @deprecated 1.8 Use elgg_get_annotations() and pass 'count' => true + * @return int + */ +function count_annotations($entity_guid = 0, $entity_type = "", $entity_subtype = "", +$name = "", $value = "", $value_type = "", $owner_guid = 0, $timelower = 0, +$timeupper = 0) { + elgg_deprecated_notice('count_annotations() is deprecated by elgg_get_annotations() and passing "count" => true', 1.8); + return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype, + $name, $value, $value_type, $owner_guid, $timelower, $timeupper, 'count'); +} + +/** + * Return the sum of a given integer annotation. + * + * @param int $entity_guid Guid of Entity + * @param string $entity_type Type of Entity + * @param string $entity_subtype Subtype of Entity + * @param string $name Name of annotation + * @param string $value Value of annotation + * @param string $value_type Type of value + * @param int $owner_guid GUID of owner of annotation + * + * @deprecated 1.8 Use elgg_get_annotations() and pass 'annotation_calculation' => 'sum' + * @return int + */ +function get_annotations_sum($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", +$value = "", $value_type = "", $owner_guid = 0) { + elgg_deprecated_notice('get_annotations_sum() is deprecated by elgg_get_annotations() and passing "annotation_calculation" => "sum"', 1.8); + + return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype, + $name, $value, $value_type, $owner_guid, $timelower, $timeupper, 'sum'); +} + +/** + * Return the max of a given integer annotation. + * + * @param int $entity_guid Guid of Entity + * @param string $entity_type Type of Entity + * @param string $entity_subtype Subtype of Entity + * @param string $name Name of annotation + * @param string $value Value of annotation + * @param string $value_type Type of value + * @param int $owner_guid GUID of owner of annotation + * + * @deprecated 1.8 Use elgg_get_annotations() and pass 'annotation_calculation' => 'max' + * @return int + */ +function get_annotations_max($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", +$value = "", $value_type = "", $owner_guid = 0) { + elgg_deprecated_notice('get_annotations_max() is deprecated by elgg_get_annotations() and passing "annotation_calculation" => "max"', 1.8); + + return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype, + $name, $value, $value_type, $owner_guid, $timelower, $timeupper, 'max'); +} + + +/** + * Return the minumum of a given integer annotation. + * + * @param int $entity_guid Guid of Entity + * @param string $entity_type Type of Entity + * @param string $entity_subtype Subtype of Entity + * @param string $name Name of annotation + * @param string $value Value of annotation + * @param string $value_type Type of value + * @param int $owner_guid GUID of owner of annotation + * + * @deprecated 1.8 Use elgg_get_annotations() and pass 'annotation_calculation' => 'min' + * @return int + */ +function get_annotations_min($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", +$value = "", $value_type = "", $owner_guid = 0) { + elgg_deprecated_notice('get_annotations_min() is deprecated by elgg_get_annotations() and passing "annotation_calculation" => "min"', 1.8); + + return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype, + $name, $value, $value_type, $owner_guid, $timelower, $timeupper, 'min'); +} + + +/** + * Return the average of a given integer annotation. + * + * @param int $entity_guid Guid of Entity + * @param string $entity_type Type of Entity + * @param string $entity_subtype Subtype of Entity + * @param string $name Name of annotation + * @param string $value Value of annotation + * @param string $value_type Type of value + * @param int $owner_guid GUID of owner of annotation + * + * @deprecated 1.8 Use elgg_get_annotations() and pass 'annotation_calculation' => 'min' + * + * @return int + */ +function get_annotations_avg($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", +$value = "", $value_type = "", $owner_guid = 0) { + elgg_deprecated_notice('get_annotations_avg() is deprecated by elgg_get_annotations() and passing "annotation_calculation" => "avg"', 1.8); + + return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype, + $name, $value, $value_type, $owner_guid, $timelower, $timeupper, 'avg'); +} + + +/** + * Perform a mathmatical calculation on integer annotations. + * + * @param string $sum What sort of calculation to perform + * @param int $entity_guid Guid of Entity + * @param string $entity_type Type of Entity + * @param string $entity_subtype Subtype of Entity + * @param string $name Name of annotation + * @param string $value Value of annotation + * @param string $value_type Type of value + * @param int $owner_guid GUID of owner of annotation + * @param int $timelower Lower time limit + * @param int $timeupper Upper time limit + * + * @return int + * @deprecated 1.8 Use elgg_get_annotations() and pass anntoation_calculation => <calculation> + */ +function get_annotations_calculate_x($sum = "avg", $entity_guid, $entity_type = "", +$entity_subtype = "", $name = "", $value = "", $value_type = "", $owner_guid = 0, +$timelower = 0, $timeupper = 0) { + elgg_deprecated_notice('get_annotations_calculate_x() is deprecated by elgg_get_annotations() and passing "annotation_calculation" => "calculation"', 1.8); + + return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype, + $name, $value, $value_type, $owner_guid, $timelower, $timeupper, $sum); +} + + +/** + * Lists entities by the totals of a particular kind of annotation AND + * the value of a piece of metadata + * + * @param string $entity_type Type of entity. + * @param string $entity_subtype Subtype of entity. + * @param string $name Name of annotation. + * @param string $mdname Metadata name + * @param string $mdvalue Metadata value + * @param int $limit Maximum number of results to return. + * @param int $owner_guid Owner. + * @param int $group_guid Group container. Currently only supported if entity_type is object + * @param boolean $asc Whether to list in ascending or descending order (default: desc) + * @param boolean $fullview Whether to display the entities in full + * @param boolean $listtypetoggle Can the 'gallery' view can be displayed (default: no) + * @param boolean $pagination Display pagination + * @param string $orderdir 'desc' or 'asc' + * + * @deprecated 1.8 Use elgg_list_entities_from_annotation_calculation(). + * + * @return string Formatted entity list + */ +function list_entities_from_annotation_count_by_metadata($entity_type = "", $entity_subtype = "", +$name = "", $mdname = '', $mdvalue = '', $limit = 10, $owner_guid = 0, $group_guid = 0, +$asc = false, $fullview = true, $listtypetoggle = false, $pagination = true, $orderdir = 'desc') { + + $msg = 'list_entities_from_annotation_count_by_metadata() is deprecated by elgg_list_entities_from_annotation_calculation().'; + + elgg_deprecated_notice($msg, 1.8); + + $options = array(); + + $options['calculation'] = 'sum'; + + if ($entity_type) { + $options['types'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtypes'] = $entity_subtype; + } + + $options['annotation_names'] = $name; + + if ($mdname) { + $options['metadata_name'] = $mdname; + } + + if ($mdvalue) { + $options['metadata_value'] = $mdvalue; + } + + if ($owner_guid) { + if (is_array($owner_guid)) { + $options['owner_guids'] = $owner_guid; + } else { + $options['owner_guid'] = $owner_guid; + } + } + + $options['full_view'] = $fullview; + + $options['list_type_toggle'] = $listtypetoggle; + + $options['pagination'] = $pagination; + + $options['limit'] = $limit; + + $options['order_by'] = "annotation_calculation $orderdir"; + + return elgg_get_entities_from_annotation_calculation($options); +} + +/** + * Set an alternative base location for a view (as opposed to the default of $CONFIG->viewpath) + * + * @param string $view The name of the view + * @param string $location The base location path + * + * @deprecated 1.8 Use elgg_set_view_location() + */ +function set_view_location($view, $location, $viewtype = '') { + elgg_deprecated_notice("set_view_location() was deprecated by elgg_set_view_location()", 1.8); + return elgg_set_view_location($view, $location, $viewtype); +} + +/** + * Sets the URL handler for a particular entity type and subtype + * + * @param string $function_name The function to register + * @param string $entity_type The entity type + * @param string $entity_subtype The entity subtype + * @return true|false Depending on success + * + * @deprecated 1.8 Use elgg_register_entity_url_handler() + */ +function register_entity_url_handler($function_name, $entity_type = "all", $entity_subtype = "all") { + elgg_deprecated_notice("register_entity_url_handler() was deprecated by elgg_register_entity_url_handler()", 1.8); + return elgg_register_entity_url_handler($entity_type, $entity_subtype, $function_name); +} + + +/** + * Get the metadata where the entities they are referring to match a given criteria. + * + * @param mixed $meta_name Metadata name + * @param mixed $meta_value Metadata value + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity. + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by Optional ordering. + * @param int $site_guid Site GUID. 0 for current, -1 for any + * + * @return mixed + * @deprecated 1.8 Use elgg_get_metadata() + */ +function find_metadata($meta_name = "", $meta_value = "", $entity_type = "", $entity_subtype = "", + $limit = 10, $offset = 0, $order_by = "", $site_guid = 0) { + + elgg_deprecated_notice('get_metadata() is deprecated by elgg_get_metadata()', 1.8); + + $options = array(); + + if ($meta_name) { + $options['annotation_name'] = $meta_name; + } + + if ($meta_value) { + $options['annotation_value'] = $meta_value; + } + + if ($entity_type) { + $options['type'] = $entity_type; + } + + if ($entity_subtype) { + $options['subtype'] = $entity_subtype; + } + + $options['limit'] = $limit; + $options['offset'] = $offset; + + if ($order_by == 'desc') { + $options['order_by'] = 'n_table.time_created desc'; + } + + if ($site_guid) { + $options['site_guid'] = $site_guid; + } + + return elgg_get_metadata($options); +} + +/** + * Get metadata objects by name. + * + * @param int $entity_guid Entity GUID + * @param string $meta_name Metadata name + * + * @return mixed ElggMetadata object, an array of ElggMetadata or false. + * @deprecated 1.8 Use elgg_get_metadata() + */ +function get_metadata_byname($entity_guid, $meta_name) { + elgg_deprecated_notice('get_metadata_byname() is deprecated by elgg_get_metadata()', 1.8); + + if (!$entity_guid || !$meta_name) { + return false; + } + + $options = array( + 'guid' => $entity_guid, + 'metadata_name' => $meta_name, + 'limit' => 0 + ); + + $md = elgg_get_metadata($options); + + if ($md && count($md) == 1) { + return $md[0]; + } + + return $md; +} + +/** + * Return all the metadata for a given GUID. + * + * @param int $entity_guid Entity GUID + * + * @return mixed + * @deprecated 1.8 Use elgg_get_metadata() + */ +function get_metadata_for_entity($entity_guid) { + elgg_deprecated_notice('get_metadata_for_entity() is deprecated by elgg_get_metadata()', 1.8); + + if (!$entity_guid) { + return false; + } + + $options = array( + 'guid' => $entity_guid, + 'limit' => 0 + ); + + return elgg_get_metadata($options); +} + +/** + * Get a specific metadata object. + * + * @param int $id The id of the metadata being retrieved. + * + * @return mixed False on failure or ElggMetadata + * @deprecated 1.8 Use elgg_get_metadata_from_id() + */ +function get_metadata($id) { + elgg_deprecated_notice('get_metadata() is deprecated by elgg_get_metadata_from_id()', 1.8); + return elgg_get_metadata_from_id($id); +} + +/** + * Clear all the metadata for a given entity, assuming you have access to that entity. + * + * @param int $guid Entity GUID + * + * @return bool + * @deprecated 1.8 Use elgg_delete_metadata() + */ +function clear_metadata($guid) { + elgg_deprecated_notice('clear_metadata() is deprecated by elgg_delete_metadata()', 1.8); + if (!$guid) { + return false; + } + return elgg_delete_metadata(array('guid' => $guid, 'limit' => 0)); +} + +/** + * Clear all metadata belonging to a given owner_guid + * + * @param int $owner_guid The owner + * + * @return bool + * @deprecated 1.8 Use elgg_delete_metadata() + */ +function clear_metadata_by_owner($owner_guid) { + elgg_deprecated_notice('clear_metadata() is deprecated by elgg_delete_metadata()', 1.8); + if (!$owner_guid) { + return false; + } + return elgg_delete_metadata(array('metadata_owner_guid' => $owner_guid, 'limit' => 0)); +} + +/** + * Delete a piece of metadata, where the current user has access. + * + * @param int $id The id of metadata to delete. + * + * @return bool + * @deprecated 1.8 Use elgg_delete_metadata() + */ +function delete_metadata($id) { + elgg_deprecated_notice('delete_metadata() is deprecated by elgg_delete_metadata()', 1.8); + if (!$id) { + return false; + } + return elgg_delete_metadata(array('metadata_id' => $id)); +} + +/** + * Removes metadata on an entity with a particular name, optionally with a given value. + * + * @param int $guid The entity GUID + * @param string $name The name of the metadata + * @param string $value The value of the metadata (useful to remove a single item of a set) + * + * @return bool Depending on success + * @deprecated 1.8 Use elgg_delete_metadata() + */ +function remove_metadata($guid, $name, $value = "") { + elgg_deprecated_notice('delete_metadata() is deprecated by elgg_delete_metadata()', 1.8); + + // prevent them from deleting everything + if (!$guid) { + return false; + } + + $options = array( + 'guid' => $guid, + 'metadata_name' => $name, + 'limit' => 0 + ); + + if ($value) { + $options['metadata_value'] = $value; + } + + return elgg_delete_metadata($options); +} + +/** + * Get a specific annotation. + * + * @param int $annotation_id Annotation ID + * + * @return ElggAnnotation + * @deprecated 1.8 Use elgg_get_annotation_from_id() + */ +function get_annotation($annotation_id) { + elgg_deprecated_notice('get_annotation() is deprecated by elgg_get_annotation_from_id()', 1.8); + return elgg_get_annotation_from_id($annotation_id); +} + +/** + * Delete a given annotation. + * + * @param int $id The annotation id + * + * @return bool + * @deprecated 1.8 Use elgg_delete_annotations() + */ +function delete_annotation($id) { + elgg_deprecated_notice('delete_annotation() is deprecated by elgg_delete_annotations()', 1.8); + if (!$id) { + return false; + } + return elgg_delete_annotations(array('annotation_id' => $annotation_id)); +} + +/** + * Clear all the annotations for a given entity, assuming you have access to that metadata. + * + * @param int $guid The entity guid + * @param string $name The name of the annotation to delete. + * + * @return int Number of annotations deleted or false if an error + * @deprecated 1.8 Use elgg_delete_annotations() + */ +function clear_annotations($guid, $name = "") { + elgg_deprecated_notice('clear_annotations() is deprecated by elgg_delete_annotations()', 1.8); + + if (!$guid) { + return false; + } + + $options = array( + 'guid' => $guid, + 'limit' => 0 + ); + + if ($name) { + $options['annotation_name'] = $name; + } + + return elgg_delete_annotations($options); +} + +/** + * Clear all annotations belonging to a given owner_guid + * + * @param int $owner_guid The owner + * + * @return int Number of annotations deleted + * @deprecated 1.8 Use elgg_delete_annotations() + */ +function clear_annotations_by_owner($owner_guid) { + elgg_deprecated_notice('clear_annotations_by_owner() is deprecated by elgg_delete_annotations()', 1.8); + + if (!$owner_guid) { + return false; + } + + $options = array( + 'annotation_owner_guid' => $guid, + 'limit' => 0 + ); + + return elgg_delete_annotations($options); +} + +/** + * Registers a page handler for a particular identifier + * + * For example, you can register a function called 'blog_page_handler' for handler type 'blog' + * Now for all URLs of type http://yoururl/pg/blog/*, the blog_page_handler() function will be called. + * The part of the URL marked with * above will be exploded on '/' characters and passed as an + * array to that function. + * For example, the URL http://yoururl/blog/username/friends/ would result in the call: + * blog_page_handler(array('username','friends'), blog); + * + * Page handler functions should return true or the default page handler will be called. + * + * A request to register a page handler with the same identifier as previously registered + * handler will replace the previous one. + * + * The context is set to the page handler identifier before the registered + * page handler function is called. For the above example, the context is set to 'blog'. + * + * @param string $handler The page type to handle + * @param string $function Your function name + * @return true|false Depending on success + * + * @deprecated 1.8 Use {@link elgg_register_page_handler()} + */ +function register_page_handler($handler, $function){ + elgg_deprecated_notice("register_page_handler() was deprecated by elgg_register_page_handler()", 1.8); + return elgg_register_page_handler($handler, $function); +} + +/** + * Unregister a page handler for an identifier + * + * Note: to replace a page handler, call register_page_handler() + * + * @param string $handler The page type identifier + * @since 1.7.2 + * + * @deprecated 1.8 Use {@link elgg_unregister_page_handler()} + */ +function unregister_page_handler($handler) { + elgg_deprecated_notice("unregister_page_handler() was deprecated by elgg_unregister_page_handler()", 1.8); + return elgg_unregister_page_handler($handler); +} + +/** + * Register an annotation url handler. + * + * @param string $function_name The function. + * @param string $extender_name The name, default 'all'. + * + * @deprecated 1.8 Use {@link elgg_register_annotation_url_handler()} + */ +function register_annotation_url_handler($function, $extender_name) { + elgg_deprecated_notice("register_annotation_url_handler() was deprecated by elgg_register_annotation_url_handler()", 1.8); + return elgg_register_annotation_url_handler($extender_name, $function); +} + +/** + * Sets the URL handler for a particular extender type and name. + * It is recommended that you do not call this directly, instead use one of the wrapper functions in the + * subtype files. + * + * @param string $function_name The function to register + * @param string $extender_type Extender type + * @param string $extender_name The name of the extender + * @return true|false Depending on success + * + * @deprecated 1.8 Use {@link elgg_register_extender_url_handler()} + */ +function register_extender_url_handler($function, $type = "all", $name = "all") { + elgg_deprecated_notice("register_extender_url_handler() was deprecated by elgg_register_extender_url_handler()", 1.8); + return elgg_register_extender_url_handler($type, $name, $function); +} + +/** + * Registers and entity type and subtype to return in search and other places. + * A description in the elgg_echo languages file of the form item:type:subtype + * is also expected. + * + * @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 + * + * @deprecated 1.8 Use {@link elgg_register_entity_type()} + */ +function register_entity_type($type, $subtype = null) { + elgg_deprecated_notice("register_entity_type() was deprecated by elgg_register_entity_type()", 1.8); + return elgg_register_entity_type($type, $subtype); +} + +/** + * Register a metadata url handler. + * + * @param string $function_name The function. + * @param string $extender_name The name, default 'all'. + * + * @deprecated 1.8 Use {@link elgg_register_metadata_url_handler()} + */ +function register_metadata_url_handler($function, $extender_name = "all") { + return elgg_register_metadata_url_handler($extender_name, $function); +} + +/** + * Sets the URL handler for a particular relationship type + * + * @param string $function_name The function to register + * @param string $relationship_type The relationship type. + * @return true|false Depending on success + * + * @deprecated 1.8 Use {@link elgg_register_relationship_url_handler()} + */ +function register_relationship_url_handler($function_name, $relationship_type = "all") { + elgg_deprecated_notice("register_relationship_url_handler() was deprecated by elgg_register_relationship_url_handler()", 1.8); + return elgg_register_relationship_url_handler($relationship_type, $function_name); +} + +/** + * Registers a view to be simply cached + * + * Views cached in this manner must take no parameters and be login agnostic - + * that is to say, they look the same no matter who is logged in (or logged out). + * + * CSS and the basic jS views are automatically cached like this. + * + * @param string $viewname View name + * + * @deprecated 1.8 Use {@link elgg_register_simplecache_view()} + */ +function elgg_view_register_simplecache($viewname) { + elgg_deprecated_notice("elgg_view_register_simplecache() was deprecated by elgg_register_simplecache_view()", 1.8); + return elgg_register_simplecache_view($viewname); +} + +/** + * Regenerates the simple cache. + * + * @param string $viewtype Optional viewtype to regenerate + * @see elgg_view_register_simplecache() + * + * @deprecated 1.8 Use {@link elgg_regenerate_simplecache()} + */ +function elgg_view_regenerate_simplecache($viewtype = NULL) { + elgg_deprecated_notice("elgg_view_regenerate_simplecache() was deprecated by elgg_regenerate_simplecache()", 1.8); + return elgg_regenerate_simplecache($viewtype); +} + +/** + * Enables the simple cache. + * + * @see elgg_view_register_simplecache() + * + * @deprecated 1.8 Use {@link elgg_enable_simplecache()} + */ +function elgg_view_enable_simplecache() { + elgg_deprecated_notice("elgg_view_enable_simplecache() was deprecated by elgg_enable_simplecache()", 1.8); + return elgg_enable_simplecache(); +} + +/** + * Disables the simple cache. + * + * @see elgg_view_register_simplecache() + * + * @deprecated 1.8 Use {@link elgg_disable_simplecache()} + */ +function elgg_view_disable_simplecache() { + elgg_deprecated_notice("elgg_view_disable_simplecache() was deprecated by elgg_disable_simplecache()", 1.8); + return elgg_disable_simplecache(); +} + +// these were internal functions that perhaps can be removed rather than deprecated +/** + * @deprecated 1.8 + */ +function is_db_installed() { + elgg_deprecated_notice('is_db_installed() has been deprecated', 1.8); + return true; +} + +/** + * @deprecated 1.8 + */ +function is_installed() { + elgg_deprecated_notice('is_installed() has been deprecated', 1.8); + return true; +} + +/** + * Attempt to authenticate. + * This function will process all registered PAM handlers or stop when the first + * handler fails. A handler fails by either returning false or throwing an + * exception. The advantage of throwing an exception is that it returns a message + * through the global $_PAM_HANDLERS_MSG which can be used in communication with + * a user. The order that handlers are processed is determined by the order that + * they were registered. + * + * If $credentials are provided the PAM handler should authenticate using the + * provided credentials, if not then credentials should be prompted for or + * otherwise retrieved (eg from the HTTP header or $_SESSION). + * + * @param mixed $credentials Mixed PAM handler specific credentials (e.g. username, password) + * @param string $policy - the policy type, default is "user" + * @return bool true if authenticated, false if not. + * + * @deprecated 1.8 See {@link ElggPAM} + */ +function pam_authenticate($credentials = NULL, $policy = "user") { + elgg_deprecated_notice('pam_authenticate has been deprecated for ElggPAM', 1.8); + global $_PAM_HANDLERS, $_PAM_HANDLERS_MSG; + + $_PAM_HANDLERS_MSG = array(); + + $authenticated = false; + + foreach ($_PAM_HANDLERS[$policy] as $k => $v) { + $handler = $v->handler; + $importance = $v->importance; + + try { + // Execute the handler + if ($handler($credentials)) { + // Explicitly returned true + $_PAM_HANDLERS_MSG[$k] = "Authenticated!"; + + $authenticated = true; + } else { + $_PAM_HANDLERS_MSG[$k] = "Not Authenticated."; + + // If this is required then abort. + if ($importance == 'required') { + return false; + } + } + } catch (Exception $e) { + $_PAM_HANDLERS_MSG[$k] = "$e"; + + // If this is required then abort. + if ($importance == 'required') { + return false; + } + } + } + + return $authenticated; +} + + +/** + * When given a widget entity and a new requested location, saves the new location + * and also provides a sensible ordering for all widgets in that column + * + * @param ElggObject $widget The widget entity + * @param int $order The order within the column + * @param int $column The column (1, 2 or 3) + * + * @return bool Depending on success + * @deprecated 1.8 use ElggWidget::move() + */ +function save_widget_location(ElggObject $widget, $order, $column) { + elgg_deprecated_notice('save_widget_location() is deprecated', 1.8); + if ($widget instanceof ElggObject) { + if ($widget->subtype == "widget") { + // If you can't move the widget, don't save a new location + if (!$widget->draggable) { + return false; + } + + // Sanitise the column value + if ($column != 1 || $column != 2 || $column != 3) { + $column = 1; + } + + $widget->column = (int) $column; + + $ordertmp = array(); + $params = array( + 'context' => $widget->context, + 'column' => $column, + ); + + if ($entities = get_entities_from_metadata_multi($params, 'object', 'widget')) { + foreach ($entities as $entity) { + $entityorder = $entity->order; + if ($entityorder < $order) { + $ordertmp[$entityorder] = $entity; + } + if ($entityorder >= $order) { + $ordertmp[$entityorder + 10000] = $entity; + } + } + } + + $ordertmp[$order] = $widget; + ksort($ordertmp); + + $orderticker = 10; + foreach ($ordertmp as $orderval => $entity) { + $entity->order = $orderticker; + $orderticker += 10; + } + + return true; + } else { + register_error($widget->subtype); + } + + } + + return false; +} + +/** + * Get widgets for a particular context and column, in order of display + * + * @param int $user_guid The owner user GUID + * @param string $context The context (profile, dashboard etc) + * @param int $column The column (1 or 2) + * + * @return array|false An array of widget ElggObjects, or false + * @deprecated 1.8 Use elgg_get_widgets() + */ +function get_widgets($user_guid, $context, $column) { + elgg_deprecated_notice('get_widgets is depecated for elgg_get_widgets', 1.8); + $params = array( + 'column' => $column, + 'context' => $context + ); + $widgets = get_entities_from_private_setting_multi($params, "object", + "widget", $user_guid, "", 10000); + + if ($widgets) { + $widgetorder = array(); + foreach ($widgets as $widget) { + $order = $widget->order; + while (isset($widgetorder[$order])) { + $order++; + } + $widgetorder[$order] = $widget; + } + + ksort($widgetorder); + + return $widgetorder; + } + + return false; +} + +/** + * Add a new widget instance + * + * @param int $entity_guid GUID of entity that owns this widget + * @param string $handler The handler for this widget + * @param string $context The page context for this widget + * @param int $order The order to display this widget in + * @param int $column The column to display this widget in (1, 2 or 3) + * @param int $access_id If not specified, it is set to the default access level + * + * @return int|false Widget GUID or false on failure + * @deprecated 1.8 use elgg_create_widget() + */ +function add_widget($entity_guid, $handler, $context, $order = 0, $column = 1, $access_id = null) { + elgg_deprecated_notice('add_widget has been deprecated for elgg_create_widget', 1.8); + if (empty($entity_guid) || empty($context) || empty($handler) || !widget_type_exists($handler)) { + return false; + } + + if ($entity = get_entity($entity_guid)) { + $widget = new ElggWidget; + $widget->owner_guid = $entity_guid; + $widget->container_guid = $entity_guid; + if (isset($access_id)) { + $widget->access_id = $access_id; + } else { + $widget->access_id = get_default_access(); + } + + $guid = $widget->save(); + + // private settings cannot be set until ElggWidget saved + $widget->handler = $handler; + $widget->context = $context; + $widget->column = $column; + $widget->order = $order; + + return $guid; + } + + return false; +} + +/** + * Define a new widget type + * + * @param string $handler The identifier for the widget handler + * @param string $name The name of the widget type + * @param string $description A description for the widget type + * @param string $context A comma-separated list of contexts where this + * widget is allowed (default: 'all') + * @param bool $multiple Whether or not multiple instances of this widget + * are allowed on a single dashboard (default: false) + * @param string $positions A comma-separated list of positions on the page + * (side or main) where this widget is allowed (default: "side,main") + * + * @return bool Depending on success + * @deprecated 1.8 Use elgg_register_widget_type + */ +function add_widget_type($handler, $name, $description, $context = "all", +$multiple = false, $positions = "side,main") { + elgg_deprecated_notice("add_widget_type deprecated for elgg_register_widget_type", 1.8); + + return elgg_register_widget_type($handler, $name, $description, $context, $multiple); +} + +/** + * Remove a widget type + * + * @param string $handler The identifier for the widget handler + * + * @return void + * @since 1.7.1 + * @deprecated 1.8 Use elgg_unregister_widget_type + */ +function remove_widget_type($handler) { + elgg_deprecated_notice("remove_widget_type deprecated for elgg_unregister_widget_type", 1.8); + return elgg_unregister_widget_type($handler); +} + +/** + * Determines whether or not widgets with the specified handler have been defined + * + * @param string $handler The widget handler identifying string + * + * @return bool Whether or not those widgets exist + * @deprecated 1.8 Use elgg_is_widget_type + */ +function widget_type_exists($handler) { + elgg_deprecated_notice("widget_type_exists deprecated for elgg_is_widget_type", 1.8); + return elgg_is_widget_type($handler); +} + +/** + * Returns an array of stdClass objects representing the defined widget types + * + * @return array A list of types defined (if any) + * @deprecated 1.8 Use elgg_get_widget_types + */ +function get_widget_types() { + elgg_deprecated_notice("get_widget_types deprecrated for elgg_get_widget_types", 1.8); + return elgg_get_widget_types(); +} + +/** + * Saves a widget's settings (by passing an array of + * (name => value) pairs to save_{$handler}_widget) + * + * @param int $widget_guid The GUID of the widget we're saving to + * @param array $params An array of name => value parameters + * + * @return bool + * @deprecated 1.8 Use elgg_save_widget_settings + */ +function save_widget_info($widget_guid, $params) { + elgg_deprecated_notice("save_widget_info() is deprecated for elgg_save_widget_settings", 1.8); + if ($widget = get_entity($widget_guid)) { + + $subtype = $widget->getSubtype(); + + if ($subtype != "widget") { + return false; + } + $handler = $widget->handler; + if (empty($handler) || !widget_type_exists($handler)) { + return false; + } + + if (!$widget->canEdit()) { + return false; + } + + // Save the params to the widget + if (is_array($params) && sizeof($params) > 0) { + foreach ($params as $name => $value) { + + if (!empty($name) && !in_array($name, array( + 'guid', 'owner_guid', 'site_guid' + ))) { + if (is_array($value)) { + // @todo Handle arrays securely + $widget->setMetaData($name, $value, "", true); + } else { + $widget->$name = $value; + } + } + } + $widget->save(); + } + + $function = "save_{$handler}_widget"; + if (is_callable($function)) { + return $function($params); + } + + return true; + } + + return false; +} + +/** + * Reorders the widgets from a widget panel + * + * @param string $panelstring1 String of guids of ElggWidget objects separated by :: + * @param string $panelstring2 String of guids of ElggWidget objects separated by :: + * @param string $panelstring3 String of guids of ElggWidget objects separated by :: + * @param string $context Profile or dashboard + * @param int $owner Owner guid + * + * @return void + * @deprecated 1.8 Don't use. + */ +function reorder_widgets_from_panel($panelstring1, $panelstring2, $panelstring3, $context, $owner) { + elgg_deprecated_notice("reorder_widgets_from_panel() is deprecated", 1.8); + $return = true; + + $mainwidgets = explode('::', $panelstring1); + $sidewidgets = explode('::', $panelstring2); + $rightwidgets = explode('::', $panelstring3); + + $handlers = array(); + $guids = array(); + + if (is_array($mainwidgets) && sizeof($mainwidgets) > 0) { + foreach ($mainwidgets as $widget) { + + $guid = (int) $widget; + + if ("{$guid}" == "{$widget}") { + $guids[1][] = $widget; + } else { + $handlers[1][] = $widget; + } + } + } + if (is_array($sidewidgets) && sizeof($sidewidgets) > 0) { + foreach ($sidewidgets as $widget) { + + $guid = (int) $widget; + + if ("{$guid}" == "{$widget}") { + $guids[2][] = $widget; + } else { + $handlers[2][] = $widget; + } + + } + } + if (is_array($rightwidgets) && sizeof($rightwidgets) > 0) { + foreach ($rightwidgets as $widget) { + + $guid = (int) $widget; + + if ("{$guid}" == "{$widget}") { + $guids[3][] = $widget; + } else { + $handlers[3][] = $widget; + } + + } + } + + // Reorder existing widgets or delete ones that have vanished + foreach (array(1, 2, 3) as $column) { + if ($dbwidgets = get_widgets($owner, $context, $column)) { + + foreach ($dbwidgets as $dbwidget) { + if (in_array($dbwidget->getGUID(), $guids[1]) + || in_array($dbwidget->getGUID(), $guids[2]) || in_array($dbwidget->getGUID(), $guids[3])) { + + if (in_array($dbwidget->getGUID(), $guids[1])) { + $pos = array_search($dbwidget->getGUID(), $guids[1]); + $col = 1; + } else if (in_array($dbwidget->getGUID(), $guids[2])) { + $pos = array_search($dbwidget->getGUID(), $guids[2]); + $col = 2; + } else { + $pos = array_search($dbwidget->getGUID(), $guids[3]); + $col = 3; + } + $pos = ($pos + 1) * 10; + $dbwidget->column = $col; + $dbwidget->order = $pos; + } else { + $dbguid = $dbwidget->getGUID(); + if (!$dbwidget->delete()) { + $return = false; + } else { + // Remove state cookie + setcookie('widget' + $dbguid, null); + } + } + } + + } + // Add new ones + if (sizeof($guids[$column]) > 0) { + foreach ($guids[$column] as $key => $guid) { + if ($guid == 0) { + $pos = ($key + 1) * 10; + $handler = $handlers[$column][$key]; + if (!add_widget($owner, $handler, $context, $pos, $column)) { + $return = false; + } + } + } + } + } + + return $return; +} + +/** + * Register a particular context for use with widgets. + * + * @param string $context The context we wish to enable context for + * + * @return void + * @deprecated 1.8 Don't use. + */ +function use_widgets($context) { + elgg_deprecated_notice("use_widgets is deprecated", 1.8); + global $CONFIG; + + if (!isset($CONFIG->widgets)) { + $CONFIG->widgets = new stdClass; + } + + if (!isset($CONFIG->widgets->contexts)) { + $CONFIG->widgets->contexts = array(); + } + + if (!empty($context)) { + $CONFIG->widgets->contexts[] = $context; + } +} + +/** + * Determines whether or not the current context is using widgets + * + * @return bool Depending on widget status + * @deprecated 1.8 Don't use. + */ +function using_widgets() { + elgg_deprecated_notice("using_widgets is deprecated", 1.8); + global $CONFIG; + + $context = elgg_get_context(); + if (isset($CONFIG->widgets->contexts) && is_array($CONFIG->widgets->contexts)) { + if (in_array($context, $CONFIG->widgets->contexts)) { + return true; + } + } + + return false; +} + +/** + * Displays a particular widget + * + * @param ElggObject $widget The widget to display + * @return string The HTML for the widget, including JavaScript wrapper + * + * @deprecated 1.8 Use elgg_view_entity() + */ +function display_widget(ElggObject $widget) { + elgg_deprecated_notice("display_widget() was been deprecated. Use elgg_view_entity().", 1.8); + return elgg_view_entity($widget); +} + +/** + * Count the number of comments attached to an entity + * + * @param ElggEntity $entity + * @return int Number of comments + * @deprecated 1.8 Use ElggEntity->countComments() + */ +function elgg_count_comments($entity) { + elgg_deprecated_notice('elgg_count_comments() is deprecated by ElggEntity->countComments()', 1.8); + + if ($entity instanceof ElggEntity) { + return $entity->countComments(); + } + + return 0; +} + +/** + * Removes all items relating to a particular acting entity from the river + * + * @param int $subject_guid The GUID of the entity + * + * @return bool Depending on success + * @deprecated 1.8 Use elgg_delete_river() + */ +function remove_from_river_by_subject($subject_guid) { + elgg_deprecated_notice("remove_from_river_by_subject() deprecated by elgg_delete_river()", 1.8); + + return elgg_delete_river(array('subject_guid' => $subject_guid)); +} + +/** + * Removes all items relating to a particular entity being acted upon from the river + * + * @param int $object_guid The GUID of the entity + * + * @return bool Depending on success + * @deprecated 1.8 Use elgg_delete_river() + */ +function remove_from_river_by_object($object_guid) { + elgg_deprecated_notice("remove_from_river_by_object() deprecated by elgg_delete_river()", 1.8); + + return elgg_delete_river(array('object_guid' => $object_guid)); +} + +/** + * Removes all items relating to a particular annotation being acted upon from the river + * + * @param int $annotation_id The ID of the annotation + * + * @return bool Depending on success + * @since 1.7.0 + * @deprecated 1.8 Use elgg_delete_river() + */ +function remove_from_river_by_annotation($annotation_id) { + elgg_deprecated_notice("remove_from_river_by_annotation() deprecated by elgg_delete_river()", 1.8); + + return elgg_delete_river(array('annotation_id' => $annotation_id)); +} + +/** + * Removes a single river entry + * + * @param int $id The ID of the river entry + * + * @return bool Depending on success + * @since 1.7.2 + * @deprecated 1.8 Use elgg_delete_river() + */ +function remove_from_river_by_id($id) { + elgg_deprecated_notice("remove_from_river_by_id() deprecated by elgg_delete_river()", 1.8); + + return elgg_delete_river(array('id' => $id)); +} + +/** + * A default page handler + * Tries to locate a suitable file to include. Only works for core pages, not plugins. + * + * @param array $page The page URL elements + * @param string $handler The base handler + * + * @return true|false Depending on success + * @deprecated 1.8 + */ +function default_page_handler($page, $handler) { + global $CONFIG; + + elgg_deprecated_notice("default_page_handler is deprecated", "1.8"); + + $page = implode('/', $page); + + // protect against including arbitary files + $page = str_replace("..", "", $page); + + $callpath = $CONFIG->path . $handler . "/" . $page; + if (is_dir($callpath)) { + $callpath = sanitise_filepath($callpath); + $callpath .= "index.php"; + if (file_exists($callpath)) { + if (include($callpath)) { + return TRUE; + } + } + } else if (file_exists($callpath)) { + include($callpath); + return TRUE; + } + + return FALSE; +} + +/** + * Invalidate this class's entry in the cache. + * + * @param int $guid The entity guid + * + * @return void + * @access private + * @deprecated 1.8 + */ +function invalidate_cache_for_entity($guid) { + elgg_deprecated_notice('invalidate_cache_for_entity() is a private function and should not be used.', 1.8); + _elgg_invalidate_cache_for_entity($guid); +} + +/** + * Cache an entity. + * + * Stores an entity in $ENTITY_CACHE; + * + * @param ElggEntity $entity Entity to cache + * + * @return void + * @access private + * @deprecated 1.8 + */ +function cache_entity(ElggEntity $entity) { + elgg_deprecated_notice('cache_entity() is a private function and should not be used.', 1.8); + _elgg_cache_entity($entity); +} + +/** + * Retrieve a entity from the cache. + * + * @param int $guid The guid + * + * @return ElggEntity|bool false if entity not cached, or not fully loaded + * @access private + * @deprecated 1.8 + */ +function retrieve_cached_entity($guid) { + elgg_deprecated_notice('retrieve_cached_entity() is a private function and should not be used.', 1.8); + return _elgg_retrieve_cached_entity($guid); +} diff --git a/engine/lib/deprecated-1.9.php b/engine/lib/deprecated-1.9.php new file mode 100644 index 000000000..31d03428f --- /dev/null +++ b/engine/lib/deprecated-1.9.php @@ -0,0 +1,582 @@ +<?php +/** + * Return a timestamp for the start of a given day (defaults today). + * + * @param int $day Day + * @param int $month Month + * @param int $year Year + * + * @return int + * @access private + * @deprecated 1.9 + */ +function get_day_start($day = null, $month = null, $year = null) { + elgg_deprecated_notice('get_day_start() has been deprecated', 1.9); + return mktime(0, 0, 0, $month, $day, $year); +} + +/** + * Return a timestamp for the end of a given day (defaults today). + * + * @param int $day Day + * @param int $month Month + * @param int $year Year + * + * @return int + * @access private + * @deprecated 1.9 + */ +function get_day_end($day = null, $month = null, $year = null) { + elgg_deprecated_notice('get_day_end() has been deprecated', 1.9); + return mktime(23, 59, 59, $month, $day, $year); +} + +/** + * Return the notable entities for a given time period. + * + * @param int $start_time The start time as a unix timestamp. + * @param int $end_time The end time as a unix timestamp. + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param string $order_by The field to order by; by default, time_created desc + * @param int $limit The number of entities to return; 10 by default + * @param int $offset The indexing offset, 0 by default + * @param boolean $count Set to true to get a count instead of entities. Defaults to false. + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any. + * @param mixed $container_guid Container or containers to get entities from (default: any). + * + * @return array|false + * @access private + * @deprecated 1.9 + */ +function get_notable_entities($start_time, $end_time, $type = "", $subtype = "", $owner_guid = 0, +$order_by = "asc", $limit = 10, $offset = 0, $count = false, $site_guid = 0, +$container_guid = null) { + elgg_deprecated_notice('get_notable_entities() has been deprecated', 1.9); + global $CONFIG; + + if ($subtype === false || $subtype === null || $subtype === 0) { + return false; + } + + $start_time = (int)$start_time; + $end_time = (int)$end_time; + $order_by = sanitise_string($order_by); + $limit = (int)$limit; + $offset = (int)$offset; + $site_guid = (int) $site_guid; + if ($site_guid == 0) { + $site_guid = $CONFIG->site_guid; + } + + $where = array(); + + if (is_array($type)) { + $tempwhere = ""; + if (sizeof($type)) { + foreach ($type as $typekey => $subtypearray) { + foreach ($subtypearray as $subtypeval) { + $typekey = sanitise_string($typekey); + if (!empty($subtypeval)) { + $subtypeval = (int) get_subtype_id($typekey, $subtypeval); + } else { + $subtypeval = 0; + } + if (!empty($tempwhere)) { + $tempwhere .= " or "; + } + $tempwhere .= "(e.type = '{$typekey}' and e.subtype = {$subtypeval})"; + } + } + } + if (!empty($tempwhere)) { + $where[] = "({$tempwhere})"; + } + } else { + $type = sanitise_string($type); + $subtype = get_subtype_id($type, $subtype); + + if ($type != "") { + $where[] = "e.type='$type'"; + } + + if ($subtype !== "") { + $where[] = "e.subtype=$subtype"; + } + } + + if ($owner_guid != "") { + if (!is_array($owner_guid)) { + $owner_array = array($owner_guid); + $owner_guid = (int) $owner_guid; + $where[] = "e.owner_guid = '$owner_guid'"; + } else if (sizeof($owner_guid) > 0) { + $owner_array = array_map('sanitise_int', $owner_guid); + // Cast every element to the owner_guid array to int + $owner_guid = implode(",", $owner_guid); + $where[] = "e.owner_guid in ({$owner_guid})"; + } + if (is_null($container_guid)) { + $container_guid = $owner_array; + } + } + + if ($site_guid > 0) { + $where[] = "e.site_guid = {$site_guid}"; + } + + if (!is_null($container_guid)) { + if (is_array($container_guid)) { + foreach ($container_guid as $key => $val) { + $container_guid[$key] = (int) $val; + } + $where[] = "e.container_guid in (" . implode(",", $container_guid) . ")"; + } else { + $container_guid = (int) $container_guid; + $where[] = "e.container_guid = {$container_guid}"; + } + } + + // Add the calendar stuff + $cal_join = " + JOIN {$CONFIG->dbprefix}metadata cal_start on e.guid=cal_start.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_start_name on cal_start.name_id=cal_start_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_start_value on cal_start.value_id=cal_start_value.id + + JOIN {$CONFIG->dbprefix}metadata cal_end on e.guid=cal_end.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_end_name on cal_end.name_id=cal_end_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_end_value on cal_end.value_id=cal_end_value.id + "; + $where[] = "cal_start_name.string='calendar_start'"; + $where[] = "cal_start_value.string>=$start_time"; + $where[] = "cal_end_name.string='calendar_end'"; + $where[] = "cal_end_value.string <= $end_time"; + + + if (!$count) { + $query = "SELECT e.* from {$CONFIG->dbprefix}entities e $cal_join where "; + } else { + $query = "SELECT count(e.guid) as total from {$CONFIG->dbprefix}entities e $cal_join where "; + } + foreach ($where as $w) { + $query .= " $w and "; + } + + $query .= get_access_sql_suffix('e'); // Add access controls + + if (!$count) { + $query .= " order by n.calendar_start $order_by"; + // Add order and limit + if ($limit) { + $query .= " limit $offset, $limit"; + } + $dt = get_data($query, "entity_row_to_elggstar"); + + return $dt; + } else { + $total = get_data_row($query); + return $total->total; + } +} + +/** + * Return the notable entities for a given time period based on an item of metadata. + * + * @param int $start_time The start time as a unix timestamp. + * @param int $end_time The end time as a unix timestamp. + * @param mixed $meta_name Metadata name + * @param mixed $meta_value Metadata value + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity. + * @param int $owner_guid Owner GUID + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by Optional ordering. + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any. + * @param bool $count If true, returns count instead of entities. (Default: false) + * + * @return int|array A list of entities, or a count if $count is set to true + * @access private + * @deprecated 1.9 + */ +function get_notable_entities_from_metadata($start_time, $end_time, $meta_name, $meta_value = "", +$entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", +$site_guid = 0, $count = false) { + elgg_deprecated_notice('get_notable_entities_from_metadata() has been deprecated', 1.9); + + global $CONFIG; + + $meta_n = get_metastring_id($meta_name); + $meta_v = get_metastring_id($meta_value); + + $start_time = (int)$start_time; + $end_time = (int)$end_time; + $entity_type = sanitise_string($entity_type); + $entity_subtype = get_subtype_id($entity_type, $entity_subtype); + $limit = (int)$limit; + $offset = (int)$offset; + if ($order_by == "") { + $order_by = "e.time_created desc"; + } + $order_by = sanitise_string($order_by); + $site_guid = (int) $site_guid; + if ((is_array($owner_guid) && (count($owner_guid)))) { + foreach ($owner_guid as $key => $guid) { + $owner_guid[$key] = (int) $guid; + } + } else { + $owner_guid = (int) $owner_guid; + } + + if ($site_guid == 0) { + $site_guid = $CONFIG->site_guid; + } + + //$access = get_access_list(); + + $where = array(); + + if ($entity_type != "") { + $where[] = "e.type='$entity_type'"; + } + + if ($entity_subtype) { + $where[] = "e.subtype=$entity_subtype"; + } + + if ($meta_name != "") { + $where[] = "m.name_id='$meta_n'"; + } + + if ($meta_value != "") { + $where[] = "m.value_id='$meta_v'"; + } + + if ($site_guid > 0) { + $where[] = "e.site_guid = {$site_guid}"; + } + + if (is_array($owner_guid)) { + $where[] = "e.container_guid in (" . implode(",", $owner_guid) . ")"; + } else if ($owner_guid > 0) { + $where[] = "e.container_guid = {$owner_guid}"; + } + + // Add the calendar stuff + $cal_join = " + JOIN {$CONFIG->dbprefix}metadata cal_start on e.guid=cal_start.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_start_name on cal_start.name_id=cal_start_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_start_value on cal_start.value_id=cal_start_value.id + + JOIN {$CONFIG->dbprefix}metadata cal_end on e.guid=cal_end.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_end_name on cal_end.name_id=cal_end_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_end_value on cal_end.value_id=cal_end_value.id + "; + + $where[] = "cal_start_name.string='calendar_start'"; + $where[] = "cal_start_value.string>=$start_time"; + $where[] = "cal_end_name.string='calendar_end'"; + $where[] = "cal_end_value.string <= $end_time"; + + if (!$count) { + $query = "SELECT distinct e.* "; + } else { + $query = "SELECT count(distinct e.guid) as total "; + } + + $query .= "from {$CONFIG->dbprefix}entities e" + . " JOIN {$CONFIG->dbprefix}metadata m on e.guid = m.entity_guid $cal_join where"; + + foreach ($where as $w) { + $query .= " $w and "; + } + + // Add access controls + $query .= get_access_sql_suffix("e"); + $query .= ' and ' . get_access_sql_suffix("m"); + + if (!$count) { + // Add order and limit + $query .= " order by $order_by limit $offset, $limit"; + return get_data($query, "entity_row_to_elggstar"); + } else { + if ($row = get_data_row($query)) { + return $row->total; + } + } + + return false; +} + +/** + * Return the notable entities for a given time period based on their relationship. + * + * @param int $start_time The start time as a unix timestamp. + * @param int $end_time The end time as a unix timestamp. + * @param string $relationship The relationship eg "friends_of" + * @param int $relationship_guid The guid of the entity to use query + * @param bool $inverse_relationship Reverse the normal function of the query to say + * "give me all entities for whom $relationship_guid is a + * $relationship of" + * @param string $type Entity type + * @param string $subtype Entity subtype + * @param int $owner_guid Owner GUID + * @param string $order_by Optional Order by + * @param int $limit Limit + * @param int $offset Offset + * @param boolean $count If true returns a count of entities (default false) + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any + * + * @return array|int|false An array of entities, or the number of entities, or false on failure + * @access private + * @deprecated 1.9 + */ +function get_noteable_entities_from_relationship($start_time, $end_time, $relationship, +$relationship_guid, $inverse_relationship = false, $type = "", $subtype = "", $owner_guid = 0, +$order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0) { + elgg_deprecated_notice('get_noteable_entities_from_relationship() has been deprecated', 1.9); + + global $CONFIG; + + $start_time = (int)$start_time; + $end_time = (int)$end_time; + $relationship = sanitise_string($relationship); + $relationship_guid = (int)$relationship_guid; + $inverse_relationship = (bool)$inverse_relationship; + $type = sanitise_string($type); + $subtype = get_subtype_id($type, $subtype); + $owner_guid = (int)$owner_guid; + if ($order_by == "") { + $order_by = "time_created desc"; + } + $order_by = sanitise_string($order_by); + $limit = (int)$limit; + $offset = (int)$offset; + $site_guid = (int) $site_guid; + if ($site_guid == 0) { + $site_guid = $CONFIG->site_guid; + } + + //$access = get_access_list(); + + $where = array(); + + if ($relationship != "") { + $where[] = "r.relationship='$relationship'"; + } + if ($relationship_guid) { + $where[] = $inverse_relationship ? + "r.guid_two='$relationship_guid'" : "r.guid_one='$relationship_guid'"; + } + if ($type != "") { + $where[] = "e.type='$type'"; + } + if ($subtype) { + $where[] = "e.subtype=$subtype"; + } + if ($owner_guid != "") { + $where[] = "e.container_guid='$owner_guid'"; + } + if ($site_guid > 0) { + $where[] = "e.site_guid = {$site_guid}"; + } + + // Add the calendar stuff + $cal_join = " + JOIN {$CONFIG->dbprefix}metadata cal_start on e.guid=cal_start.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_start_name on cal_start.name_id=cal_start_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_start_value on cal_start.value_id=cal_start_value.id + + JOIN {$CONFIG->dbprefix}metadata cal_end on e.guid=cal_end.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_end_name on cal_end.name_id=cal_end_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_end_value on cal_end.value_id=cal_end_value.id + "; + $where[] = "cal_start_name.string='calendar_start'"; + $where[] = "cal_start_value.string>=$start_time"; + $where[] = "cal_end_name.string='calendar_end'"; + $where[] = "cal_end_value.string <= $end_time"; + + // Select what we're joining based on the options + $joinon = "e.guid = r.guid_one"; + if (!$inverse_relationship) { + $joinon = "e.guid = r.guid_two"; + } + + if ($count) { + $query = "SELECT count(distinct e.guid) as total "; + } else { + $query = "SELECT distinct e.* "; + } + $query .= " from {$CONFIG->dbprefix}entity_relationships r" + . " JOIN {$CONFIG->dbprefix}entities e on $joinon $cal_join where "; + + foreach ($where as $w) { + $query .= " $w and "; + } + // Add access controls + $query .= get_access_sql_suffix("e"); + if (!$count) { + $query .= " order by $order_by limit $offset, $limit"; // Add order and limit + return get_data($query, "entity_row_to_elggstar"); + } else { + if ($count = get_data_row($query)) { + return $count->total; + } + } + return false; +} + +/** + * Get all entities for today. + * + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param string $order_by The field to order by; by default, time_created desc + * @param int $limit The number of entities to return; 10 by default + * @param int $offset The indexing offset, 0 by default + * @param boolean $count If true returns a count of entities (default false) + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any + * @param mixed $container_guid Container(s) to get entities from (default: any). + * + * @return array|false + * @access private + * @deprecated 1.9 + */ +function get_todays_entities($type = "", $subtype = "", $owner_guid = 0, $order_by = "", +$limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = null) { + elgg_deprecated_notice('get_todays_entities() has been deprecated', 1.9); + + $day_start = get_day_start(); + $day_end = get_day_end(); + + return get_notable_entities($day_start, $day_end, $type, $subtype, $owner_guid, $order_by, + $limit, $offset, $count, $site_guid, $container_guid); +} + +/** + * Get entities for today from metadata. + * + * @param mixed $meta_name Metadata name + * @param mixed $meta_value Metadata value + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity. + * @param int $owner_guid Owner GUID + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by Optional ordering. + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any. + * @param bool $count If true, returns count instead of entities. (Default: false) + * + * @return int|array A list of entities, or a count if $count is set to true + * @access private + * @deprecated 1.9 + */ +function get_todays_entities_from_metadata($meta_name, $meta_value = "", $entity_type = "", +$entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, +$count = false) { + elgg_deprecated_notice('get_todays_entities_from_metadata() has been deprecated', 1.9); + + $day_start = get_day_start(); + $day_end = get_day_end(); + + return get_notable_entities_from_metadata($day_start, $day_end, $meta_name, $meta_value, + $entity_type, $entity_subtype, $owner_guid, $limit, $offset, $order_by, $site_guid, $count); +} + +/** + * Get entities for today from a relationship + * + * @param string $relationship The relationship eg "friends_of" + * @param int $relationship_guid The guid of the entity to use query + * @param bool $inverse_relationship Reverse the normal function of the query to say + * "give me all entities for whom $relationship_guid is a + * $relationship of" + * @param string $type Entity type + * @param string $subtype Entity subtype + * @param int $owner_guid Owner GUID + * @param string $order_by Optional Order by + * @param int $limit Limit + * @param int $offset Offset + * @param boolean $count If true returns a count of entities (default false) + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any + * + * @return array|int|false An array of entities, or the number of entities, or false on failure + * @access private + * @deprecated 1.9 + */ +function get_todays_entities_from_relationship($relationship, $relationship_guid, +$inverse_relationship = false, $type = "", $subtype = "", $owner_guid = 0, +$order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0) { + elgg_deprecated_notice('get_todays_entities_from_relationship() has been deprecated', 1.9); + + $day_start = get_day_start(); + $day_end = get_day_end(); + + return get_notable_entities_from_relationship($day_start, $day_end, $relationship, + $relationship_guid, $inverse_relationship, $type, $subtype, $owner_guid, $order_by, + $limit, $offset, $count, $site_guid); +} + +/** + * Returns a viewable list of entities for a given time period. + * + * @see elgg_view_entity_list + * + * @param int $start_time The start time as a unix timestamp. + * @param int $end_time The end time as a unix timestamp. + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param int $limit The number of entities to return; 10 by default + * @param boolean $fullview Whether or not to display the full view (default: true) + * @param boolean $listtypetoggle Whether or not to allow gallery view + * @param boolean $navigation Display pagination? Default: true + * + * @return string A viewable list of entities + * @access private + * @deprecated 1.9 + */ +function list_notable_entities($start_time, $end_time, $type= "", $subtype = "", $owner_guid = 0, +$limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) { + elgg_deprecated_notice('list_notable_entities() has been deprecated', 1.9); + + $offset = (int) get_input('offset'); + $count = get_notable_entities($start_time, $end_time, $type, $subtype, + $owner_guid, "", $limit, $offset, true); + + $entities = get_notable_entities($start_time, $end_time, $type, $subtype, + $owner_guid, "", $limit, $offset); + + return elgg_view_entity_list($entities, $count, $offset, $limit, + $fullview, $listtypetoggle, $navigation); +} + +/** + * Return a list of today's entities. + * + * @see list_notable_entities + * + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param int $limit The number of entities to return; 10 by default + * @param boolean $fullview Whether or not to display the full view (default: true) + * @param boolean $listtypetoggle Whether or not to allow gallery view + * @param boolean $navigation Display pagination? Default: true + * + * @return string A viewable list of entities + * @access private + * @deprecated 1.9 + */ +function list_todays_entities($type= "", $subtype = "", $owner_guid = 0, $limit = 10, +$fullview = true, $listtypetoggle = false, $navigation = true) { + elgg_deprecated_notice('list_todays_entities() has been deprecated', 1.9); + + $day_start = get_day_start(); + $day_end = get_day_end(); + + return list_notable_entities($day_start, $day_end, $type, $subtype, $owner_guid, $limit, + $fullview, $listtypetoggle, $navigation); +} diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index e8014f2a2..34111c69d 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -18,12 +18,13 @@ elgg_register_classes(dirname(dirname(__FILE__)) . '/classes'); * * @return void * @throws Exception + * @access private */ function _elgg_autoload($class) { global $CONFIG; if (!isset($CONFIG->classes[$class]) || !include($CONFIG->classes[$class])) { - throw new Exception("Failed to autoload $class"); + return false; } } @@ -50,7 +51,7 @@ function elgg_register_classes($dir) { * @param string $class The name of the class * @param string $location The location of the file * - * @return void + * @return true * @since 1.8.0 */ function elgg_register_class($class, $location) { @@ -61,6 +62,8 @@ function elgg_register_class($class, $location) { } $CONFIG->classes[$class] = $location; + + return true; } /** @@ -90,10 +93,17 @@ function elgg_register_library($name, $location) { * @return void * @throws InvalidParameterException * @since 1.8.0 + * @todo return boolean in 1.9 to indicate whether the library has been loaded */ function elgg_load_library($name) { global $CONFIG; + static $loaded_libraries = array(); + + if (in_array($name, $loaded_libraries)) { + return; + } + if (!isset($CONFIG->libraries)) { $CONFIG->libraries = array(); } @@ -104,9 +114,14 @@ function elgg_load_library($name) { } if (!include_once($CONFIG->libraries[$name])) { - $error = elgg_echo('InvalidParameterException:LibraryNotRegistered', array($name)); + $error = elgg_echo('InvalidParameterException:LibraryNotFound', array( + $name, + $CONFIG->libraries[$name]) + ); throw new InvalidParameterException($error); } + + $loaded_libraries[] = $name; } /** @@ -118,12 +133,11 @@ function elgg_load_library($name) { * @param string $location URL to forward to browser to. Can be path relative to the network's URL. * @param string $reason Short explanation for why we're forwarding * - * @return False False if headers have been sent. Terminates execution if forwarding. + * @return false False if headers have been sent. Terminates execution if forwarding. + * @throws SecurityException */ function forward($location = "", $reason = 'system') { - global $CONFIG; - - if (!headers_sent()) { + if (!headers_sent($file, $line)) { if ($location === REFERER) { $location = $_SERVER['HTTP_REFERER']; } @@ -141,9 +155,9 @@ function forward($location = "", $reason = 'system') { } else if ($location === '') { exit; } + } else { + throw new SecurityException(elgg_echo('SecurityException:ForwardFailedToRedirect', array($file, $line))); } - - return false; } /** @@ -155,248 +169,283 @@ function forward($location = "", $reason = 'system') { * JavaScript from a view that may be called more than once. It also handles * more than one plugin adding the same JavaScript. * - * Plugin authors are encouraged to use the $id variable. jQuery plugins - * often have filenames such as jquery.rating.js. In that case, the id - * would be "jquery.rating". It is recommended to not use version numbers - * in the id. + * jQuery plugins often have filenames such as jquery.rating.js. A best practice + * is to base $name on the filename: "jquery.rating". It is recommended to not + * use version numbers in the name. * * The JavaScript files can be local to the server or remote (such as * Google's CDN). * + * @param string $name An identifier for the JavaScript library * @param string $url URL of the JavaScript file - * @param string $id An identifier of the JavaScript library * @param string $location Page location: head or footer. (default: head) + * @param int $priority Priority of the JS file (lower numbers load earlier) + * * @return bool + * @since 1.8.0 */ -function elgg_register_js($url, $id = '', $location = 'head') { - return elgg_register_external_file('javascript', $url, $id, $location); +function elgg_register_js($name, $url, $location = 'head', $priority = null) { + return elgg_register_external_file('js', $name, $url, $location, $priority); } /** - * Register a CSS file for inclusion in the HTML head + * Unregister a JavaScript file + * + * @param string $name The identifier for the JavaScript library * - * @param string $url URL of the CSS file - * @param string $id An identifier for the CSS file * @return bool + * @since 1.8.0 */ -function elgg_register_css($url, $id = '') { - return elgg_register_external_file('css', $url, $id, 'head'); +function elgg_unregister_js($name) { + return elgg_unregister_external_file('js', $name); } /** - * Core registration function for external files + * Load a JavaScript resource on this page * - * @param string $type Type of external resource - * @param string $url URL - * @param string $id Identifier used as key - * @param string $location Location in the page to include the file - * @return bool + * This must be called before elgg_view_page(). It can be called before the + * script is registered. If you do not want a script loaded, unregister it. + * + * @param string $name Identifier of the JavaScript resource + * + * @return void + * @since 1.8.0 */ -function elgg_register_external_file($type, $url, $id, $location) { - global $CONFIG; - - if (empty($url)) { - return false; - } - - $url = elgg_format_url($url); - - if (!isset($CONFIG->externals)) { - $CONFIG->externals = array(); - } - - if (!isset($CONFIG->externals[$type])) { - $CONFIG->externals[$type] = array(); - } - - if (!isset($CONFIG->externals[$type][$location])) { - $CONFIG->externals[$type][$location] = array(); - } - - if (!$id) { - $id = count($CONFIG->externals[$type][$location]); - } else { - $id = trim(strtolower($id)); - } - - $CONFIG->externals[$type][$location][$id] = elgg_normalize_url($url); - - return true; +function elgg_load_js($name) { + elgg_load_external_file('js', $name); } /** - * Unregister a JavaScript file + * Get the JavaScript URLs that are loaded * - * @param string $id The identifier for the JavaScript library - * @param string $url Optional URL to search for if id is not specified - * @param string $location Location in the page - * @return bool + * @param string $location 'head' or 'footer' + * + * @return array + * @since 1.8.0 */ -function elgg_unregister_js($id = '', $url = '', $location = 'head') { - return elgg_unregister_external_file('javascript', $id, $url, $location); +function elgg_get_loaded_js($location = 'head') { + return elgg_get_loaded_external_files('js', $location); } /** - * Unregister an external file + * Register a CSS file for inclusion in the HTML head + * + * @param string $name An identifier for the CSS file + * @param string $url URL of the CSS file + * @param int $priority Priority of the CSS file (lower numbers load earlier) * - * @param string $id The identifier of the CSS file - * @param string $url Optional URL to search for if id is not specified * @return bool + * @since 1.8.0 */ -function elgg_unregister_css($id = '', $url = '') { - return elgg_unregister_external_file('css', $id, $url, 'head'); +function elgg_register_css($name, $url, $priority = null) { + return elgg_register_external_file('css', $name, $url, 'head', $priority); } /** - * Unregister an external file + * Unregister a CSS file + * + * @param string $name The identifier for the CSS file * - * @param string $type Type of file: javascript or css - * @param string $id The identifier of the file - * @param string $url Optional URL to search for if the id is not specified - * @param string $location Location in the page * @return bool + * @since 1.8.0 */ -function elgg_unregister_external_file($type, $id = '', $url = '', $location = 'head') { - global $CONFIG; - - if (!isset($CONFIG->externals)) { - return false; - } - - if (!isset($CONFIG->externals[$type])) { - return false; - } - - if (!isset($CONFIG->externals[$type][$location])) { - return false; - } - - if (array_key_exists($id, $CONFIG->externals[$type][$location])) { - unset($CONFIG->externals[$type][$location][$id]); - return true; - } - - // was not registered with an id so do a search for the url - $key = array_search($url, $CONFIG->externals[$type][$location]); - if ($key) { - unset($CONFIG->externals[$type][$location][$key]); - return true; - } - - return false; +function elgg_unregister_css($name) { + return elgg_unregister_external_file('css', $name); } /** - * Get the JavaScript URLs + * Load a CSS file for this page * - * @param string $location 'head' or 'footer' + * This must be called before elgg_view_page(). It can be called before the + * CSS file is registered. If you do not want a CSS file loaded, unregister it. * - * @return array + * @param string $name Identifier of the CSS file + * + * @return void + * @since 1.8.0 */ -function elgg_get_js($location = 'head') { - return elgg_get_external_file('javascript', $location); +function elgg_load_css($name) { + elgg_load_external_file('css', $name); } /** - * Get the CSS URLs + * Get the loaded CSS URLs * * @return array + * @since 1.8.0 */ -function elgg_get_css() { - return elgg_get_external_file('css', 'head'); +function elgg_get_loaded_css() { + return elgg_get_loaded_external_files('css', 'head'); } /** - * Get external resource descriptors + * Core registration function for external files * - * @param string $type Type of resource - * @param string $location Page location - * @return array + * @param string $type Type of external resource (js or css) + * @param string $name Identifier used as key + * @param string $url URL + * @param string $location Location in the page to include the file + * @param int $priority Loading priority of the file + * + * @return bool + * @since 1.8.0 */ -function elgg_get_external_file($type, $location) { +function elgg_register_external_file($type, $name, $url, $location, $priority = 500) { global $CONFIG; - if (isset($CONFIG->externals) && - isset($CONFIG->externals[$type]) && - isset($CONFIG->externals[$type][$location])) { + if (empty($name) || empty($url)) { + return false; + } + + $url = elgg_format_url($url); + $url = elgg_normalize_url($url); + + elgg_bootstrap_externals_data_structure($type); - return array_values($CONFIG->externals[$type][$location]); + $name = trim(strtolower($name)); + + // normalize bogus priorities, but allow empty, null, and false to be defaults. + if (!is_numeric($priority)) { + $priority = 500; } - return array(); + + // no negative priorities right now. + $priority = max((int)$priority, 0); + + $item = elgg_extract($name, $CONFIG->externals_map[$type]); + + if ($item) { + // updating a registered item + // don't update loaded because it could already be set + $item->url = $url; + $item->location = $location; + + // if loaded before registered, that means it hasn't been added to the list yet + if ($CONFIG->externals[$type]->contains($item)) { + $priority = $CONFIG->externals[$type]->move($item, $priority); + } else { + $priority = $CONFIG->externals[$type]->add($item, $priority); + } + } else { + $item = new stdClass(); + $item->loaded = false; + $item->url = $url; + $item->location = $location; + + $priority = $CONFIG->externals[$type]->add($item, $priority); + } + + $CONFIG->externals_map[$type][$name] = $item; + + return $priority !== false; } /** - * Returns the HTML for "likes" and "like this" on entities. - * - * @param ElggEntity $entity The entity to like + * Unregister an external file * - * @return string|false The HTML for the likes, or false on failure + * @param string $type Type of file: js or css + * @param string $name The identifier of the file * - * @since 1.8 - * @see @elgg_view likes/forms/edit + * @return bool + * @since 1.8.0 */ -function elgg_view_likes($entity) { - if (!($entity instanceof ElggEntity)) { - return false; - } +function elgg_unregister_external_file($type, $name) { + global $CONFIG; - $params = array('entity' => $entity); + elgg_bootstrap_externals_data_structure($type); - if ($likes = elgg_trigger_plugin_hook('likes', $entity->getType(), $params, false)) { - return $likes; - } else { - $likes = elgg_view('likes/forms/edit', $params); - return $likes; + $name = trim(strtolower($name)); + $item = elgg_extract($name, $CONFIG->externals_map[$type]); + + if ($item) { + unset($CONFIG->externals_map[$type][$name]); + return $CONFIG->externals[$type]->remove($item); } + + return false; } /** - * Count the number of likes attached to an entity + * Load an external resource for use on this page * - * @param ElggEntity $entity The entity to count likes for + * @param string $type Type of file: js or css + * @param string $name The identifier for the file * - * @return int Number of likes - * @since 1.8 + * @return void + * @since 1.8.0 */ -function elgg_count_likes($entity) { - if ($likeno = elgg_trigger_plugin_hook('likes:count', $entity->getType(), - array('entity' => $entity), false)) { - return $likeno; +function elgg_load_external_file($type, $name) { + global $CONFIG; + + elgg_bootstrap_externals_data_structure($type); + + $name = trim(strtolower($name)); + + $item = elgg_extract($name, $CONFIG->externals_map[$type]); + + if ($item) { + // update a registered item + $item->loaded = true; } else { - return count_annotations($entity->getGUID(), "", "", "likes"); + $item = new stdClass(); + $item->loaded = true; + $item->url = ''; + $item->location = ''; + + $CONFIG->externals[$type]->add($item); + $CONFIG->externals_map[$type][$name] = $item; } } /** - * Count the number of comments attached to an entity + * Get external resource descriptors * - * @param ElggEntity $entity The entity to count comments for + * @param string $type Type of file: js or css + * @param string $location Page location * - * @return int Number of comments + * @return array + * @since 1.8.0 */ -function elgg_count_comments($entity) { - if ($commentno = elgg_trigger_plugin_hook('comments:count', $entity->getType(), - array('entity' => $entity), false)) { - return $commentno; - } else { - return count_annotations($entity->getGUID(), "", "", "generic_comment"); +function elgg_get_loaded_external_files($type, $location) { + global $CONFIG; + + if (isset($CONFIG->externals) && $CONFIG->externals[$type] instanceof ElggPriorityList) { + $items = $CONFIG->externals[$type]->getElements(); + + $callback = "return \$v->loaded == true && \$v->location == '$location';"; + $items = array_filter($items, create_function('$v', $callback)); + if ($items) { + array_walk($items, create_function('&$v,$k', '$v = $v->url;')); + } + return $items; } + return array(); } /** - * Returns all php files in a directory. - * - * @deprecated 1.7 Use elgg_get_file_list() instead + * Bootstraps the externals data structure in $CONFIG. * - * @param string $directory Directory to look in - * @param array $exceptions Array of extensions (with .!) to ignore - * @param array $list A list files to include in the return - * - * @return array + * @param string $type The type of external, js or css. + * @access private */ -function get_library_files($directory, $exceptions = array(), $list = array()) { - elgg_deprecated_notice('get_library_files() deprecated by elgg_get_file_list()', 1.7); - return elgg_get_file_list($directory, $exceptions, $list, array('.php')); +function elgg_bootstrap_externals_data_structure($type) { + global $CONFIG; + + if (!isset($CONFIG->externals)) { + $CONFIG->externals = array(); + } + + if (!isset($CONFIG->externals[$type]) || !$CONFIG->externals[$type] instanceof ElggPriorityList) { + $CONFIG->externals[$type] = new ElggPriorityList(); + } + + if (!isset($CONFIG->externals_map)) { + $CONFIG->externals_map = array(); + } + + if (!isset($CONFIG->externals_map[$type])) { + $CONFIG->externals_map[$type] = array(); + } } /** @@ -447,6 +496,8 @@ function sanitise_filepath($path, $append_slash = TRUE) { // Convert to correct UNIX paths $path = str_replace('\\', '/', $path); $path = str_replace('../', '/', $path); + // replace // with / except when preceeded by : + $path = preg_replace("/([^:])\/\//", "$1/", $path); // Sort trailing slash $path = trim($path); @@ -461,125 +512,6 @@ function sanitise_filepath($path, $append_slash = TRUE) { } /** - * Adds an entry in $CONFIG[$register_name][$subregister_name]. - * - * This is only used for the site-wide menu. See {@link add_menu()}. - * - * @param string $register_name The name of the top-level register - * @param string $subregister_name The name of the subregister - * @param mixed $subregister_value The value of the subregister - * @param array $children_array Optionally, an array of children - * - * @return true|false Depending on success - * @todo Can be deprecated when the new menu system is introduced. - */ -function add_to_register($register_name, $subregister_name, $subregister_value, -$children_array = array()) { - - global $CONFIG; - - if (empty($register_name) || empty($subregister_name)) { - return false; - } - - if (!isset($CONFIG->registers)) { - $CONFIG->registers = array(); - } - - if (!isset($CONFIG->registers[$register_name])) { - $CONFIG->registers[$register_name] = array(); - } - - $subregister = new stdClass; - $subregister->name = $subregister_name; - $subregister->value = $subregister_value; - - if (is_array($children_array)) { - $subregister->children = $children_array; - } - - $CONFIG->registers[$register_name][$subregister_name] = $subregister; - return true; -} - -/** - * Removes a register entry from $CONFIG[register_name][subregister_name] - * - * This is used to by {@link remove_menu()} to remove site-wide menu items. - * - * @param string $register_name The name of the top-level register - * @param string $subregister_name The name of the subregister - * - * @return true|false Depending on success - * @since 1.7.0 - * @todo Can be deprecated when the new menu system is introduced. - */ -function remove_from_register($register_name, $subregister_name) { - global $CONFIG; - - if (empty($register_name) || empty($subregister_name)) { - return false; - } - - if (!isset($CONFIG->registers)) { - return false; - } - - if (!isset($CONFIG->registers[$register_name])) { - return false; - } - - if (isset($CONFIG->registers[$register_name][$subregister_name])) { - unset($CONFIG->registers[$register_name][$subregister_name]); - return true; - } - - return false; -} - -/** - * Constructs and returns a register object. - * - * @param string $register_name The name of the register - * @param mixed $register_value The value of the register - * @param array $children_array Optionally, an array of children - * - * @return false|stdClass Depending on success - * @todo Can be deprecated when the new menu system is introduced. - */ -function make_register_object($register_name, $register_value, $children_array = array()) { - elgg_deprecated_notice('make_register_object() is deprecated by add_submenu_item()', 1.7); - if (empty($register_name) || empty($register_value)) { - return false; - } - - $register = new stdClass; - $register->name = $register_name; - $register->value = $register_value; - $register->children = $children_array; - - return $register; -} - -/** - * If it exists, returns a particular register as an array - * - * @param string $register_name The name of the register - * - * @return array|false Depending on success - * @todo Can be deprecated when the new menu system is introduced. - */ -function get_register($register_name) { - global $CONFIG; - - if (isset($CONFIG->registers[$register_name])) { - return $CONFIG->registers[$register_name]; - } - - return false; -} - -/** * Queues a message to be displayed. * * Messages will not be displayed immediately, but are stored in @@ -587,7 +519,7 @@ function get_register($register_name) { * * The method of displaying these messages differs depending upon plugins and * viewtypes. The core default viewtype retrieves messages in - * {@link views/default/page_shells/default.php} and displays messages as + * {@link views/default/page/shells/default.php} and displays messages as * javascript popups. * * @internal Messages are stored as strings in the $_SESSION['msg'][$register] array. @@ -601,10 +533,10 @@ function get_register($register_name) { * 'messages') as well as {@link register_error()} messages ($register = 'errors'). * * @param mixed $message Optionally, a single message or array of messages to add, (default: null) - * @param string $register Types of message: "errors", "messages" (default: messages) + * @param string $register Types of message: "error", "success" (default: success) * @param bool $count Count the number of messages (default: false) * - * @return true|false|array Either the array of messages, or a response regarding + * @return bool|array Either the array of messages, or a response regarding * whether the message addition was successful. * @todo Clean up. Separate registering messages and retrieving them. */ @@ -638,7 +570,7 @@ function system_messages($message = null, $register = "success", $count = false) return sizeof($_SESSION['msg'][$register]); } else { $count = 0; - foreach ($_SESSION['msg'] as $register => $submessages) { + foreach ($_SESSION['msg'] as $submessages) { $count += sizeof($submessages); } return $count; @@ -685,33 +617,6 @@ function register_error($error) { } /** - * Deprecated events core function. Code divided between elgg_register_event_handler() - * and trigger_elgg_event(). - * - * @param string $event The type of event (eg 'init', 'update', 'delete') - * @param string $object_type The type of object (eg 'system', 'blog', 'user') - * @param string $function The name of the function that will handle the event - * @param int $priority Priority to call handler. Lower numbers called first (default 500) - * @param boolean $call Set to true to call the event rather than add to it (default false) - * @param mixed $object Optionally, the object the event is being performed on (eg a user) - * - * @return true|false Depending on success - * @deprecated 1.8 Use explicit register/trigger event functions - */ -function events($event = "", $object_type = "", $function = "", $priority = 500, -$call = false, $object = null) { - - elgg_deprecated_notice('events() has been deprecated.', 1.8); - - // leaving this here just in case someone was directly calling this internal function - if (!$call) { - return elgg_register_event_handler($event, $object_type, $function, $priority); - } else { - return trigger_elgg_event($event, $object_type, $object); - } -} - -/** * Register a callback as an Elgg event handler. * * Events are emitted by Elgg when certain actions occur. Plugins @@ -774,7 +679,7 @@ function elgg_register_event_handler($event, $object_type, $callback, $priority global $CONFIG; if (empty($event) || empty($object_type)) { - return FALSE; + return false; } if (!isset($CONFIG->events)) { @@ -787,8 +692,8 @@ function elgg_register_event_handler($event, $object_type, $callback, $priority $CONFIG->events[$event][$object_type] = array(); } - if (!is_callable($callback)) { - return FALSE; + if (!is_callable($callback, true)) { + return false; } $priority = max((int) $priority, 0); @@ -798,15 +703,7 @@ function elgg_register_event_handler($event, $object_type, $callback, $priority } $CONFIG->events[$event][$object_type][$priority] = $callback; ksort($CONFIG->events[$event][$object_type]); - return TRUE; -} - -/** - * @deprecated 1.8 Use elgg_register_event_handler() instead - */ -function register_elgg_event_handler($event, $object_type, $callback, $priority = 500) { - elgg_deprecated_notice("register_elgg_event_handler() was deprecated by elgg_register_event_handler()", 1.8); - return elgg_register_event_handler($event, $object_type, $callback, $priority); + return true; } /** @@ -821,22 +718,17 @@ function register_elgg_event_handler($event, $object_type, $callback, $priority */ function elgg_unregister_event_handler($event, $object_type, $callback) { global $CONFIG; - foreach ($CONFIG->events[$event][$object_type] as $key => $event_callback) { - if ($event_callback == $callback) { - unset($CONFIG->events[$event][$object_type][$key]); + + if (isset($CONFIG->events[$event]) && isset($CONFIG->events[$event][$object_type])) { + foreach ($CONFIG->events[$event][$object_type] as $key => $event_callback) { + if ($event_callback == $callback) { + unset($CONFIG->events[$event][$object_type][$key]); + } } } } /** - * @deprecated 1.8 Use elgg_unregister_event_handler instead - */ -function unregister_elgg_event_handler($event, $object_type, $callback) { - elgg_deprecated_notice('unregister_elgg_event_handler => elgg_unregister_event_handler', 1.8); - elgg_unregister_event_handler($event, $object_type, $callback); -} - -/** * Trigger an Elgg Event and run all handler callbacks registered to that event, type. * * This function runs all handlers registered to $event, $object_type or @@ -854,7 +746,7 @@ function unregister_elgg_event_handler($event, $object_type, $callback) { * @tip When referring to events, the preferred syntax is "event, type". * * @internal Only rarely should events be changed, added, or removed in core. - * When making changes to events, be sure to first create a ticket in trac. + * When making changes to events, be sure to first create a ticket on Github. * * @internal @tip Think of $object_type as the primary namespace element, and * $event as the secondary namespace. @@ -870,34 +762,33 @@ function unregister_elgg_event_handler($event, $object_type, $callback) { function elgg_trigger_event($event, $object_type, $object = null) { global $CONFIG; - $events = array( - $CONFIG->events[$event][$object_type], - $CONFIG->events['all'][$object_type], - $CONFIG->events[$event]['all'], - $CONFIG->events['all']['all'], - ); + $events = array(); + if (isset($CONFIG->events[$event][$object_type])) { + $events[] = $CONFIG->events[$event][$object_type]; + } + if (isset($CONFIG->events['all'][$object_type])) { + $events[] = $CONFIG->events['all'][$object_type]; + } + if (isset($CONFIG->events[$event]['all'])) { + $events[] = $CONFIG->events[$event]['all']; + } + if (isset($CONFIG->events['all']['all'])) { + $events[] = $CONFIG->events['all']['all']; + } $args = array($event, $object_type, $object); foreach ($events as $callback_list) { if (is_array($callback_list)) { foreach ($callback_list as $callback) { - if (call_user_func_array($callback, $args) === FALSE) { - return FALSE; + if (is_callable($callback) && (call_user_func_array($callback, $args) === false)) { + return false; } } } } - return TRUE; -} - -/** - * @deprecated 1.8 Use elgg_trigger_event() instead - */ -function trigger_elgg_event($event, $object_type, $object = null) { - elgg_deprecated_notice('trigger_elgg_event() was deprecated by elgg_trigger_event()', 1.8); - return elgg_trigger_event($event, $object_type, $object); + return true; } /** @@ -956,7 +847,7 @@ function trigger_elgg_event($event, $object_type, $object = null) { * * @param string $hook The name of the hook * @param string $type The type of the hook - * @param callback $callback The name of a valid function or an array with object and method + * @param callable $callback The name of a valid function or an array with object and method * @param int $priority The priority - 500 is default, lower numbers called first * * @return bool @@ -964,13 +855,13 @@ function trigger_elgg_event($event, $object_type, $object = null) { * @example hooks/register/basic.php Registering for a plugin hook and examining the variables. * @example hooks/register/advanced.php Registering for a plugin hook and changing the params. * @link http://docs.elgg.org/Tutorials/Plugins/Hooks - * @since 1.8 + * @since 1.8.0 */ function elgg_register_plugin_hook_handler($hook, $type, $callback, $priority = 500) { global $CONFIG; if (empty($hook) || empty($type)) { - return FALSE; + return false; } if (!isset($CONFIG->hooks)) { @@ -983,8 +874,8 @@ function elgg_register_plugin_hook_handler($hook, $type, $callback, $priority = $CONFIG->hooks[$hook][$type] = array(); } - if (!is_callable($callback)) { - return FALSE; + if (!is_callable($callback, true)) { + return false; } $priority = max((int) $priority, 0); @@ -994,15 +885,7 @@ function elgg_register_plugin_hook_handler($hook, $type, $callback, $priority = } $CONFIG->hooks[$hook][$type][$priority] = $callback; ksort($CONFIG->hooks[$hook][$type]); - return TRUE; -} - -/** - * @deprecated 1.8 Use elgg_register_plugin_hook_handler() instead - */ -function register_plugin_hook($hook, $type, $callback, $priority = 500) { - elgg_deprecated_notice("register_plugin_hook() was deprecated by elgg_register_plugin_hook_handler()", 1.8); - return elgg_register_plugin_hook_handler($hook, $type, $callback, $priority); + return true; } /** @@ -1010,29 +893,24 @@ function register_plugin_hook($hook, $type, $callback, $priority = 500) { * * @param string $hook The name of the hook * @param string $entity_type The name of the type of entity (eg "user", "object" etc) - * @param callback $callback The PHP callback to be removed + * @param callable $callback The PHP callback to be removed * * @return void - * @since 1.8 + * @since 1.8.0 */ function elgg_unregister_plugin_hook_handler($hook, $entity_type, $callback) { global $CONFIG; - foreach ($CONFIG->hooks[$hook][$entity_type] as $key => $hook_callback) { - if ($hook_callback == $callback) { - unset($CONFIG->hooks[$hook][$entity_type][$key]); + + if (isset($CONFIG->hooks[$hook]) && isset($CONFIG->hooks[$hook][$entity_type])) { + foreach ($CONFIG->hooks[$hook][$entity_type] as $key => $hook_callback) { + if ($hook_callback == $callback) { + unset($CONFIG->hooks[$hook][$entity_type][$key]); + } } } } /** - * @deprecated 1.8 Use elgg_unregister_plugin_hook_handler() instead - */ -function unregister_plugin_hook($hook, $entity_type, $callback) { - elgg_deprecated_notice("unregister_plugin_hook() was deprecated by elgg_unregister_plugin_hook_handler()", 1.8); - elgg_unregister_plugin_hook_handler($hook, $entity_type, $callback); -} - -/** * Trigger a Plugin Hook and run all handler callbacks registered to that hook:type. * * This function runs all handlers regsitered to $hook, $type or @@ -1054,6 +932,12 @@ function unregister_plugin_hook($hook, $entity_type, $callback) { * called for all hooks of type $event, regardless of $object_type. If $hook * and $type both are 'all', the handler will be called for all hooks. * + * @internal The checks for $hook and/or $type not being equal to 'all' is to + * prevent a plugin hook being registered with an 'all' being called more than + * once if the trigger occurs with an 'all'. An example in core of this is in + * actions.php: + * elgg_trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', ...) + * * @see elgg_register_plugin_hook_handler() * * @param string $hook The name of the hook to trigger ("all" will @@ -1072,25 +956,40 @@ function unregister_plugin_hook($hook, $entity_type, $callback) { * @example hooks/basic.php Trigger and respond to a basic plugin hook. * @link http://docs.elgg.org/Tutorials/Plugins/Hooks * - * @since 1.8 + * @since 1.8.0 */ function elgg_trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null) { global $CONFIG; - $hooks = array( - $CONFIG->hooks[$hook][$type], - $CONFIG->hooks['all'][$type], - $CONFIG->hooks[$hook]['all'], - $CONFIG->hooks['all']['all'], - ); + $hooks = array(); + if (isset($CONFIG->hooks[$hook][$type])) { + if ($hook != 'all' && $type != 'all') { + $hooks[] = $CONFIG->hooks[$hook][$type]; + } + } + if (isset($CONFIG->hooks['all'][$type])) { + if ($type != 'all') { + $hooks[] = $CONFIG->hooks['all'][$type]; + } + } + if (isset($CONFIG->hooks[$hook]['all'])) { + if ($hook != 'all') { + $hooks[] = $CONFIG->hooks[$hook]['all']; + } + } + if (isset($CONFIG->hooks['all']['all'])) { + $hooks[] = $CONFIG->hooks['all']['all']; + } foreach ($hooks as $callback_list) { if (is_array($callback_list)) { foreach ($callback_list as $hookcallback) { - $args = array($hook, $type, $returnvalue, $params); - $temp_return_value = call_user_func_array($hookcallback, $args); - if (!is_null($temp_return_value)) { - $returnvalue = $temp_return_value; + if (is_callable($hookcallback)) { + $args = array($hook, $type, $returnvalue, $params); + $temp_return_value = call_user_func_array($hookcallback, $args); + if (!is_null($temp_return_value)) { + $returnvalue = $temp_return_value; + } } } } @@ -1100,15 +999,7 @@ function elgg_trigger_plugin_hook($hook, $type, $params = null, $returnvalue = n } /** - * @deprecated 1.8 Use elgg_trigger_plugin_hook() instead - */ -function trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null) { - elgg_deprecated_notice("trigger_plugin_hook() was deprecated by elgg_trigger_plugin_hook()", 1.8); - return elgg_trigger_plugin_hook($hook, $type, $params, $returnvalue); -} - -/** - * Intercepts, logs, and display uncaught exceptions. + * Intercepts, logs, and displays uncaught exceptions. * * @warning This function should never be called directly. * @@ -1117,9 +1008,11 @@ function trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null) * @param Exception $exception The exception being handled * * @return void + * @access private */ function _elgg_php_exception_handler($exception) { - error_log("*** FATAL EXCEPTION *** : " . $exception); + $timestamp = time(); + error_log("Exception #$timestamp: $exception"); // Wipe any existing output buffer ob_end_clean(); @@ -1128,11 +1021,31 @@ function _elgg_php_exception_handler($exception) { header("Cache-Control: no-cache, must-revalidate", true); header('Expires: Fri, 05 Feb 1982 00:00:00 -0500', true); // @note Do not send a 500 header because it is not a server error - //header("Internal Server Error", true, 500); - elgg_set_viewtype('failsafe'); - $body = elgg_view("messages/exceptions/exception", array('object' => $exception)); - echo elgg_view_page(elgg_echo('exception:title'), $body); + try { + // we don't want the 'pagesetup', 'system' event to fire + global $CONFIG; + $CONFIG->pagesetupdone = true; + + elgg_set_viewtype('failsafe'); + if (elgg_is_admin_logged_in()) { + $body = elgg_view("messages/exceptions/admin_exception", array( + 'object' => $exception, + 'ts' => $timestamp + )); + } else { + $body = elgg_view("messages/exceptions/exception", array( + 'object' => $exception, + 'ts' => $timestamp + )); + } + echo elgg_view_page(elgg_echo('exception:title'), $body); + } catch (Exception $e) { + $timestamp = time(); + $message = $e->getMessage(); + echo "Fatal error in exception handler. Check log for Exception #$timestamp"; + error_log("Exception #$timestamp : fatal error in exception handler : $message"); + } } /** @@ -1155,6 +1068,9 @@ function _elgg_php_exception_handler($exception) { * @param array $vars An array that points to the active symbol table where error occurred * * @return true + * @throws Exception + * @access private + * @todo Replace error_log calls with elgg_log calls. */ function _elgg_php_error_handler($errno, $errmsg, $filename, $linenum, $vars) { $error = date("Y-m-d H:i:s (T)") . ": \"$errmsg\" in file $filename (line $linenum)"; @@ -1170,7 +1086,12 @@ function _elgg_php_error_handler($errno, $errmsg, $filename, $linenum, $vars) { case E_WARNING : case E_USER_WARNING : - error_log("PHP WARNING: $error"); + case E_RECOVERABLE_ERROR: // (e.g. type hint violation) + + // check if the error wasn't suppressed by the error control operator (@) + if (error_reporting()) { + error_log("PHP WARNING: $error"); + } break; default: @@ -1194,8 +1115,8 @@ function _elgg_php_error_handler($errno, $errmsg, $filename, $linenum, $vars) { * * @note No messages will be displayed unless debugging has been enabled. * - * @param str $message User message - * @param str $level NOTICE | WARNING | ERROR | DEBUG + * @param string $message User message + * @param string $level NOTICE | WARNING | ERROR | DEBUG * * @return bool * @since 1.7.0 @@ -1257,9 +1178,11 @@ function elgg_dump($value, $to_screen = TRUE, $level = 'NOTICE') { global $CONFIG; // plugin can return false to stop the default logging method - $params = array('level' => $level, - 'msg' => $value, - 'to_screen' => $to_screen); + $params = array( + 'level' => $level, + 'msg' => $value, + 'to_screen' => $to_screen, + ); if (!elgg_trigger_plugin_hook('debug', 'log', $params, true)) { return; } @@ -1271,6 +1194,11 @@ function elgg_dump($value, $to_screen = TRUE, $level = 'NOTICE') { $to_screen = FALSE; } + // Do not want to write to JS or CSS pages + if (elgg_in_context('js') || elgg_in_context('css')) { + $to_screen = FALSE; + } + if ($to_screen == TRUE) { echo '<pre>'; print_r($value); @@ -1285,7 +1213,9 @@ function elgg_dump($value, $to_screen = TRUE, $level = 'NOTICE') { * * This function either displays or logs the deprecation message, * depending upon the deprecation policies in {@link CODING.txt}. - * Logged messages are sent with the level of 'WARNING'. + * Logged messages are sent with the level of 'WARNING'. Only admins + * get visual deprecation notices. When non-admins are logged in, the + * notices are sent to PHP's log through elgg_dump(). * * A user-visual message will be displayed if $dep_version is greater * than 1 minor releases lower than the current Elgg version, or at all @@ -1296,216 +1226,77 @@ function elgg_dump($value, $to_screen = TRUE, $level = 'NOTICE') { * * @see CODING.txt * - * @param str $msg Message to log / display. - * @param str $dep_version Human-readable *release* version: 1.7, 1.7.3 + * @param string $msg Message to log / display. + * @param string $dep_version Human-readable *release* version: 1.7, 1.8, ... + * @param int $backtrace_level How many levels back to display the backtrace. + * Useful if calling from functions that are called + * from other places (like elgg_view()). Set to -1 + * for a full backtrace. * * @return bool * @since 1.7.0 */ -function elgg_deprecated_notice($msg, $dep_version) { +function elgg_deprecated_notice($msg, $dep_version, $backtrace_level = 1) { // if it's a major release behind, visual and logged - // if it's a 2 minor releases behind, visual and logged - // if it's 1 minor release behind, logged. - // bugfixes don't matter because you're not deprecating between them, RIGHT? + // if it's a 1 minor release behind, visual and logged + // if it's for current minor release, logged. + // bugfixes don't matter because we are not deprecating between them + if (!$dep_version) { - return FALSE; + return false; } - $elgg_version = get_version(TRUE); + $elgg_version = get_version(true); $elgg_version_arr = explode('.', $elgg_version); - $elgg_major_version = $elgg_version_arr[0]; - $elgg_minor_version = $elgg_version_arr[1]; - - $dep_version_arr = explode('.', $dep_version); - $dep_major_version = $dep_version_arr[0]; - $dep_minor_version = $dep_version_arr[1]; + $elgg_major_version = (int)$elgg_version_arr[0]; + $elgg_minor_version = (int)$elgg_version_arr[1]; - $last_working_version = $dep_minor_version - 1; + $dep_major_version = (int)$dep_version; + $dep_minor_version = 10 * ($dep_version - $dep_major_version); - $visual = FALSE; + $visual = false; - // use version_compare to account for 1.7a < 1.7 - if (($dep_major_version < $elgg_major_version) - || (($elgg_minor_version - $last_working_version) > 1)) { - $visual = TRUE; + if (($dep_major_version < $elgg_major_version) || + ($dep_minor_version < $elgg_minor_version)) { + $visual = true; } - $msg = "Deprecated in $dep_version: $msg"; + $msg = "Deprecated in $dep_major_version.$dep_minor_version: $msg"; - if ($visual) { + if ($visual && elgg_is_admin_logged_in()) { register_error($msg); } // Get a file and line number for the log. Never show this in the UI. // Skip over the function that sent this notice and see who called the deprecated // function itself. + $msg .= " Called from "; + $stack = array(); $backtrace = debug_backtrace(); - $caller = $backtrace[1]; - $msg .= " (Called from {$caller['file']}:{$caller['line']})"; + // never show this call. + array_shift($backtrace); + $i = count($backtrace); - elgg_log($msg, 'WARNING'); + foreach ($backtrace as $trace) { + $stack[] = "[#$i] {$trace['file']}:{$trace['line']}"; + $i--; - return TRUE; -} - - -/** - * Checks if code is being called from a certain function. - * - * To use, call this function with the function name (and optional - * file location) that it has to be called from, it will either - * return true or false. - * - * e.g. - * - * function my_secure_function() - * { - * if (!call_gatekeeper("my_call_function")) - * return false; - * - * ... do secure stuff ... - * } - * - * function my_call_function() - * { - * // will work - * my_secure_function(); - * } - * - * function bad_function() - * { - * // Will not work - * my_secure_function(); - * } - * - * @param mixed $function The function that this function must have in its call stack, - * to test against a method pass an array containing a class and - * method name. - * @param string $file Optional file that the function must reside in. - * - * @return bool - * - * @deprecated 1.8 A neat but pointless function - */ -function call_gatekeeper($function, $file = "") { - elgg_deprecated_notice("call_gatekeeper() is neat but pointless", 1.8); - // Sanity check - if (!$function) { - return false; - } - - // Check against call stack to see if this is being called from the correct location - $callstack = debug_backtrace(); - $stack_element = false; - - foreach ($callstack as $call) { - if (is_array($function)) { - if ( - (strcmp($call['class'], $function[0]) == 0) && - (strcmp($call['function'], $function[1]) == 0) - ) { - $stack_element = $call; - } - } else { - if (strcmp($call['function'], $function) == 0) { - $stack_element = $call; + if ($backtrace_level > 0) { + if ($backtrace_level <= 1) { + break; } + $backtrace_level--; } } - if (!$stack_element) { - return false; - } + $msg .= implode("<br /> -> ", $stack); - // If file then check that this it is being called from this function - if ($file) { - $mirror = null; - - if (is_array($function)) { - $mirror = new ReflectionMethod($function[0], $function[1]); - } else { - $mirror = new ReflectionFunction($function); - } - - if ((!$mirror) || (strcmp($file, $mirror->getFileName()) != 0)) { - return false; - } - } + elgg_log($msg, 'WARNING'); return true; } /** - * This function checks to see if it is being called at somepoint by a function defined somewhere - * on a given path (optionally including subdirectories). - * - * This function is similar to call_gatekeeper() but returns true if it is being called - * by a method or function which has been defined on a given path or by a specified file. - * - * @param string $path The full path and filename that this function must have - * in its call stack If a partial path is given and - * $include_subdirs is true, then the function will return - * true if called by any function in or below the specified path. - * @param bool $include_subdirs Are subdirectories of the path ok, or must you specify an - * absolute path and filename. - * @param bool $strict_mode If true then the calling method or function must be directly - * called by something on $path, if false the whole call stack is - * searched. - * - * @return void - * - * @deprecated 1.8 A neat but pointless function - */ -function callpath_gatekeeper($path, $include_subdirs = true, $strict_mode = false) { - elgg_deprecated_notice("callpath_gatekeeper() is neat but pointless", 1.8); - - global $CONFIG; - - $path = sanitise_string($path); - - if ($path) { - $callstack = debug_backtrace(); - - foreach ($callstack as $call) { - $call['file'] = str_replace("\\", "/", $call['file']); - - if ($include_subdirs) { - if (strpos($call['file'], $path) === 0) { - - if ($strict_mode) { - $callstack[1]['file'] = str_replace("\\", "/", $callstack[1]['file']); - if ($callstack[1] === $call) { - return true; - } - } else { - return true; - } - } - } else { - if (strcmp($path, $call['file']) == 0) { - if ($strict_mode) { - if ($callstack[1] === $call) { - return true; - } - } else { - return true; - } - } - } - - } - return false; - } - - if (isset($CONFIG->debug)) { - system_message("Gatekeeper'd function called from {$callstack[1]['file']}:" - . "{$callstack[1]['line']}\n\nStack trace:\n\n" . print_r($callstack, true)); - } - - return false; -} - -/** * Returns the current page's complete URL. * * The current URL is assembled using the network's wwwroot and the request URI @@ -1515,8 +1306,6 @@ function callpath_gatekeeper($path, $include_subdirs = true, $strict_mode = fals * @return string The current page URL. */ function current_page_url() { - global $CONFIG; - $url = parse_url(elgg_get_site_url()); $page = $url['scheme'] . "://"; @@ -1561,7 +1350,7 @@ function full_url() { "" : (":" . $_SERVER["SERVER_PORT"]); // This is here to prevent XSS in poorly written browsers used by 80% of the population. - // {@trac [5813]} + // https://github.com/Elgg/Elgg/commit/0c947e80f512cb0a482b1864fd0a6965c8a0cd4a $quotes = array('\'', '"'); $encoded = array('%27', '%22'); @@ -1577,7 +1366,7 @@ function full_url() { * @param array $parts Associative array of URL components like parse_url() returns * @param bool $html_encode HTML Encode the url? * - * @return str Full URL + * @return string Full URL * @since 1.7.0 */ function elgg_http_build_url(array $parts, $html_encode = TRUE) { @@ -1608,14 +1397,14 @@ function elgg_http_build_url(array $parts, $html_encode = TRUE) { * add tokens to the action. The form view automatically handles * tokens. * - * @param str $url Full action URL - * @param bool $html_encode HTML encode the url? + * @param string $url Full action URL + * @param bool $html_encode HTML encode the url? (default: false) * - * @return str URL with action tokens + * @return string URL with action tokens * @since 1.7.0 * @link http://docs.elgg.org/Tutorials/Actions */ -function elgg_add_action_tokens_to_url($url, $html_encode = TRUE) { +function elgg_add_action_tokens_to_url($url, $html_encode = FALSE) { $components = parse_url(elgg_normalize_url($url)); if (isset($components['query'])) { @@ -1637,23 +1426,6 @@ function elgg_add_action_tokens_to_url($url, $html_encode = TRUE) { return elgg_http_build_url($components, $html_encode); } - -/** - * Add action tokens to URL. - * - * @param string $url URL - * - * @return string - * - * @deprecated 1.7 final - */ -function elgg_validate_action_url($url) { - elgg_deprecated_notice('elgg_validate_action_url() deprecated by elgg_add_action_tokens_to_url().', - '1.7b'); - - return elgg_add_action_tokens_to_url($url); -} - /** * Removes an element from a URL's query string. * @@ -1680,17 +1452,17 @@ function elgg_http_remove_url_query_element($url, $element) { } $url_array['query'] = http_build_query($query); - $string = elgg_http_build_url($url_array); + $string = elgg_http_build_url($url_array, false); return $string; } /** * Adds an element or elements to a URL's query string. * - * @param str $url The URL - * @param array $elements Key/value pairs to add to the URL + * @param string $url The URL + * @param array $elements Key/value pairs to add to the URL * - * @return str The new URL with the query strings added + * @return string The new URL with the query strings added * @since 1.7.0 */ function elgg_http_add_url_query_elements($url, array $elements) { @@ -1707,7 +1479,7 @@ function elgg_http_add_url_query_elements($url, array $elements) { } $url_array['query'] = http_build_query($query); - $string = elgg_http_build_url($url_array); + $string = elgg_http_build_url($url_array, false); return $string; } @@ -1723,12 +1495,10 @@ function elgg_http_add_url_query_elements($url, array $elements) { * @param string $url2 Second URL * @param array $ignore_params GET params to ignore in the comparison * - * @return BOOL - * @since 1.8 + * @return bool + * @since 1.8.0 */ function elgg_http_url_is_identical($url1, $url2, $ignore_params = array('offset', 'limit')) { - global $CONFIG; - // if the server portion is missing but it starts with / then add the url in. // @todo use elgg_normalize_url() if (elgg_substr($url1, 0, 1) == '/') { @@ -1748,8 +1518,12 @@ function elgg_http_url_is_identical($url1, $url2, $ignore_params = array('offset $url1_info = parse_url($url1); $url2_info = parse_url($url2); - $url1_info['path'] = trim($url1_info['path'], '/'); - $url2_info['path'] = trim($url2_info['path'], '/'); + if (isset($url1_info['path'])) { + $url1_info['path'] = trim($url1_info['path'], '/'); + } + if (isset($url2_info['path'])) { + $url2_info['path'] = trim($url2_info['path'], '/'); + } // compare basic bits $parts = array('scheme', 'host', 'path'); @@ -1812,132 +1586,6 @@ function elgg_http_url_is_identical($url1, $url2, $ignore_params = array('offset } /** - * Load all the REQUEST variables into the sticky form cache - * - * Call this from an action when you want all your submitted variables - * available if the submission fails validation and is sent back to the form - * - * @param string $form_name Name of the sticky form - * - * @return void - * @link http://docs.elgg.org/Tutorials/UI/StickyForms - */ -function elgg_make_sticky_form($form_name) { - global $CONFIG; - - $CONFIG->active_sticky_form = $form_name; - elgg_clear_sticky_form($form_name); - - if (!isset($_SESSION['sticky_forms'])) { - $_SESSION['sticky_forms'] = array(); - } - $_SESSION['sticky_forms'][$form_name] = array(); - - foreach ($_REQUEST as $key => $var) { - // will go through XSS filtering on the get function - $_SESSION['sticky_forms'][$form_name][$key] = $var; - } -} - -/** - * Clear the sticky form cache - * - * Call this if validation is successful in the action handler or - * when they sticky values have been used to repopulate the form - * after a validation error. - * - * @param string $form_name Form namespace - * - * @return void - * @link http://docs.elgg.org/Tutorials/UI/StickyForms - */ -function elgg_clear_sticky_form($form_name) { - unset($_SESSION['sticky_forms'][$form_name]); -} - -/** - * Has this form been made sticky? - * - * @param string $form_name Form namespace - * - * @return boolean - * @link http://docs.elgg.org/Tutorials/UI/StickyForms - */ -function elgg_is_sticky_form($form_name) { - return isset($_SESSION['sticky_forms'][$form_name]); -} - -/** - * Get a specific sticky variable - * - * @param string $form_name The name of the form - * @param string $variable The name of the variable - * @param mixed $default Default value if the variable does not exist in sticky cache - * @param boolean $filter_result Filter for bad input if true - * - * @return mixed - * - * @todo should this filter the default value? - * @link http://docs.elgg.org/Tutorials/UI/StickyForms - */ -function elgg_get_sticky_value($form_name, $variable = '', $default = NULL, $filter_result = true) { - if (isset($_SESSION['sticky_forms'][$form_name][$variable])) { - $value = $_SESSION['sticky_forms'][$form_name][$variable]; - if ($filter_result) { - // XSS filter result - $value = filter_tags($value); - } - return $value; - } - return $default; -} - -/** - * Clear a specific sticky variable - * - * @param string $form_name The name of the form - * @param string $variable The name of the variable to clear - * - * @return void - * @link http://docs.elgg.org/Tutorials/UI/StickyForms - */ -function elgg_clear_sticky_value($form_name, $variable) { - unset($_SESSION['sticky_forms'][$form_name][$variable]); -} - -/** - * Returns the current active sticky form. - * - * @return mixed Str | FALSE - * @link http://docs.elgg.org/Tutorials/UI/StickyForms - */ -function elgg_get_active_sticky_form() { - global $CONFIG; - - if (isset($CONFIG->active_sticky_form)) { - $form_name = $CONFIG->active_sticky_form; - } else { - return FALSE; - } - - return (elgg_is_sticky_form($form_name)) ? $form_name : FALSE; -} - -/** - * Sets the active sticky form. - * - * @param string $form_name The name of the form - * - * @return void - * @link http://docs.elgg.org/Tutorials/UI/StickyForms - */ -function elgg_set_active_sticky_form($form_name) { - global $CONFIG; - - $CONFIG->active_sticky_form = $form_name; -} - -/** * Checks for $array[$key] and returns its value if it exists, else * returns $default. * @@ -1946,12 +1594,22 @@ function elgg_set_active_sticky_form($form_name) { * @param string $key The key to check. * @param array $array The array to check against. * @param mixed $default Default value to return if nothing is found. + * @param bool $strict Return array key if it's set, even if empty. If false, + * return $default if the array key is unset or empty. * - * @return void - * @since 1.8 + * @return mixed + * @since 1.8.0 */ -function elgg_get_array_value($key, array $array, $default = NULL) { - return (isset($array[$key])) ? $array[$key] : $default; +function elgg_extract($key, array $array, $default = null, $strict = true) { + if (!is_array($array)) { + return $default; + } + + if ($strict) { + return (isset($array[$key])) ? $array[$key] : $default; + } else { + return (isset($array[$key]) && !empty($array[$key])) ? $array[$key] : $default; + } } /** @@ -1979,7 +1637,7 @@ $sort_type = SORT_LOCALE_STRING) { $sort = array(); - foreach ($array as $k => $v) { + foreach ($array as $v) { if (isset($v[$element])) { $sort[] = strtolower($v[$element]); } else { @@ -1998,7 +1656,7 @@ $sort_type = SORT_LOCALE_STRING) { * * @param string $ini_get_arg The INI setting * - * @return true|false Depending on whether it's on or off + * @return bool Depending on whether it's on or off */ function ini_get_bool($ini_get_arg) { $temp = strtolower(ini_get($ini_get_arg)); @@ -2014,7 +1672,7 @@ function ini_get_bool($ini_get_arg) { * * @tip Use this for arithmetic when determining if a file can be uploaded. * - * @param str $setting The php.ini setting + * @param string $setting The php.ini setting * * @return int * @since 1.7.0 @@ -2029,8 +1687,10 @@ function elgg_get_ini_setting_in_bytes($setting) { switch($last) { case 'g': $val *= 1024; + // fallthrough intentional case 'm': $val *= 1024; + // fallthrough intentional case 'k': $val *= 1024; } @@ -2064,10 +1724,11 @@ function is_not_null($string) { * names by singular names. * * @param array $options The options array. $options['keys'] = 'values'; - * @param array $singulars A list of sinular words to pluralize by adding 's'. + * @param array $singulars A list of singular words to pluralize by adding 's'. * * @return array * @since 1.7.0 + * @access private */ function elgg_normalise_plural_options_array($options, $singulars) { foreach ($singulars as $singular) { @@ -2093,30 +1754,6 @@ function elgg_normalise_plural_options_array($options, $singulars) { } /** - * Does nothing. - * - * @deprecated 1.7 - * @return 0 - */ -function test_ip() { - elgg_deprecated_notice('test_ip() was removed because of licensing issues.', 1.7); - - return 0; -} - -/** - * Does nothing. - * - * @return bool - * @deprecated 1.7 - */ -function is_ip_in_array() { - elgg_deprecated_notice('is_ip_in_array() was removed because of licensing issues.', 1.7); - - return false; -} - -/** * Emits a shutdown:system event upon PHP shutdown, but before database connections are dropped. * * @tip Register for the shutdown:system event to perform functions at the end of page loads. @@ -2125,17 +1762,27 @@ function is_ip_in_array() { * useful. Servers will hold pages until processing is done before sending * them out to the browser. * + * @see http://www.php.net/register-shutdown-function + * * @return void * @see register_shutdown_hook() + * @access private */ function _elgg_shutdown_hook() { global $START_MICROTIME; - elgg_trigger_event('shutdown', 'system'); + try { + elgg_trigger_event('shutdown', 'system'); - $time = (float)(microtime(TRUE) - $START_MICROTIME); - // demoted to NOTICE from DEBUG so javascript is not corrupted - elgg_log("Page {$_SERVER['REQUEST_URI']} generated in $time seconds", 'NOTICE'); + $time = (float)(microtime(TRUE) - $START_MICROTIME); + // demoted to NOTICE from DEBUG so javascript is not corrupted + elgg_log("Page {$_SERVER['REQUEST_URI']} generated in $time seconds", 'NOTICE'); + } catch (Exception $e) { + $message = 'Error: ' . get_class($e) . ' thrown within the shutdown handler. '; + $message .= "Message: '{$e->getMessage()}' in file {$e->getFile()} (line {$e->getLine()})"; + error_log($message); + error_log("Exception trace stack: {$e->getTraceAsString()}"); + } } /** @@ -2146,22 +1793,51 @@ function _elgg_shutdown_hook() { * * @param array $page The page array * - * @return void + * @return bool * @elgg_pagehandler js + * @access private + */ +function elgg_js_page_handler($page) { + return elgg_cacheable_view_page_handler($page, 'js'); +} + +/** + * Serve individual views for Ajax. + * + * /ajax/view/<name of view>?<key/value params> + * + * @param array $page The page array + * + * @return bool + * @elgg_pagehandler ajax + * @access private */ -function js_page_handler($page) { +function elgg_ajax_page_handler($page) { if (is_array($page) && sizeof($page)) { - $js = substr($page[0], 0, strpos($page[0], '.')); - $return = elgg_view('js/' . $js); + // throw away 'view' and form the view name + unset($page[0]); + $view = implode('/', $page); - header('Content-type: text/javascript'); - header('Expires: ' . date('r', time() + 864000)); - header("Pragma: public"); - header("Cache-Control: public"); - header("Content-Length: " . strlen($return)); + $allowed_views = elgg_get_config('allowed_ajax_views'); + if (!array_key_exists($view, $allowed_views)) { + header('HTTP/1.1 403 Forbidden'); + exit; + } - echo $return; + // pull out GET parameters through filter + $vars = array(); + foreach ($_GET as $name => $value) { + $vars[$name] = get_input($name); + } + + if (isset($vars['guid'])) { + $vars['entity'] = get_entity($vars['guid']); + } + + echo elgg_view($view, $vars); + return true; } + return false; } /** @@ -2171,24 +1847,197 @@ function js_page_handler($page) { * * @param array $page The page array * - * @return void + * @return bool * @elgg_pagehandler css + * @access private */ -function css_page_handler($page) { +function elgg_css_page_handler($page) { if (!isset($page[0])) { // default css $page[0] = 'elgg'; } + + return elgg_cacheable_view_page_handler($page, 'css'); +} + +/** + * Serves a JS or CSS view with headers for caching. + * + * /<css||js>/name/of/view.<last_cache>.<css||js> + * + * @param array $page The page array + * @param string $type The type: js or css + * + * @return bool + * @access private + */ +function elgg_cacheable_view_page_handler($page, $type) { + + switch ($type) { + case 'js': + $content_type = 'text/javascript'; + break; + + case 'css': + $content_type = 'text/css'; + break; + + default: + return false; + break; + } + + if ($page) { + // the view file names can have multiple dots + // eg: views/default/js/calendars/jquery.fullcalendar.min.php + // translates to the url /js/calendars/jquery.fullcalendar.min.<ts>.js + // and the view js/calendars/jquery.fullcalendar.min + // we ignore the last two dots for the ts and the ext. + // Additionally, the timestamp is optional. + $page = implode('/', $page); + $regex = '|(.+?)\.([\d]+\.)?\w+$|'; + preg_match($regex, $page, $matches); + $view = $matches[1]; + $return = elgg_view("$type/$view"); - $css = substr($page[0], 0, strpos($page[0], '.')); - $return = elgg_view("css/$css"); + header("Content-type: $content_type"); - header("Content-type: text/css", true); - header('Expires: ' . date('r', time() + 86400000), true); - header("Pragma: public", true); - header("Cache-Control: public", true); + // @todo should js be cached when simple cache turned off + //header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+10 days")), true); + //header("Pragma: public"); + //header("Cache-Control: public"); + //header("Content-Length: " . strlen($return)); - echo $return; + echo $return; + return true; + } + return false; +} + +/** + * Reverses the ordering in an ORDER BY clause. This is achived by replacing + * asc with desc, or appending desc to the end of the clause. + * + * This is used mostly for elgg_get_entities() and other similar functions. + * + * @param string $order_by An order by clause + * @access private + * @return string + * @access private + */ +function elgg_sql_reverse_order_by_clause($order_by) { + $order_by = strtolower($order_by); + + if (strpos($order_by, ' asc') !== false) { + $return = str_replace(' asc', ' desc', $order_by); + } elseif (strpos($order_by, ' desc') !== false) { + $return = str_replace(' desc', ' asc', $order_by); + } else { + // no order specified, so default to desc since mysql defaults to asc + $return = $order_by . ' desc'; + } + + return $return; +} + +/** + * Enable objects with an enable() method. + * + * Used as a callback for ElggBatch. + * + * @todo why aren't these static methods on ElggBatch? + * + * @param object $object The object to enable + * @return bool + * @access private + */ +function elgg_batch_enable_callback($object) { + // our db functions return the number of rows affected... + return $object->enable() ? true : false; +} + +/** + * Disable objects with a disable() method. + * + * Used as a callback for ElggBatch. + * + * @param object $object The object to disable + * @return bool + * @access private + */ +function elgg_batch_disable_callback($object) { + // our db functions return the number of rows affected... + return $object->disable() ? true : false; +} + +/** + * Delete objects with a delete() method. + * + * Used as a callback for ElggBatch. + * + * @param object $object The object to disable + * @return bool + * @access private + */ +function elgg_batch_delete_callback($object) { + // our db functions return the number of rows affected... + return $object->delete() ? true : false; +} + +/** + * Checks if there are some constraints on the options array for + * potentially dangerous operations. + * + * @param array $options Options array + * @param string $type Options type: metadata or annotations + * @return bool + * @access private + */ +function elgg_is_valid_options_for_batch_operation($options, $type) { + if (!$options || !is_array($options)) { + return false; + } + + // at least one of these is required. + $required = array( + // generic restraints + 'guid', 'guids' + ); + + switch ($type) { + case 'metadata': + $metadata_required = array( + 'metadata_owner_guid', 'metadata_owner_guids', + 'metadata_name', 'metadata_names', + 'metadata_value', 'metadata_values' + ); + + $required = array_merge($required, $metadata_required); + break; + + case 'annotations': + case 'annotation': + $annotations_required = array( + 'annotation_owner_guid', 'annotation_owner_guids', + 'annotation_name', 'annotation_names', + 'annotation_value', 'annotation_values' + ); + + $required = array_merge($required, $annotations_required); + break; + + default: + return false; + } + + foreach ($required as $key) { + // check that it exists and is something. + if (isset($options[$key]) && $options[$key]) { + return true; + } + } + + return false; } /** @@ -2196,15 +2045,53 @@ function css_page_handler($page) { * * @link http://docs.elgg.org/Tutorials/WalledGarden * @elgg_plugin_hook index system - * @return void + * + * @param string $hook The name of the hook + * @param string $type The type of hook + * @param bool $value Has a plugin already rendered an index page? + * @param array $params Array of parameters (should be empty) + * @return bool + * @access private */ -function elgg_walled_garden_index() { - $login = elgg_view('account/login_walled_garden'); +function elgg_walled_garden_index($hook, $type, $value, $params) { + if ($value) { + // do not create a second index page so return + return; + } - echo elgg_view_page('', $login, 'walled_garden'); + elgg_load_css('elgg.walled_garden'); + elgg_load_js('elgg.walled_garden'); + + $content = elgg_view('core/walled_garden/login'); - // @hack Index must exit to keep plugins from continuing to extend - exit; + $params = array( + 'content' => $content, + 'class' => 'elgg-walledgarden-double', + 'id' => 'elgg-walledgarden-login', + ); + $body = elgg_view_layout('walled_garden', $params); + echo elgg_view_page('', $body, 'walled_garden'); + + // return true to prevent other plugins from adding a front page + return true; +} + +/** + * Serve walled garden sections + * + * @param array $page Array of URL segments + * @return string + * @access private + */ +function _elgg_walled_garden_ajax_handler($page) { + $view = $page[0]; + $params = array( + 'content' => elgg_view("core/walled_garden/$view"), + 'class' => 'elgg-walledgarden-single hidden', + 'id' => str_replace('_', '-', "elgg-walledgarden-$view"), + ); + echo elgg_view_layout('walled_garden', $params); + return true; } /** @@ -2215,14 +2102,20 @@ function elgg_walled_garden_index() { * plugin pages by {@elgg_hook public_pages walled_garden} will redirect to * a login page. * - * @since 1.8 + * @since 1.8.0 * @elgg_event_handler init system * @link http://docs.elgg.org/Tutorials/WalledGarden * @return void + * @access private */ function elgg_walled_garden() { global $CONFIG; + elgg_register_css('elgg.walled_garden', '/css/walled_garden.css'); + elgg_register_js('elgg.walled_garden', '/js/walled_garden.js'); + + elgg_register_page_handler('walled_garden', '_elgg_walled_garden_ajax_handler'); + // check for external page view if (isset($CONFIG->site) && $CONFIG->site instanceof ElggSite) { $CONFIG->site->checkWalledGarden(); @@ -2230,27 +2123,95 @@ function elgg_walled_garden() { } /** + * Remove public access for walled gardens + * + * @param string $hook + * @param string $type + * @param array $accesses + * @return array + * @access private + */ +function _elgg_walled_garden_remove_public_access($hook, $type, $accesses) { + if (isset($accesses[ACCESS_PUBLIC])) { + unset($accesses[ACCESS_PUBLIC]); + } + return $accesses; +} + +/** + * Boots the engine + * + * 1. sets error handlers + * 2. connects to database + * 3. verifies the installation suceeded + * 4. loads application configuration + * 5. loads i18n data + * 6. loads site configuration + * + * @access private + */ +function _elgg_engine_boot() { + // Register the error handlers + set_error_handler('_elgg_php_error_handler'); + set_exception_handler('_elgg_php_exception_handler'); + + setup_db_connections(); + + verify_installation(); + + _elgg_load_application_config(); + + _elgg_load_site_config(); + + _elgg_session_boot(); + + _elgg_load_cache(); + + _elgg_load_translations(); +} + +/** * Elgg's main init. * - * Handles core actions for comments and likes, the JS pagehandler, and the shutdown function. + * Handles core actions for comments, the JS pagehandler, and the shutdown function. * * @elgg_event_handler init system * @return void + * @access private */ function elgg_init() { global $CONFIG; elgg_register_action('comments/add'); elgg_register_action('comments/delete'); - elgg_register_action('likes/add'); - elgg_register_action('likes/delete'); - - register_page_handler('js', 'js_page_handler'); - register_page_handler('css', 'css_page_handler'); + elgg_register_page_handler('js', 'elgg_js_page_handler'); + elgg_register_page_handler('css', 'elgg_css_page_handler'); + elgg_register_page_handler('ajax', 'elgg_ajax_page_handler'); + + elgg_register_js('elgg.autocomplete', 'js/lib/ui.autocomplete.js'); + elgg_register_js('jquery.ui.autocomplete.html', 'vendors/jquery/jquery.ui.autocomplete.html.js'); + elgg_register_js('elgg.userpicker', 'js/lib/ui.userpicker.js'); + elgg_register_js('elgg.friendspicker', 'js/lib/ui.friends_picker.js'); + elgg_register_js('jquery.easing', 'vendors/jquery/jquery.easing.1.3.packed.js'); + elgg_register_js('elgg.avatar_cropper', 'js/lib/ui.avatar_cropper.js'); + elgg_register_js('jquery.imgareaselect', 'vendors/jquery/jquery.imgareaselect-0.9.8/scripts/jquery.imgareaselect.min.js'); + elgg_register_js('elgg.ui.river', 'js/lib/ui.river.js'); + + elgg_register_css('jquery.imgareaselect', 'vendors/jquery/jquery.imgareaselect-0.9.8/css/imgareaselect-deprecated.css'); + // Trigger the shutdown:system event upon PHP shutdown. register_shutdown_function('_elgg_shutdown_hook'); + $logo_url = elgg_get_site_url() . "_graphics/elgg_toolbar_logo.gif"; + elgg_register_menu_item('topbar', array( + 'name' => 'elgg_logo', + 'href' => 'http://www.elgg.org/', + 'text' => "<img src=\"$logo_url\" alt=\"Elgg logo\" width=\"38\" height=\"20\" />", + 'priority' => 1, + 'link_class' => 'elgg-topbar-logo', + )); + // Sets a blacklist of words in the current language. // This is a comma separated list in word:blacklist. // @todo possibly deprecate @@ -2272,7 +2233,8 @@ function elgg_init() { * @param array $params empty * * @elgg_plugin_hook unit_tests system - * @return void + * @return array + * @access private */ function elgg_api_test($hook, $type, $value, $params) { global $CONFIG; @@ -2283,7 +2245,10 @@ function elgg_api_test($hook, $type, $value, $params) { } /**#@+ - * Controlls access levels on ElggEntity entities, metadata, and annotations. + * Controls access levels on ElggEntity entities, metadata, and annotations. + * + * @warning ACCESS_DEFAULT is a place holder for the input/access view. Do not + * use it when saving an entity. * * @var int */ @@ -2317,7 +2282,7 @@ define('ELGG_ENTITIES_NO_VALUE', 0); * referring page. * * @see forward - * @var unknown_type + * @var int -1 */ define('REFERRER', -1); @@ -2332,6 +2297,7 @@ define('REFERRER', -1); define('REFERER', -1); elgg_register_event_handler('init', 'system', 'elgg_init'); +elgg_register_event_handler('boot', 'system', '_elgg_engine_boot', 1); elgg_register_plugin_hook_handler('unit_test', 'system', 'elgg_api_test'); elgg_register_event_handler('init', 'system', 'add_custom_menu_items', 1000); diff --git a/engine/lib/entities.php b/engine/lib/entities.php index 57a98ae40..4fcf1c657 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -13,15 +13,54 @@ * @global array $ENTITY_CACHE * @access private */ +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 */ -$SUBTYPE_CACHE = NULL; +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; + +/** + * 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. @@ -31,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); } /** @@ -47,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; @@ -122,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 * @@ -133,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; +} + +/** + * 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; - $SUBTYPE_CACHE[$subtype_id] = $result; - return $result->subtype; + 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; } /** - * Return a classname for a registered type and subtype. + * 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 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 @@ -212,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'"); - - if ($result) { - if (!$SUBTYPE_CACHE) { - $SUBTYPE_CACHE = array(); - } + global $SUBTYPE_CACHE; - $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 * @@ -244,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; } /** @@ -277,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 @@ -289,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; @@ -312,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 * @@ -330,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 @@ -339,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; } /** @@ -365,12 +446,13 @@ function update_subtype($type, $subtype, $class = '') { * @param int $owner_guid The new owner guid * @param int $access_id The new access id * @param int $container_guid The new container guid + * @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) { +function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $time_created = null) { global $CONFIG, $ENTITY_CACHE; $guid = (int)$guid; @@ -384,11 +466,22 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null) { $entity = get_entity($guid); + if ($time_created == null) { + $time_created = $entity->time_created; + } else { + $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" - . " set owner_guid='$owner_guid', access_id='$access_id'," - . " container_guid='$container_guid', time_updated='$time' WHERE guid=$guid"); + $ret = update_data("UPDATE {$CONFIG->dbprefix}entities + set owner_guid='$owner_guid', access_id='$access_id', + container_guid='$container_guid', time_created='$time_created', + time_updated='$time' WHERE guid=$guid"); if ($entity instanceof ElggObject) { update_river_access_by_object($guid, $access_id); @@ -400,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! @@ -414,7 +507,7 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null) { } /** - * Determine if a given user is can write to an entity container. + * Determine if a given user can write to an entity container. * * An entity can be a container for any other entity by setting the * container_guid. container_guid can differ from owner_guid. @@ -422,7 +515,7 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null) { * A plugin hook container_permissions_check:$entity_type is emitted to allow granular * access controls in plugins. * - * @param int $user_guid The user guid, or 0 for get_loggedin_userid() + * @param int $user_guid The user guid, or 0 for logged in user * @param int $container_guid The container, or 0 for the current page owner. * @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 @@ -434,7 +527,7 @@ function can_write_to_container($user_guid = 0, $container_guid = 0, $type = 'al $user_guid = (int)$user_guid; $user = get_entity($user_guid); if (!$user) { - $user = get_loggedin_user(); + $user = elgg_get_logged_in_user_entity(); } $container_guid = (int)$container_guid; @@ -442,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); @@ -451,23 +546,29 @@ 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. - // @todo this should be moved to the groups plugin - if ($user && $container instanceof ElggGroup) { - if (!$container->isMember($user)) { - $return = FALSE; - } else { - $return = TRUE; + // 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 (!$return && $user && $container instanceof ElggGroup) { + /* @var ElggGroup $container */ + if ($container->isMember($user)) { + $return = true; } } } // See if anyone else has anything to say - return elgg_trigger_plugin_hook('container_permissions_check', $type, - array('container' => $container, 'user' => $user, 'subtype' => $subtype), $return); + return elgg_trigger_plugin_hook( + 'container_permissions_check', + $type, + array( + 'container' => $container, + 'user' => $user, + 'subtype' => $subtype + ), + $return); } /** @@ -490,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) { @@ -501,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; @@ -510,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 = get_loggedin_user(); - 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; } } @@ -567,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)) { @@ -650,28 +756,87 @@ 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; } +/** + * Does an entity exist? + * + * This function checks for the existence of an entity independent of access + * permissions. It is useful for situations when a user cannot access an entity + * and it must be determined whether entity has been deleted or the access level + * has changed. + * + * @param int $guid The GUID of the entity + * + * @return bool + * @since 1.8.0 + */ +function elgg_entity_exists($guid) { + global $CONFIG; + + $guid = sanitize_int($guid); + + $query = "SELECT count(*) as total FROM {$CONFIG->dbprefix}entities WHERE guid = $guid"; + $result = get_data_row($query); + if ($result->total == 0) { + return false; + } else { + return true; + } +} /** * Returns an array of entities with optional filtering. @@ -693,19 +858,24 @@ function get_entity($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 * - * owner_guids => NULL|INT entity guid + * guids => NULL|ARR Array of entity guids + * + * owner_guids => NULL|ARR Array of owner guids * - * container_guids => NULL|INT container_guid + * container_guids => NULL|ARR Array of container_guids * - * site_guids => NULL (current_site)|INT site_guid + * site_guids => NULL (current_site)|ARR Array of site_guid * * order_by => NULL (time_created desc)|STR SQL order by clause * - * limit => NULL (10)|INT SQL limit clause + * reverse_order_by => BOOL Reverse the default order by clause + * + * limit => NULL (10)|INT SQL limit clause (0 means no limit) * * offset => NULL (0)|INT SQL offset clause * @@ -723,7 +893,9 @@ function get_entity($guid) { * * joins => array() Additional joins * - * @return mixed int if count is true, an array of entity objects, or false on failure + * callback => string A callback function to pass each row through + * + * @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() @@ -740,6 +912,7 @@ function elgg_get_entities(array $options = array()) { 'subtypes' => ELGG_ENTITIES_ANY_VALUE, 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, + 'guids' => ELGG_ENTITIES_ANY_VALUE, 'owner_guids' => ELGG_ENTITIES_ANY_VALUE, 'container_guids' => ELGG_ENTITIES_ANY_VALUE, 'site_guids' => $CONFIG->site_guid, @@ -749,6 +922,7 @@ function elgg_get_entities(array $options = array()) { 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE, 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE, + 'reverse_order_by' => false, 'order_by' => 'e.time_created desc', 'group_by' => ELGG_ENTITIES_ANY_VALUE, 'limit' => 10, @@ -756,7 +930,11 @@ function elgg_get_entities(array $options = array()) { 'count' => FALSE, 'selects' => array(), 'wheres' => array(), - 'joins' => array() + 'joins' => array(), + + 'callback' => 'entity_row_to_elggstar', + + '__ElggBatch' => null, ); $options = array_merge($defaults, $options); @@ -772,7 +950,7 @@ function elgg_get_entities(array $options = array()) { } } - $singulars = array('type', 'subtype', 'owner_guid', 'container_guid', 'site_guid'); + $singulars = array('type', 'subtype', 'guid', 'owner_guid', 'container_guid', 'site_guid'); $options = elgg_normalise_plural_options_array($options, $singulars); // evaluate where clauses @@ -784,15 +962,15 @@ function elgg_get_entities(array $options = array()) { $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'], $options['subtypes'], $options['type_subtype_pairs']); - $wheres[] = elgg_get_entity_site_where_sql('e', $options['site_guids']); - $wheres[] = elgg_get_entity_owner_where_sql('e', $options['owner_guids']); - $wheres[] = elgg_get_entity_container_where_sql('e', $options['container_guids']); + + $wheres[] = elgg_get_guid_based_where_sql('e.guid', $options['guids']); + $wheres[] = elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']); + $wheres[] = elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']); + $wheres[] = elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']); + $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) { @@ -803,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']); @@ -823,7 +1004,7 @@ function elgg_get_entities(array $options = array()) { if ($options['selects']) { $selects = ''; foreach ($options['selects'] as $select) { - $selects = ", $select"; + $selects .= ", $select"; } } else { $selects = ''; @@ -849,24 +1030,53 @@ function elgg_get_entities(array $options = array()) { // Add access controls $query .= get_access_sql_suffix('e'); + + // reverse order by + if ($options['reverse_order_by']) { + $options['order_by'] = elgg_sql_reverse_order_by_clause($options['order_by']); + } + 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']); - $offset = sanitise_int($options['offset']); + $limit = sanitise_int($options['limit'], false); + $offset = sanitise_int($options['offset'], false); $query .= " LIMIT $offset, $limit"; } - $dt = get_data($query, "entity_row_to_elggstar"); + if ($options['callback'] === 'entity_row_to_elggstar') { + $dt = _elgg_fetch_entities_from_sql($query, $options['__ElggBatch']); + } else { + $dt = get_data($query, $options['callback']); + } - //@todo normalize this to array() + 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); @@ -875,96 +1085,101 @@ function elgg_get_entities(array $options = array()) { } /** - * Returns entities. - * - * @deprecated 1.7. Use elgg_get_entities(). - * - * @param string $type Entity type - * @param string $subtype Entity subtype - * @param int $owner_guid Owner GUID - * @param string $order_by Order by clause - * @param int $limit Limit - * @param int $offset Offset - * @param bool $count Return a count or an array of entities - * @param int $site_guid Site GUID - * @param int $container_guid Container GUID - * @param int $timelower Lower time limit - * @param int $timeupper Upper time limit + * Return entities from an SQL query generated by elgg_get_entities. * - * @return array + * @param string $sql + * @param ElggBatch $batch + * @return ElggEntity[] + * + * @access private + * @throws LogicException */ -function get_entities($type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, -$offset = 0, $count = false, $site_guid = 0, $container_guid = null, $timelower = 0, -$timeupper = 0) { +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', + ); - elgg_deprecated_notice('get_entities() was deprecated by elgg_get_entities().', 1.7); + $rows = get_data($sql); - // rewrite owner_guid to container_guid to emulate old functionality - if ($owner_guid != "") { - if (is_null($container_guid)) { - $container_guid = $owner_guid; - $owner_guid = NULL; - } + // 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(); } - $options = array(); - if ($type) { - if (is_array($type)) { - $options['types'] = $type; - } else { - $options['type'] = $type; + // 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; } } - - if ($subtype) { - if (is_array($subtype)) { - $options['subtypes'] = $subtype; - } else { - $options['subtype'] = $subtype; + // 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); + } + } } } - - if ($owner_guid) { - if (is_array($owner_guid)) { - $options['owner_guids'] = $owner_guid; + // Second pass to finish conversion + foreach ($rows as $i => $row) { + if ($row instanceof ElggEntity) { + continue; } else { - $options['owner_guid'] = $owner_guid; + 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); + } + } } } - - if ($order_by) { - $options['order_by'] = $order_by; - } - - // need to pass 0 for all option - $options['limit'] = $limit; - - if ($offset) { - $options['offset'] = $offset; - } - - if ($count) { - $options['count'] = $count; - } - - if ($site_guid) { - $options['site_guids'] = $site_guid; - } - - if ($container_guid) { - $options['container_guids'] = $container_guid; - } - - if ($timeupper) { - $options['created_time_upper'] = $timeupper; - } - - if ($timelower) { - $options['created_time_lower'] = $timelower; - } - - $r = elgg_get_entities($options); - return $r; + return $rows; } /** @@ -991,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(); @@ -1018,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); @@ -1036,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!", 'WARNING'); + // 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; + } } } @@ -1070,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); @@ -1095,7 +1321,7 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair ELGG_ENTITIES_NO_VALUE : $paired_subtype_id; } else { $valid_pairs_subtypes_count--; - elgg_log("Type-subtype $paired_type:$paired_subtype' does not exist!", 'WARNING'); + elgg_log("Type-subtype '$paired_type:$paired_subtype' does not exist!", 'NOTICE'); // return false if we're all invalid subtypes in the only valid type continue; } @@ -1129,83 +1355,44 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair /** * Returns SQL where clause for owner and containers. * - * @todo Probably DRY up once things are settled. - * - * @param string $table Entity table prefix as defined in SELECT...FROM entities $table - * @param NULL|array $owner_guids Owner GUIDs + * @param string $column Column name the guids should be checked against. Usually + * best to provide in table.column format. + * @param NULL|array $guids Array of GUIDs. * - * @return FALSE|str - * @since 1.7.0 + * @return false|string + * @since 1.8.0 * @access private */ -function elgg_get_entity_owner_where_sql($table, $owner_guids) { +function elgg_get_guid_based_where_sql($column, $guids) { // short circuit if nothing requested - // 0 is a valid owner_guid. - if (!$owner_guids && $owner_guids !== 0) { + // 0 is a valid guid + if (!$guids && $guids !== 0) { return ''; } // normalize and sanitise owners - if (!is_array($owner_guids)) { - $owner_guids = array($owner_guids); - } - - $owner_guids_sanitised = array(); - foreach ($owner_guids as $owner_guid) { - if (($owner_guid != sanitise_int($owner_guid))) { - return FALSE; - } - $owner_guids_sanitised[] = $owner_guid; - } - - $where = ''; - - // implode(',', 0) returns 0. - if (($owner_str = implode(',', $owner_guids_sanitised)) - && ($owner_str !== FALSE) && ($owner_str !== '')) { - - $where = "({$table}.owner_guid IN ($owner_str))"; - } - - return $where; -} - -/** - * Returns SQL where clause for containers. - * - * @param string $table Entity table prefix as defined in - * SELECT...FROM entities $table - * @param NULL|array $container_guids Array of container guids - * - * @return FALSE|string - * @since 1.7.0 - * @access private - */ -function elgg_get_entity_container_where_sql($table, $container_guids) { - // short circuit if nothing is requested. - // 0 is a valid container_guid. - if (!$container_guids && $container_guids !== 0) { - return ''; + if (!is_array($guids)) { + $guids = array($guids); } - // normalize and sanitise containers - if (!is_array($container_guids)) { - $container_guids = array($container_guids); - } + $guids_sanitized = array(); + foreach ($guids as $guid) { + if ($guid !== ELGG_ENTITIES_NO_VALUE) { + $guid = sanitise_int($guid); - $container_guids_sanitised = array(); - foreach ($container_guids as $container_guid) { - if (($container_guid != sanitise_int($container_guid))) { - return FALSE; + if (!$guid) { + return false; + } } - $container_guids_sanitised[] = $container_guid; + $guids_sanitized[] = $guid; } $where = ''; + $guid_str = implode(',', $guids_sanitized); // implode(',', 0) returns 0. - if (FALSE !== $container_str = implode(',', $container_guids_sanitised)) { - $where = "({$table}.container_guid IN ($container_str))"; + if ($guid_str !== FALSE && $guid_str !== '') { + $where = "($column IN ($guid_str))"; } return $where; @@ -1216,12 +1403,12 @@ function elgg_get_entity_container_where_sql($table, $container_guids) { * * @param string $table Entity table prefix as defined in * SELECT...FROM entities $table - * @param NULL|int $time_created_upper Time crated upper limit + * @param NULL|int $time_created_upper Time created upper limit * @param NULL|int $time_created_lower Time created lower limit * @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 */ @@ -1256,41 +1443,6 @@ $time_created_lower = NULL, $time_updated_upper = NULL, $time_updated_lower = NU } /** - * Returns SQL where clause for site entities - * - * @param string $table Entity table prefix as defined in SELECT...FROM entities $table - * @param NULL|array $site_guids Array of site guids - * - * @return FALSE|string - * @since 1.7.0 - * @access private - */ -function elgg_get_entity_site_where_sql($table, $site_guids) { - // short circuit if nothing requested - if (!$site_guids) { - return ''; - } - - if (!is_array($site_guids)) { - $site_guids = array($site_guids); - } - - $site_guids_sanitised = array(); - foreach ($site_guids as $site_guid) { - if (!$site_guid || ($site_guid != sanitise_int($site_guid))) { - return FALSE; - } - $site_guids_sanitised[] = $site_guid; - } - - if ($site_guids_str = implode(',', $site_guids_sanitised)) { - return "({$table}.site_guid IN ($site_guids_str))"; - } - - return ''; -} - -/** * Returns a string of parsed entities. * * Displays list of entities with formatting specified @@ -1298,12 +1450,16 @@ function elgg_get_entity_site_where_sql($table, $site_guids) { * * @tip Pagination is handled automatically. * + * @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 + * 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. * * @return string * @since 1.7 @@ -1311,9 +1467,16 @@ function elgg_get_entity_site_where_sql($table, $site_guids) { * @see elgg_view_entity_list() * @link http://docs.elgg.org/Entities/Output */ -function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entities') { +function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entities', + $viewer = 'elgg_view_entity_list') { + + 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, @@ -1326,65 +1489,16 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti if (isset($options['view_type_toggle'])) { $options['list_type_toggle'] = $options['view_type_toggle']; } - + $options['count'] = TRUE; $count = $getter($options); $options['count'] = FALSE; $entities = $getter($options); - return elgg_view_entity_list($entities, $count, $options['offset'], $options['limit'], - $options['full_view'], $options['list_type_toggle'], $options['pagination']); -} + $options['count'] = $count; -/** - * Lists entities - * - * @deprecated 1.7. Use elgg_list_entities(). - * - * @param string $type Entity type - * @param string $subtype Entity subtype - * @param int $owner_guid Owner GUID - * @param int $limit Limit - * @param bool $fullview Display entity full views? - * @param bool $listtypetoggle Allow switching to gallery mode? - * @param bool $pagination Show pagination? - * - * @return string - */ -function list_entities($type= "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, -$listtypetoggle = false, $pagination = true) { - - elgg_deprecated_notice('list_entities() was deprecated by elgg_list_entities()!', 1.7); - - $options = array(); - - // rewrite owner_guid to container_guid to emulate old functionality - if ($owner_guid) { - $options['container_guids'] = $owner_guid; - } - - if ($type) { - $options['types'] = $type; - } - - if ($subtype) { - $options['subtypes'] = $subtype; - } - - if ($limit) { - $options['limit'] = $limit; - } - - if ($offset = sanitise_int(get_input('offset', null))) { - $options['offset'] = $offset; - } - - $options['full_view'] = $fullview; - $options['list_type_toggle'] = $listtypetoggle; - $options['pagination'] = $pagination; - - return elgg_list_entities($options); + return $viewer($entities, $options); } /** @@ -1392,13 +1506,15 @@ $listtypetoggle = false, $pagination = true) { * * @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 */ @@ -1505,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; @@ -1523,29 +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(get_loggedin_userid()); - - $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(); + _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; } @@ -1557,40 +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}"); - $entity->clearMetaData('disable_reason'); + 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; } /** @@ -1611,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; @@ -1624,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) @@ -1633,28 +1778,53 @@ function delete_entity($guid, $recursive = true) { // @todo Do this better. static $__RECURSIVE_DELETE_TOKEN; // Make it slightly harder to guess - $__RECURSIVE_DELETE_TOKEN = md5(get_loggedin_userid()); - - $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(); - } + $__RECURSIVE_DELETE_TOKEN = md5(elgg_get_logged_in_user_guid()); + + $entity_disable_override = access_get_show_hidden_status(); + access_show_hidden_entities(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->clearMetadata(); - $entity->clearAnnotations(); - $entity->clearRelationships(); - remove_from_river_by_subject($guid); - remove_from_river_by_object($guid); + $entity->deleteMetadata(); + $entity->deleteOwnedMetadata(); + $entity->deleteAnnotations(); + $entity->deleteOwnedAnnotations(); + $entity->deleteRelationships(); + + 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}"); if ($res) { $sub_table = ""; @@ -1680,7 +1850,7 @@ function delete_entity($guid, $recursive = true) { } } - return $res; + return (bool)$res; } } } @@ -1689,25 +1859,6 @@ function delete_entity($guid, $recursive = true) { } /** - * Delete multiple entities that match a given query. - * This function iterates through and calls delete_entity on - * each one, this is somewhat inefficient but lets - * the 'delete' event be called for each entity. - * - * @deprecated 1.7. This is a dangerous function as it defaults to deleting everything. - * - * @param string $type The type of entity (eg "user", "object" etc) - * @param string $subtype The arbitrary subtype of the entity - * @param int $owner_guid The GUID of the owning user - * - * @return false - */ -function delete_entities($type = "", $subtype = "", $owner_guid = 0) { - elgg_deprecated_notice('delete_entities() was deprecated because no one should use it.', 1.7); - return false; -} - -/** * Exports attributes generated on the fly (volatile) about an entity. * * @param string $hook volatile @@ -1715,7 +1866,7 @@ function delete_entities($type = "", $subtype = "", $owner_guid = 0) { * @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 @@ -1759,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 @@ -1800,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'); @@ -1811,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(); @@ -1870,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); @@ -1883,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); } @@ -1917,16 +2076,14 @@ 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) { - $user = get_loggedin_user(); + $user = elgg_get_logged_in_user_entity(); } + $return = false; if ($entity = get_entity($entity_guid)) { - $return = false; // Test user if possible - should default to false unless a plugin hook says otherwise if ($user) { @@ -1945,13 +2102,10 @@ function can_edit_entity($entity_guid, $user_guid = 0) { } } } + } - return elgg_trigger_plugin_hook('permissions_check', $entity->type, + return elgg_trigger_plugin_hook('permissions_check', $entity->type, array('entity' => $entity, 'user' => $user), $return); - - } else { - return false; - } } /** @@ -1974,16 +2128,21 @@ 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, $parms, $return); + $return = elgg_trigger_plugin_hook('permissions_check:metadata', $entity->type, $params, $return); return $return; } else { return false; @@ -1991,79 +2150,6 @@ function can_edit_entity_metadata($entity_guid, $user_guid = 0, $metadata = null } /** - * Return the icon URL for an entity. - * - * @tip Can be overridden by registering a plugin hook for entity:icon:url, $entity_type. - * - * @internal This is passed an entity rather than a guid to handle non-created entities. - * - * @param ElggEntity $entity The entity - * @param string $size Icon size - * - * @return string URL to the entity icon. - */ -function get_entity_icon_url(ElggEntity $entity, $size = 'medium') { - global $CONFIG; - - $size = sanitise_string($size); - switch (strtolower($size)) { - case 'master': - $size = 'master'; - break; - - case 'large' : - $size = 'large'; - break; - - case 'topbar' : - $size = 'topbar'; - break; - - case 'tiny' : - $size = 'tiny'; - break; - - case 'small' : - $size = 'small'; - break; - - case 'medium' : - default: - $size = 'medium'; - } - - $url = false; - - $viewtype = elgg_get_viewtype(); - - // Step one, see if anyone knows how to render this in the current view - $params = array('entity' => $entity, 'viewtype' => $viewtype, 'size' => $size); - $url = elgg_trigger_plugin_hook('entity:icon:url', $entity->getType(), $params, $url); - - // Fail, so use default - if (!$url) { - $type = $entity->getType(); - $subtype = $entity->getSubtype(); - - if (!empty($subtype)) { - $overrideurl = elgg_view("icon/{$type}/{$subtype}/{$size}", array('entity' => $entity)); - if (!empty($overrideurl)) { - return $overrideurl; - } - } - - $overrideurl = elgg_view("icon/{$type}/default/{$size}", array('entity' => $entity)); - if (!empty($overrideurl)) { - return $overrideurl; - } - - $url = "_graphics/icons/default/$size.png"; - } - - return elgg_normalize_url($url); -} - -/** * Returns the URL for an entity. * * @tip Can be overridden with {@link register_entity_url_handler()}. @@ -2082,26 +2168,25 @@ function get_entity_url($entity_guid) { if (isset($CONFIG->entity_url_handler[$entity->getType()][$entity->getSubType()])) { $function = $CONFIG->entity_url_handler[$entity->getType()][$entity->getSubType()]; if (is_callable($function)) { - $url = $function($entity); + $url = call_user_func($function, $entity); } } elseif (isset($CONFIG->entity_url_handler[$entity->getType()]['all'])) { $function = $CONFIG->entity_url_handler[$entity->getType()]['all']; if (is_callable($function)) { - $url = $function($entity); + $url = call_user_func($function, $entity); } } elseif (isset($CONFIG->entity_url_handler['all']['all'])) { $function = $CONFIG->entity_url_handler['all']['all']; if (is_callable($function)) { - $url = $function($entity); + $url = call_user_func($function, $entity); } } if ($url == "") { - $url = "pg/view/" . $entity_guid; + $url = "view/" . $entity_guid; } return elgg_normalize_url($url); - } return false; @@ -2110,20 +2195,19 @@ function get_entity_url($entity_guid) { /** * Sets the URL handler for a particular entity type and subtype * - * @param string $function_name The function to register * @param string $entity_type The entity type * @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 */ -function register_entity_url_handler($function_name, $entity_type = "all", -$entity_subtype = "all") { - +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; } @@ -2141,46 +2225,6 @@ $entity_subtype = "all") { } /** - * Default Icon handler for entities. - * - * @tip This will attempt to find a default entity for the current view and return a url. - * This is registered at a high priority so that other handlers will pick it up first. - * - * @param string $hook entity:icon:url - * @param string $entity_type all - * @param mixed $returnvalue Previous hook's return value - * @param array $params Array of params - * - * @return string|null String of URL for entity's icon - * @elgg_plugin_hook_handler entity:icon:url all - */ -function default_entity_icon_hook($hook, $entity_type, $returnvalue, $params) { - global $CONFIG; - - if ((!$returnvalue) && ($hook == 'entity:icon:url')) { - $entity = $params['entity']; - $type = $entity->type; - $subtype = get_subtype_from_id($entity->subtype); - $viewtype = $params['viewtype']; - $size = $params['size']; - - $url = "views/$viewtype/graphics/icons/$type/$subtype/$size.png"; - - if (!@file_exists($CONFIG->path . $url)) { - $url = "views/$viewtype/graphics/icons/$type/default/$size.png"; - } - - if (!@file_exists($CONFIG->path . $url)) { - $url = "views/$viewtype/graphics/icons/default/$size.png"; - } - - if (@file_exists($CONFIG->path . $url)) { - return elgg_get_site_url().$url; - } - } -} - -/** * Registers an entity type and subtype as a public-facing entity that should * be shown in search and by {@link elgg_list_registered_entities()}. * @@ -2191,16 +2235,16 @@ function default_entity_icon_hook($hook, $entity_type, $returnvalue, $params) { * @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 */ -function register_entity_type($type, $subtype) { +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; } @@ -2228,14 +2272,14 @@ function register_entity_type($type, $subtype) { * @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 - * @see register_entity_type() + * @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; } @@ -2267,15 +2311,15 @@ function unregister_entity_type($type, $subtype) { * @param string $type The type of entity (object, site, user, group) or blank for all * * @return array|false Depending on whether entities have been registered - * @see register_entity_type() + * @see elgg_register_entity_type() */ -function get_registered_entity_types($type = '') { +function get_registered_entity_types($type = null) { global $CONFIG; if (!isset($CONFIG->registered_entities)) { return false; } - if (!empty($type)) { + if ($type) { $type = strtolower($type); } if (!empty($type) && empty($CONFIG->registered_entities[$type])) { @@ -2290,26 +2334,32 @@ function get_registered_entity_types($type = '') { } /** - * Returns if the entity type and subtype have been registered with {@see register_entity_type()}. + * Returns if the entity type and subtype have been registered with {@see elgg_register_entity_type()}. * * @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) { +function is_registered_entity_type($type, $subtype = null) { global $CONFIG; if (!isset($CONFIG->registered_entities)) { return false; } + $type = strtolower($type); - if (empty($CONFIG->registered_entities[$type])) { + + // @todo registering a subtype implicitly registers the type. + // see #2684 + if (!isset($CONFIG->registered_entities[$type])) { return false; } - if (in_array($subtype, $CONFIG->registered_entities[$type])) { - return true; + + if ($subtype && !in_array($subtype, $CONFIG->registered_entities[$type])) { + return false; } + return true; } /** @@ -2317,56 +2367,18 @@ function is_registered_entity_type($type, $subtype) { * * @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; } -} - -/** - * Lists entities. - * - * @param int $owner_guid Owner GUID - * @param int $limit Limit - * @param bool $fullview Show entity full views - * @param bool $listtypetoggle Show list type toggle - * @param bool $allowedtypes A string of the allowed types - * - * @return string - * @deprecated 1.7. Use elgg_list_registered_entities(). - */ -function list_registered_entities($owner_guid = 0, $limit = 10, $fullview = true, -$listtypetoggle = false, $allowedtypes = true) { - - elgg_deprecated_notice('list_registered_entities() was deprecated by elgg_list_registered_entities().', 1.7); - - $options = array(); - - // don't want to send anything if not being used. - if ($owner_guid) { - $options['owner_guid'] = $owner_guid; - } - - if ($limit) { - $options['limit'] = $limit; - } - - if ($allowedtypes) { - $options['allowed_types'] = $allowedtypes; - } - - // need to send because might be BOOL - $options['full_view'] = $fullview; - $options['list_type_toggle'] = $listtypetoggle; - - $options['offset'] = get_input('offset', 0); - - return elgg_list_registered_entities($options); + return false; } /** @@ -2387,67 +2399,56 @@ $listtypetoggle = false, $allowedtypes = true) { * @return string A viewable list of entities * @since 1.7.0 */ -function elgg_list_registered_entities($options) { +function elgg_list_registered_entities(array $options = array()) { + global $autofeed; + $autofeed = true; + $defaults = array( 'full_view' => TRUE, 'allowed_types' => TRUE, 'list_type_toggle' => FALSE, 'pagination' => TRUE, - 'offset' => 0 + 'offset' => 0, + 'types' => array(), + 'type_subtype_pairs' => array() ); $options = array_merge($defaults, $options); + //backwards compatibility if (isset($options['view_type_toggle'])) { $options['list_type_toggle'] = $options['view_type_toggle']; } - - $typearray = array(); - if ($object_types = get_registered_entity_types()) { - foreach ($object_types as $object_type => $subtype_array) { - if (in_array($object_type, $options['allowed_types']) || $options['allowed_types'] === TRUE) { - $typearray[$object_type] = array(); + $types = get_registered_entity_types(); + foreach ($types as $type => $subtype_array) { + if (in_array($type, $options['allowed_types']) || $options['allowed_types'] === TRUE) { + // you must explicitly register types to show up in here and in search for objects + if ($type == 'object') { if (is_array($subtype_array) && count($subtype_array)) { - foreach ($subtype_array as $subtype) { - $typearray[$object_type][] = $subtype; - } + $options['type_subtype_pairs'][$type] = $subtype_array; + } + } else { + if (is_array($subtype_array) && count($subtype_array)) { + $options['type_subtype_pairs'][$type] = $subtype_array; + } else { + $options['type_subtype_pairs'][$type] = ELGG_ENTITIES_ANY_VALUE; } } } } - $options['type_subtype_pairs'] = $typearray; - - $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 ((isloggedin()) && ($__RECURSIVE_DELETE_TOKEN) - && (strcmp($__RECURSIVE_DELETE_TOKEN, md5(get_loggedin_userid())))) { - 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); } /** @@ -2461,13 +2462,14 @@ function recursive_delete_permissions_check() { * @param string $subtype Entity subtype * @param string $class Class name * - * @return Bool - * @since 1.8 + * @return bool + * @since 1.8.0 */ function elgg_instanceof($entity, $type = NULL, $subtype = NULL, $class = NULL) { $return = ($entity instanceof ElggEntity); if ($type) { + /* @var ElggEntity $entity */ $return = $return && ($entity->getType() == $type); } @@ -2482,12 +2484,9 @@ function elgg_instanceof($entity, $type = NULL, $subtype = NULL, $class = NULL) return $return; } - /** * 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. * @@ -2495,10 +2494,12 @@ 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; + $posted = (int)$posted; if (!$posted) { $posted = time(); @@ -2523,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; @@ -2556,17 +2566,13 @@ function entities_test($hook, $type, $value, $params) { * * @return void * @elgg_event_handler init system + * @access private */ function entities_init() { - register_page_handler('view', 'entities_page_handler'); + 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'); } @@ -2579,8 +2585,6 @@ elgg_register_plugin_hook_handler("export", "all", "export_entity_plugin_hook", /** Hook to get certain named bits of volatile data about an entity */ elgg_register_plugin_hook_handler('volatile', 'metadata', 'volatile_data_export_plugin_hook'); -/** Hook for rendering a default icon for entities */ -elgg_register_plugin_hook_handler('entity:icon:url', 'all', 'default_entity_icon_hook', 1000); - /** Register init system event **/ -elgg_register_event_handler('init', 'system', 'entities_init');
\ No newline at end of file +elgg_register_event_handler('init', 'system', 'entities_init'); + diff --git a/engine/lib/export.php b/engine/lib/export.php index f81bee2fe..ecc894e63 100644 --- a/engine/lib/export.php +++ b/engine/lib/export.php @@ -11,7 +11,7 @@ * * @param mixed $object The object either an ElggEntity, ElggRelationship or ElggExtender * - * @return the UUID or false + * @return string|false the UUID or false */ function get_uuid_from_object($object) { if ($object instanceof ElggEntity) { @@ -40,8 +40,6 @@ function get_uuid_from_object($object) { * @return string */ function guid_to_uuid($guid) { - global $CONFIG; - return elgg_get_site_url() . "export/opendd/$guid/"; } @@ -53,8 +51,6 @@ function guid_to_uuid($guid) { * @return bool */ function is_uuid_this_domain($uuid) { - global $CONFIG; - if (strpos($uuid, elgg_get_site_url()) === 0) { return true; } @@ -67,7 +63,7 @@ function is_uuid_this_domain($uuid) { * * @param string $uuid A unique ID * - * @return mixed + * @return ElggEntity|false */ function get_entity_from_uuid($uuid) { $uuid = sanitise_string($uuid); @@ -111,23 +107,25 @@ $IMPORTED_OBJECT_COUNTER = 0; * @param ODD $odd The odd element to process * * @return bool + * @access private */ function _process_element(ODD $odd) { global $IMPORTED_DATA, $IMPORTED_OBJECT_COUNTER; // See if anyone handles this element, return true if it is. + $to_be_serialised = null; if ($odd) { $handled = elgg_trigger_plugin_hook("import", "all", array("element" => $odd), $to_be_serialised); - } - // If not, then see if any of its sub elements are handled - if ($handled) { - // Increment validation counter - $IMPORTED_OBJECT_COUNTER ++; - // Return the constructed object - $IMPORTED_DATA[] = $handled; + // If not, then see if any of its sub elements are handled + if ($handled) { + // Increment validation counter + $IMPORTED_OBJECT_COUNTER ++; + // Return the constructed object + $IMPORTED_DATA[] = $handled; - return true; + return true; + } } return false; @@ -140,6 +138,7 @@ function _process_element(ODD $odd) { * * @return array * @throws ExportException + * @access private */ function exportAsArray($guid) { $guid = (int)$guid; @@ -165,8 +164,9 @@ function exportAsArray($guid) { * * @param int $guid The GUID. * - * @return xml + * @return string XML * @see ElggEntity for an example of its usage. + * @access private */ function export($guid) { $odd = new ODDDocument(exportAsArray($guid)); @@ -181,7 +181,8 @@ function export($guid) { * @param string $xml XML string * * @return bool - * @throws Exception if there was a problem importing the data. + * @throws ImportException if there was a problem importing the data. + * @access private */ function import($xml) { global $IMPORTED_DATA, $IMPORTED_OBJECT_COUNTER; @@ -210,6 +211,7 @@ function import($xml) { * Register the OpenDD import action * * @return void + * @access private */ function export_init() { global $CONFIG; diff --git a/engine/lib/extender.php b/engine/lib/extender.php index 8df207767..8323bd3ce 100644 --- a/engine/lib/extender.php +++ b/engine/lib/extender.php @@ -19,7 +19,7 @@ * @return string */ function detect_extender_valuetype($value, $value_type = "") { - if ($value_type != "") { + if ($value_type != "" && ($value_type == 'integer' || $value_type == 'text')) { return $value_type; } @@ -44,6 +44,7 @@ function detect_extender_valuetype($value, $value_type = "") { * @param ODDMetaData $element The OpenDD element * * @return bool + * @access private */ function oddmetadata_to_elggextender(ElggEntity $entity, ODDMetaData $element) { // Get the type of extender (metadata, type, attribute etc) @@ -85,6 +86,7 @@ function oddmetadata_to_elggextender(ElggEntity $entity, ODDMetaData $element) { * @return null * @elgg_plugin_hook_handler volatile metadata * @todo investigate more. + * @throws ImportException * @access private */ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params) { @@ -93,6 +95,7 @@ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params) $tmp = NULL; if ($element instanceof ODDMetaData) { + /* @var ODDMetaData $element */ // Recall entity $entity_uuid = $element->getAttribute('entity_uuid'); $entity = get_entity_from_uuid($entity_uuid); @@ -104,6 +107,7 @@ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params) // Save if (!$entity->save()) { + $attr_name = $element->getAttribute('name'); $msg = elgg_echo('ImportException:ProblemUpdatingMeta', array($attr_name, $entity_uuid)); throw new ImportException($msg); } @@ -119,62 +123,68 @@ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params) * @param string $type 'metadata' or 'annotation' * @param int $user_guid The GUID of the user * - * @return true|false + * @return bool */ function can_edit_extender($extender_id, $type, $user_guid = 0) { - if (!isloggedin()) { - return false; + // @todo Since Elgg 1.0, Elgg has returned false from can_edit_extender() + // if no user was logged in. This breaks the access override. This is a + // temporary work around. This function needs to be rewritten in Elgg 1.9 + if (!elgg_check_access_overrides($user_guid)) { + if (!elgg_is_logged_in()) { + return false; + } } $user_guid = (int)$user_guid; - $user = get_entity($user_guid); + $user = get_user($user_guid); if (!$user) { - $user = get_loggedin_user(); + $user = elgg_get_logged_in_user_entity(); + $user_guid = elgg_get_logged_in_user_guid(); } - $functionname = "get_{$type}"; + $functionname = "elgg_get_{$type}_from_id"; if (is_callable($functionname)) { - $extender = $functionname($extender_id); + $extender = call_user_func($functionname, $extender_id); } else { return false; } - if (!is_a($extender, "ElggExtender")) { + if (!($extender instanceof ElggExtender)) { return false; } + /* @var ElggExtender $extender */ // If the owner is the specified user, great! They can edit. - if ($extender->getOwnerGUID() == $user->getGUID()) { + if ($extender->getOwnerGUID() == $user_guid) { return true; } // If the user can edit the entity this is attached to, great! They can edit. - if (can_edit_entity($extender->entity_guid, $user->getGUID())) { + if (can_edit_entity($extender->entity_guid, $user_guid)) { return true; } - // Trigger plugin hooks - $params = array('entity' => $entity, 'user' => $user); + // Trigger plugin hook - note that $user may be null + $params = array('entity' => $extender->getEntity(), 'user' => $user); return elgg_trigger_plugin_hook('permissions_check', $type, $params, false); } /** * Sets the URL handler for a particular extender type and name. * It is recommended that you do not call this directly, instead use - * one of the wrapper functions in the subtype files. + * one of the wrapper functions such as elgg_register_annotation_url_handler(). * - * @param string $function_name The function to register - * @param string $extender_type Extender type + * @param string $extender_type Extender type ('annotation', 'metadata') * @param string $extender_name The name of the extender + * @param string $function_name The function to register * - * @return true|false Depending on success + * @return bool */ -function register_extender_url_handler($function_name, $extender_type = "all", -$extender_name = "all") { +function elgg_register_extender_url_handler($extender_type, $extender_name, $function_name) { global $CONFIG; - if (!is_callable($function_name)) { + if (!is_callable($function_name, true)) { return false; } @@ -221,13 +231,13 @@ function get_extender_url(ElggExtender $extender) { } if (is_callable($function)) { - $url = $function($extender); + $url = call_user_func($function, $extender); } if ($url == "") { $nameid = $extender->id; if ($type == 'volatile') { - $nameid == $extender->name; + $nameid = $extender->name; } $url = "export/$view/$guid/$type/$nameid/"; } @@ -236,4 +246,4 @@ function get_extender_url(ElggExtender $extender) { } /** Register the hook */ -elgg_register_plugin_hook_handler("import", "all", "import_extender_plugin_hook", 2);
\ No newline at end of file +elgg_register_plugin_hook_handler("import", "all", "import_extender_plugin_hook", 2); diff --git a/engine/lib/filestore.php b/engine/lib/filestore.php index 9bdc26845..a3c7ba439 100644 --- a/engine/lib/filestore.php +++ b/engine/lib/filestore.php @@ -18,7 +18,7 @@ */ function get_dir_size($dir, $totalsize = 0) { $handle = @opendir($dir); - while ($file = @readdir ($handle)) { + while ($file = @readdir($handle)) { if (eregi("^\.{1,2}$", $file)) { continue; } @@ -149,6 +149,12 @@ $x1 = 0, $y1 = 0, $x2 = 0, $y2 = 0, $upscale = FALSE) { return FALSE; } + // color transparencies white (default is black) + imagefilledrectangle( + $new_image, 0, 0, $params['newwidth'], $params['newheight'], + imagecolorallocate($new_image, 255, 255, 255) + ); + $rtn_code = imagecopyresampled( $new_image, $original_image, 0, @@ -302,8 +308,6 @@ function get_image_resize_parameters($width, $height, $options) { function file_delete($guid) { if ($file = get_entity($guid)) { if ($file->canEdit()) { - $container = get_entity($file->container_guid); - $thumbnail = $file->thumbnail; $smallthumb = $file->smallthumb; $largethumb = $file->largethumb; @@ -376,8 +380,8 @@ function file_get_general_file_type($mimetype) { /** * Delete a directory and all its contents - *
- * @param str $directory Directory to delete
+ * + * @param string $directory Directory to delete * * @return bool */ @@ -465,6 +469,7 @@ function set_default_filestore(ElggFilestore $filestore) { * ElggFile. * * @return void + * @access private */ function filestore_run_once() { // Register a class @@ -473,16 +478,19 @@ function filestore_run_once() { /** * Initialise the file modules. - * Listens to system boot and registers any appropriate file types and classes + * Listens to system init and configures the default filestore * * @return void + * @access private */ function filestore_init() { global $CONFIG; // Now register a default filestore - set_default_filestore(new ElggDiskFilestore($CONFIG->dataroot)); - + if (isset($CONFIG->dataroot)) { + set_default_filestore(new ElggDiskFilestore($CONFIG->dataroot)); + } + // Now run this stuff, but only once run_function_once("filestore_run_once"); } @@ -490,12 +498,13 @@ function filestore_init() { /** * Unit tests for files * - * @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 filestore_test($hook, $type, $value, $params) { global $CONFIG; @@ -508,4 +517,4 @@ function filestore_test($hook, $type, $value, $params) { elgg_register_event_handler('init', 'system', 'filestore_init', 100); // Unit testing -elgg_register_plugin_hook_handler('unit_test', 'system', 'filestore_test');
\ No newline at end of file +elgg_register_plugin_hook_handler('unit_test', 'system', 'filestore_test'); diff --git a/engine/lib/group.php b/engine/lib/group.php index c6bd27f2d..6ded8a825 100644 --- a/engine/lib/group.php +++ b/engine/lib/group.php @@ -14,6 +14,7 @@ * @param int $guid GUID for a group * * @return array|false + * @access private */ function get_group_entity_as_row($guid) { global $CONFIG; @@ -24,7 +25,7 @@ function get_group_entity_as_row($guid) { } /** - * Create or update the extras table for a given group. + * Create or update the entities table for a given group. * Call create_entity first. * * @param int $guid GUID @@ -32,6 +33,7 @@ function get_group_entity_as_row($guid) { * @param string $description Description * * @return bool + * @access private */ function create_group_entity($guid, $name, $description) { global $CONFIG; @@ -79,23 +81,6 @@ function create_group_entity($guid, $name, $description) { } /** - * THIS FUNCTION IS DEPRECATED. - * - * Delete a group's extra data. - * - * @param int $guid The guid of the group - * - * @return bool - * @deprecated 1.6 - */ -function delete_group_entity($guid) { - elgg_deprecated_notice("delete_group_entity has been deprecated", 1.6); - - // Always return that we have deleted one row in order to not break existing code. - return 1; -} - -/** * Add an object to the given group. * * @param int $group_guid The group to add the object to. @@ -164,352 +149,6 @@ function remove_object_from_group($group_guid, $object_guid) { } /** - * Return an array of objects in a given container. - * - * @see get_entities() - * - * @param int $group_guid The container (defaults to current page owner) - * @param string $subtype The subtype - * @param int $owner_guid Owner - * @param int $site_guid The site - * @param string $order_by Order - * @param int $limit Limit on number of elements to return, by default 10. - * @param int $offset Where to start, by default 0. - * @param bool $count Whether to return the entities or a count of them. - * - * @return array|false - * @deprecated 1.8 Use elgg_get_entities() instead - */ -function get_objects_in_group($group_guid, $subtype = "", $owner_guid = 0, $site_guid = 0, -$order_by = "", $limit = 10, $offset = 0, $count = FALSE) { - elgg_deprecated_notice("get_objects_in_group was deprected in 1.8. Use elgg_get_entities() instead", 1.8); - - global $CONFIG; - - if ($subtype === FALSE || $subtype === null || $subtype === 0) { - return FALSE; - } - - if ($order_by == "") { - $order_by = "e.time_created desc"; - } - $order_by = sanitise_string($order_by); - $limit = (int)$limit; - $offset = (int)$offset; - $site_guid = (int) $site_guid; - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; - } - - $container_guid = (int)$group_guid; - if ($container_guid == 0) { - $container_guid = elgg_get_page_owner_guid(); - } - - $where = array(); - - $where[] = "e.type='object'"; - - if (!empty($subtype)) { - if (!$subtype = get_subtype_id('object', $subtype)) { - return FALSE; - } - $where[] = "e.subtype=$subtype"; - } - if ($owner_guid != "") { - if (!is_array($owner_guid)) { - $owner_guid = (int) $owner_guid; - $where[] = "e.container_guid = '$owner_guid'"; - } else if (sizeof($owner_guid) > 0) { - // Cast every element to the owner_guid array to int - $owner_guid = array_map("sanitise_int", $owner_guid); - $owner_guid = implode(",", $owner_guid); - $where[] = "e.container_guid in ({$owner_guid})"; - } - } - if ($site_guid > 0) { - $where[] = "e.site_guid = {$site_guid}"; - } - - if ($container_guid > 0) { - $where[] = "e.container_guid = {$container_guid}"; - } - - if (!$count) { - $query = "SELECT * from {$CONFIG->dbprefix}entities e" - . " join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid where "; - } else { - $query = "SELECT count(e.guid) as total from {$CONFIG->dbprefix}entities e" - . " join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid where "; - } - foreach ($where as $w) { - $query .= " $w and "; - } - - // Add access controls - $query .= get_access_sql_suffix('e'); - if (!$count) { - $query .= " order by $order_by"; - - // Add order and limit - if ($limit) { - $query .= " limit $offset, $limit"; - } - - $dt = get_data($query, "entity_row_to_elggstar"); - return $dt; - } else { - $total = get_data_row($query); - return $total->total; - } -} - -/** - * Lists entities that belong to a group. - * - * @param string $subtype The arbitrary subtype of the entity - * @param int $owner_guid The GUID of the owning user - * @param int $container_guid The GUID of the containing group - * @param int $limit The number of entities to display per page (default: 10) - * @param bool $fullview Whether or not to display the full view (default: true) - * @param bool $listtypetoggle Whether or not to allow gallery view (default: true) - * @param bool $pagination Whether to display pagination (default: true) - * - * @return string List of parsed entities - * - * @see elgg_list_entities() - * @deprecated 1.8 Use elgg_list_entities() instead - */ -function list_entities_groups($subtype = "", $owner_guid = 0, $container_guid = 0, -$limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true) { - elgg_deprecated_notice("list_entities_groups was deprecated in 1.8. Use elgg_list_entities() instead.", 1.8); - $offset = (int) get_input('offset'); - $count = get_objects_in_group($container_guid, $subtype, $owner_guid, - 0, "", $limit, $offset, true); - $entities = get_objects_in_group($container_guid, $subtype, $owner_guid, - 0, "", $limit, $offset); - - return elgg_view_entity_list($entities, $count, $offset, $limit, - $fullview, $listtypetoggle, $pagination); -} - -/** - * Get all the entities from metadata from a group. - * - * @param int $group_guid The ID of the group. - * @param mixed $meta_name Metadata name - * @param mixed $meta_value Metadata value - * @param string $entity_type The type of entity to look for, eg 'site' or 'object' - * @param string $entity_subtype The subtype of the entity. - * @param int $owner_guid Owner guid - * @param int $limit Limit - * @param int $offset Offset - * @param string $order_by Optional ordering. - * @param int $site_guid Site GUID. 0 for current, -1 for any - * @param bool $count Return count instead of entities - * - * @return array|false - * @deprecated 1.8 Use elgg_get_entities_from_metadata() - */ -function get_entities_from_metadata_groups($group_guid, $meta_name, $meta_value = "", -$entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, -$order_by = "", $site_guid = 0, $count = false) { - elgg_deprecated_notice("get_entities_from_metadata_groups was deprecated in 1.8.", 1.8); - global $CONFIG; - - $meta_n = get_metastring_id($meta_name); - $meta_v = get_metastring_id($meta_value); - - $entity_type = sanitise_string($entity_type); - $entity_subtype = get_subtype_id($entity_type, $entity_subtype); - $limit = (int)$limit; - $offset = (int)$offset; - if ($order_by == "") { - $order_by = "e.time_created desc"; - } - $order_by = sanitise_string($order_by); - $site_guid = (int) $site_guid; - if (is_array($owner_guid)) { - foreach ($owner_guid as $key => $guid) { - $owner_guid[$key] = (int) $guid; - } - } else { - $owner_guid = (int) $owner_guid; - } - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; - } - - $container_guid = (int)$group_guid; - if ($container_guid == 0) { - $container_guid = elgg_get_page_owner_guid(); - } - - $where = array(); - - if ($entity_type != "") { - $where[] = "e.type='$entity_type'"; - } - if ($entity_subtype) { - $where[] = "e.subtype=$entity_subtype"; - } - if ($meta_name != "") { - $where[] = "m.name_id='$meta_n'"; - } - if ($meta_value != "") { - $where[] = "m.value_id='$meta_v'"; - } - if ($site_guid > 0) { - $where[] = "e.site_guid = {$site_guid}"; - } - if ($container_guid > 0) { - $where[] = "e.container_guid = {$container_guid}"; - } - - if (is_array($owner_guid)) { - $where[] = "e.container_guid in (" . implode(",", $owner_guid ) . ")"; - } else if ($owner_guid > 0) { - $where[] = "e.container_guid = {$owner_guid}"; - } - - if (!$count) { - $query = "SELECT distinct e.* "; - } else { - $query = "SELECT count(e.guid) as total "; - } - - $query .= "from {$CONFIG->dbprefix}entities e" - . " JOIN {$CONFIG->dbprefix}metadata m on e.guid = m.entity_guid " - . " JOIN {$CONFIG->dbprefix}objects_entity o on e.guid = o.guid where"; - - foreach ($where as $w) { - $query .= " $w and "; - } - - // Add access controls - $query .= get_access_sql_suffix("e"); - - if (!$count) { - $query .= " order by $order_by limit $offset, $limit"; // Add order and limit - return get_data($query, "entity_row_to_elggstar"); - } else { - if ($row = get_data_row($query)) { - return $row->total; - } - } - return false; -} - -/** - * As get_entities_from_metadata_groups() but with multiple entities. - * - * @param int $group_guid The ID of the group. - * @param array $meta_array Array of 'name' => 'value' pairs - * @param string $entity_type The type of entity to look for, eg 'site' or 'object' - * @param string $entity_subtype The subtype of the entity. - * @param int $owner_guid Owner GUID - * @param int $limit Limit - * @param int $offset Offset - * @param string $order_by Optional ordering. - * @param int $site_guid Site GUID. 0 for current, -1 for any - * @param bool $count Return count of entities instead of entities - * - * @return int|array List of ElggEntities, or the total number if count is set to false - * @deprecated 1.8 Use elgg_get_entities_from_metadata() - */ -function get_entities_from_metadata_groups_multi($group_guid, $meta_array, $entity_type = "", -$entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", -$site_guid = 0, $count = false) { - elgg_deprecated_notice("get_entities_from_metadata_groups_multi was deprecated in 1.8.", 1.8); - - global $CONFIG; - - if (!is_array($meta_array) || sizeof($meta_array) == 0) { - return false; - } - - $where = array(); - - $mindex = 1; - $join = ""; - foreach ($meta_array as $meta_name => $meta_value) { - $meta_n = get_metastring_id($meta_name); - $meta_v = get_metastring_id($meta_value); - $join .= " JOIN {$CONFIG->dbprefix}metadata m{$mindex} on e.guid = m{$mindex}.entity_guid" - . " JOIN {$CONFIG->dbprefix}objects_entity o on e.guid = o.guid "; - - if ($meta_name != "") { - $where[] = "m{$mindex}.name_id='$meta_n'"; - } - - if ($meta_value != "") { - $where[] = "m{$mindex}.value_id='$meta_v'"; - } - - $mindex++; - } - - $entity_type = sanitise_string($entity_type); - $entity_subtype = get_subtype_id($entity_type, $entity_subtype); - $limit = (int)$limit; - $offset = (int)$offset; - if ($order_by == "") { - $order_by = "e.time_created desc"; - } - $order_by = sanitise_string($order_by); - $owner_guid = (int) $owner_guid; - - $site_guid = (int) $site_guid; - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; - } - - //$access = get_access_list(); - - if ($entity_type != "") { - $where[] = "e.type = '{$entity_type}'"; - } - - if ($entity_subtype) { - $where[] = "e.subtype = {$entity_subtype}"; - } - - if ($site_guid > 0) { - $where[] = "e.site_guid = {$site_guid}"; - } - - if ($owner_guid > 0) { - $where[] = "e.owner_guid = {$owner_guid}"; - } - - if ($container_guid > 0) { - $where[] = "e.container_guid = {$container_guid}"; - } - - if ($count) { - $query = "SELECT count(e.guid) as total "; - } else { - $query = "SELECT distinct e.* "; - } - - $query .= " from {$CONFIG->dbprefix}entities e {$join} where"; - foreach ($where as $w) { - $query .= " $w and "; - } - $query .= get_access_sql_suffix("e"); // Add access controls - - if (!$count) { - $query .= " order by $order_by limit $offset, $limit"; // Add order and limit - return get_data($query, "entity_row_to_elggstar"); - } else { - if ($count = get_data_row($query)) { - return $count->total; - } - } - return false; -} - -/** * Return a list of this group's members. * * @param int $group_guid The ID of the container/group. @@ -531,7 +170,7 @@ function get_group_members($group_guid, $limit = 10, $offset = 0, $site_guid = 0 'relationship' => 'member', 'relationship_guid' => $group_guid, 'inverse_relationship' => TRUE, - 'types' => 'user', + 'type' => 'user', 'limit' => $limit, 'offset' => $offset, 'count' => $count, @@ -567,8 +206,10 @@ function is_group_member($group_guid, $user_guid) { function join_group($group_guid, $user_guid) { $result = add_entity_relationship($user_guid, 'member', $group_guid); - $params = array('group' => get_entity($group_guid), 'user' => get_entity($user_guid)); - elgg_trigger_event('join', 'group', $params); + if ($result) { + $params = array('group' => get_entity($group_guid), 'user' => get_entity($user_guid)); + elgg_trigger_event('join', 'group', $params); + } return $result; } @@ -599,49 +240,52 @@ function leave_group($group_guid, $user_guid) { */ function get_users_membership($user_guid) { $options = array( + 'type' => 'group', 'relationship' => 'member', 'relationship_guid' => $user_guid, - 'inverse_relationship' => FALSE + 'inverse_relationship' => false, + 'limit' => false, ); return elgg_get_entities_from_relationship($options); } /** - * Checks access to a group. + * May the current user access item(s) on this page? If the page owner is a group, + * membership, visibility, and logged in status are taken into account. * * @param boolean $forward If set to true (default), will forward the page; * if set to false, will return true or false. * - * @return true|false If $forward is set to false. + * @return bool If $forward is set to false. */ function group_gatekeeper($forward = true) { - $allowed = true; - $url = ''; - - if ($group = elgg_get_page_owner()) { - if ($group instanceof ElggGroup) { - $url = $group->getURL(); - if ( - ((!isloggedin()) && (!$group->isPublicMembership())) || - ((!$group->isMember(get_loggedin_user()) && (!$group->isPublicMembership()))) - ) { - $allowed = false; - } - // Admin override - if (isadminloggedin()) { - $allowed = true; - } - } + $page_owner_guid = elgg_get_page_owner_guid(); + if (!$page_owner_guid) { + return true; + } + $visibility = ElggGroupItemVisibility::factory($page_owner_guid); + + if (!$visibility->shouldHideItems) { + return true; } + if ($forward) { + // only forward to group if user can see it + $group = get_entity($page_owner_guid); + $forward_url = $group ? $group->getURL() : ''; + + if (!elgg_is_logged_in()) { + $_SESSION['last_forward_from'] = current_page_url(); + $forward_reason = 'login'; + } else { + $forward_reason = 'member'; + } - if ($forward && $allowed == false) { - register_error(elgg_echo('membershiprequired')); - forward($url, 'member'); - exit; + register_error(elgg_echo($visibility->reasonHidden)); + forward($forward_url, $forward_reason); } - return $allowed; + return false; } /** @@ -654,6 +298,7 @@ function group_gatekeeper($forward = true) { * @param bool $default_on True if this option should be active by default * * @return void + * @since 1.5.0 */ function add_group_tool_option($name, $label, $default_on = true) { global $CONFIG; @@ -679,6 +324,7 @@ function add_group_tool_option($name, $label, $default_on = true) { * @param string $name Name of the group tool option * * @return void + * @since 1.7.5 */ function remove_group_tool_option($name) { global $CONFIG; @@ -693,121 +339,3 @@ function remove_group_tool_option($name) { } } } - - -/** - * Searches for a group based on a complete or partial name or description - * - * @param string $criteria The partial or full name or description - * @param int $limit Limit of the search. - * @param int $offset Offset. - * @param string $order_by The order. - * @param boolean $count Whether to return the count of results or just the results. - * - * @return mixed - * @deprecated 1.7 - */ -function search_for_group($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) { - elgg_deprecated_notice('search_for_group() was deprecated by new search plugin.', 1.7); - global $CONFIG; - - $criteria = sanitise_string($criteria); - $limit = (int)$limit; - $offset = (int)$offset; - $order_by = sanitise_string($order_by); - - $access = get_access_sql_suffix("e"); - - if ($order_by == "") { - $order_by = "e.time_created desc"; - } - - if ($count) { - $query = "SELECT count(e.guid) as total "; - } else { - $query = "SELECT e.* "; - } - $query .= "from {$CONFIG->dbprefix}entities e" - . " JOIN {$CONFIG->dbprefix}groups_entity g on e.guid=g.guid where "; - - $query .= "(g.name like \"%{$criteria}%\" or g.description like \"%{$criteria}%\")"; - $query .= " and $access"; - - if (!$count) { - $query .= " order by $order_by limit $offset, $limit"; // Add order and limit - return get_data($query, "entity_row_to_elggstar"); - } else { - if ($count = get_data_row($query)) { - return $count->total; - } - } - return false; -} - -/** - * Returns a formatted list of groups suitable for injecting into search. - * - * @deprecated 1.7 - * - * @param string $hook Hook name - * @param string $user User - * @param mixed $returnvalue Previous hook's return value - * @param string $tag Tag to search on - * - * @return string - */ -function search_list_groups_by_name($hook, $user, $returnvalue, $tag) { - elgg_deprecated_notice('search_list_groups_by_name() was deprecated by new search plugin', 1.7); - // Change this to set the number of groups that display on the search page - $threshold = 4; - - $object = get_input('object'); - - if (!get_input('offset') && (empty($object) || $object == 'group')) { - if ($groups = search_for_group($tag, $threshold)) { - $countgroups = search_for_group($tag, 0, 0, "", true); - - $return = elgg_view('group/search/startblurb', array('count' => $countgroups, 'tag' => $tag)); - foreach ($groups as $group) { - $return .= elgg_view_entity($group); - } - $vars = array('count' => $countgroups, 'threshold' => $threshold, 'tag' => $tag); - $return .= elgg_view('group/search/finishblurb', $vars); - return $return; - } - } -} - -/** - * Displays a list of group objects that have been searched for. - * - * @see elgg_view_entity_list - * - * @param string $tag Search criteria - * @param int $limit The number of entities to display on a page - * - * @return string The list in a form suitable to display - * @deprecated 1.7 - */ -function list_group_search($tag, $limit = 10) { - elgg_deprecated_notice('list_group_search() was deprecated by new search plugin.', 1.7); - $offset = (int) get_input('offset'); - $limit = (int) $limit; - $count = (int) search_for_group($tag, 10, 0, '', true); - $entities = search_for_group($tag, $limit, $offset); - - return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, false); - -} - -/** - * Performs initialisation functions for groups - * - * @return void - */ -function group_init() { - // Register an entity type - register_entity_type('group', ''); -} - -elgg_register_event_handler('init', 'system', 'group_init'); diff --git a/engine/lib/input.php b/engine/lib/input.php index 76daf5fa3..80b0b8766 100644 --- a/engine/lib/input.php +++ b/engine/lib/input.php @@ -8,46 +8,51 @@ */ /** - * Get some input from variables passed on the GET or POST line. + * Get some input from variables passed submitted through GET or POST. + * + * If using any data obtained from get_input() in a web page, please be aware that + * it is a possible vector for a reflected XSS attack. If you are expecting an + * integer, cast it to an int. If it is a string, escape quotes. * * Note: this function does not handle nested arrays (ex: form input of param[m][n]) * because of the filtering done in htmlawed from the filter_tags call. + * @todo Is this ^ still true? * - * @param string $variable The variable we want to return. + * @param string $variable The variable name we want. * @param mixed $default A default value for the variable if it is not found. - * @param bool $filter_result If true then the result is filtered for bad tags. + * @param bool $filter_result If true, then the result is filtered for bad tags. * - * @return string + * @return mixed */ function get_input($variable, $default = NULL, $filter_result = TRUE) { global $CONFIG; + $result = $default; + + elgg_push_context('input'); + if (isset($CONFIG->input[$variable])) { - $var = $CONFIG->input[$variable]; + $result = $CONFIG->input[$variable]; if ($filter_result) { - $var = filter_tags($var); + $result = filter_tags($result); } - - return $var; - } - - if (isset($_REQUEST[$variable])) { + } elseif (isset($_REQUEST[$variable])) { if (is_array($_REQUEST[$variable])) { - $var = $_REQUEST[$variable]; + $result = $_REQUEST[$variable]; } else { - $var = trim($_REQUEST[$variable]); + $result = trim($_REQUEST[$variable]); } if ($filter_result) { - $var = filter_tags($var); + $result = filter_tags($result); } - - return $var; } - return $default; + elgg_pop_context(); + + return $result; } /** @@ -55,8 +60,8 @@ function get_input($variable, $default = NULL, $filter_result = TRUE) { * * Note: this function does not handle nested arrays (ex: form input of param[m][n]) * - * @param string $variable The name of the variable - * @param string $value The value of the variable + * @param string $variable The name of the variable + * @param string|string[] $value The value of the variable * * @return void */ @@ -98,20 +103,151 @@ function is_email_address($address) { } /** + * Load all the REQUEST variables into the sticky form cache + * + * Call this from an action when you want all your submitted variables + * available if the submission fails validation and is sent back to the form + * + * @param string $form_name Name of the sticky form + * + * @return void + * @link http://docs.elgg.org/Tutorials/UI/StickyForms + * @since 1.8.0 + */ +function elgg_make_sticky_form($form_name) { + + elgg_clear_sticky_form($form_name); + + if (!isset($_SESSION['sticky_forms'])) { + $_SESSION['sticky_forms'] = array(); + } + $_SESSION['sticky_forms'][$form_name] = array(); + + foreach ($_REQUEST as $key => $var) { + // will go through XSS filtering on the get function + $_SESSION['sticky_forms'][$form_name][$key] = $var; + } +} + +/** + * Clear the sticky form cache + * + * Call this if validation is successful in the action handler or + * when they sticky values have been used to repopulate the form + * after a validation error. + * + * @param string $form_name Form namespace + * + * @return void + * @link http://docs.elgg.org/Tutorials/UI/StickyForms + * @since 1.8.0 + */ +function elgg_clear_sticky_form($form_name) { + unset($_SESSION['sticky_forms'][$form_name]); +} + +/** + * Has this form been made sticky? + * + * @param string $form_name Form namespace + * + * @return boolean + * @link http://docs.elgg.org/Tutorials/UI/StickyForms + * @since 1.8.0 + */ +function elgg_is_sticky_form($form_name) { + return isset($_SESSION['sticky_forms'][$form_name]); +} + +/** + * Get a specific sticky variable + * + * @param string $form_name The name of the form + * @param string $variable The name of the variable + * @param mixed $default Default value if the variable does not exist in sticky cache + * @param boolean $filter_result Filter for bad input if true + * + * @return mixed + * + * @todo should this filter the default value? + * @link http://docs.elgg.org/Tutorials/UI/StickyForms + * @since 1.8.0 + */ +function elgg_get_sticky_value($form_name, $variable = '', $default = NULL, $filter_result = true) { + if (isset($_SESSION['sticky_forms'][$form_name][$variable])) { + $value = $_SESSION['sticky_forms'][$form_name][$variable]; + if ($filter_result) { + // XSS filter result + $value = filter_tags($value); + } + return $value; + } + return $default; +} + +/** + * Get all the values in a sticky form in an array + * + * @param string $form_name The name of the form + * @param bool $filter_result Filter for bad input if true + * + * @return array + * @since 1.8.0 + */ +function elgg_get_sticky_values($form_name, $filter_result = true) { + if (!isset($_SESSION['sticky_forms'][$form_name])) { + return array(); + } + + $values = $_SESSION['sticky_forms'][$form_name]; + if ($filter_result) { + foreach ($values as $key => $value) { + // XSS filter result + $values[$key] = filter_tags($value); + } + } + return $values; +} + +/** + * Clear a specific sticky variable + * + * @param string $form_name The name of the form + * @param string $variable The name of the variable to clear + * + * @return void + * @link http://docs.elgg.org/Tutorials/UI/StickyForms + * @since 1.8.0 + */ +function elgg_clear_sticky_value($form_name, $variable) { + unset($_SESSION['sticky_forms'][$form_name][$variable]); +} + +/** * Page handler for autocomplete endpoint. * - * @param array $page Pages array + * @todo split this into functions/objects, this is way too big * - * @return unknown_type + * /livesearch?q=<query> + * + * Other options include: + * match_on string all or array(groups|users|friends) + * match_owner int 0/1 + * limit int default is 10 + * + * @param array $page + * @return string JSON string is returned and then exit + * @access private */ function input_livesearch_page_handler($page) { global $CONFIG; + // only return results to logged in users. - if (!$user = get_loggedin_user()) { + if (!$user = elgg_get_logged_in_user_entity()) { exit; } - if (!$q = get_input('q')) { + if (!$q = get_input('term', get_input('q'))) { exit; } @@ -121,91 +257,123 @@ function input_livesearch_page_handler($page) { $q = str_replace(array('_', '%'), array('\_', '\%'), $q); $match_on = get_input('match_on', 'all'); - if ($match_on == 'all' || $match_on[0] == 'all') { - $match_on = array('users', 'groups'); - } if (!is_array($match_on)) { $match_on = array($match_on); } + // all = users and groups + if (in_array('all', $match_on)) { + $match_on = array('users', 'groups'); + } + if (get_input('match_owner', false)) { - $owner_guid = $user->getGUID(); $owner_where = 'AND e.owner_guid = ' . $user->getGUID(); } else { - $owner_guid = null; $owner_where = ''; } - $limit = get_input('limit', 10); + $limit = sanitise_int(get_input('limit', 10)); // grab a list of entities and send them in json. $results = array(); - foreach ($match_on as $type) { - switch ($type) { - case 'all': - // only need to pull up title from objects. - - $options = array('owner_guid' => $owner_guid, 'limit' => $limit); - if (!$entities = elgg_get_entities($options) AND is_array($entities)) { - $results = array_merge($results, $entities); - } - break; - + foreach ($match_on as $match_type) { + switch ($match_type) { case 'users': $query = "SELECT * FROM {$CONFIG->dbprefix}users_entity as ue, {$CONFIG->dbprefix}entities as e WHERE e.guid = ue.guid AND e.enabled = 'yes' AND ue.banned = 'no' - AND (ue.name LIKE '$q%' OR ue.username LIKE '$q%') + AND (ue.name LIKE '$q%' OR ue.name LIKE '% $q%' OR ue.username LIKE '$q%') LIMIT $limit "; if ($entities = get_data($query)) { foreach ($entities as $entity) { - $json = json_encode(array( + // @todo use elgg_get_entities (don't query in a loop!) + $entity = get_entity($entity->guid); + /* @var ElggUser $entity */ + if (!$entity) { + continue; + } + + if (in_array('groups', $match_on)) { + $value = $entity->guid; + } else { + $value = $entity->username; + } + + $output = elgg_view_list_item($entity, array( + 'use_hover' => false, + 'class' => 'elgg-autocomplete-item', + )); + + $icon = elgg_view_entity_icon($entity, 'tiny', array( + 'use_hover' => false, + )); + + $result = array( 'type' => 'user', 'name' => $entity->name, 'desc' => $entity->username, - 'icon' => '<img class="livesearch_icon" src="' . - get_entity($entity->guid)->getIcon('tiny') . '" />', - 'guid' => $entity->guid - )); - $results[$entity->name . rand(1, 100)] = $json; + 'guid' => $entity->guid, + 'label' => $output, + 'value' => $value, + 'icon' => $icon, + 'url' => $entity->getURL(), + ); + $results[$entity->name . rand(1, 100)] = $result; } } break; case 'groups': // don't return results if groups aren't enabled. - if (!is_plugin_enabled('groups')) { + if (!elgg_is_active_plugin('groups')) { continue; } $query = "SELECT * FROM {$CONFIG->dbprefix}groups_entity as ge, {$CONFIG->dbprefix}entities as e WHERE e.guid = ge.guid AND e.enabled = 'yes' $owner_where - AND (ge.name LIKE '$q%' OR ge.description LIKE '%$q%') + AND (ge.name LIKE '$q%' OR ge.name LIKE '% $q%' OR ge.description LIKE '% $q%') LIMIT $limit "; if ($entities = get_data($query)) { foreach ($entities as $entity) { - $json = json_encode(array( + // @todo use elgg_get_entities (don't query in a loop!) + $entity = get_entity($entity->guid); + /* @var ElggGroup $entity */ + if (!$entity) { + continue; + } + + $output = elgg_view_list_item($entity, array( + 'use_hover' => false, + 'class' => 'elgg-autocomplete-item', + )); + + $icon = elgg_view_entity_icon($entity, 'tiny', array( + 'use_hover' => false, + )); + + $result = array( 'type' => 'group', 'name' => $entity->name, 'desc' => strip_tags($entity->description), - 'icon' => '<img class="livesearch_icon" src="' - . get_entity($entity->guid)->getIcon('tiny') . '" />', - 'guid' => $entity->guid - )); - - $results[$entity->name . rand(1, 100)] = $json; + 'guid' => $entity->guid, + 'label' => $output, + 'value' => $entity->guid, + 'icon' => $icon, + 'url' => $entity->getURL(), + ); + + $results[$entity->name . rand(1, 100)] = $result; } } break; case 'friends': - $access = get_access_sql_suffix(); $query = "SELECT * FROM {$CONFIG->dbprefix}users_entity as ue, {$CONFIG->dbprefix}entity_relationships as er, @@ -216,36 +384,54 @@ function input_livesearch_page_handler($page) { AND e.guid = ue.guid AND e.enabled = 'yes' AND ue.banned = 'no' - AND (ue.name LIKE '$q%' OR ue.username LIKE '$q%') + AND (ue.name LIKE '$q%' OR ue.name LIKE '% $q%' OR ue.username LIKE '$q%') LIMIT $limit "; if ($entities = get_data($query)) { foreach ($entities as $entity) { - $json = json_encode(array( + // @todo use elgg_get_entities (don't query in a loop!) + $entity = get_entity($entity->guid); + /* @var ElggUser $entity */ + if (!$entity) { + continue; + } + + $output = elgg_view_list_item($entity, array( + 'use_hover' => false, + 'class' => 'elgg-autocomplete-item', + )); + + $icon = elgg_view_entity_icon($entity, 'tiny', array( + 'use_hover' => false, + )); + + $result = array( 'type' => 'user', 'name' => $entity->name, 'desc' => $entity->username, - 'icon' => '<img class="livesearch_icon" src="' - . get_entity($entity->guid)->getIcon('tiny') . '" />', - 'guid' => $entity->guid - )); - $results[$entity->name . rand(1, 100)] = $json; + 'guid' => $entity->guid, + 'label' => $output, + 'value' => $entity->username, + 'icon' => $icon, + 'url' => $entity->getURL(), + ); + $results[$entity->name . rand(1, 100)] = $result; } } break; default: - // arbitrary subtype. - //@todo you cannot specify a subtype without a type. - // did this ever work? - elgg_get_entities(array('subtype' => $type, 'owner_guid' => $owner_guid)); + header("HTTP/1.0 400 Bad Request", true); + echo "livesearch: unknown match_on of $match_type"; + exit; break; } } ksort($results); - echo implode($results, "\n"); + header("Content-Type: application/json"); + echo json_encode(array_values($results)); exit; } @@ -253,10 +439,11 @@ function input_livesearch_page_handler($page) { * Register input functions and sanitize input * * @return void + * @access private */ function input_init() { // register an endpoint for live search / autocomplete. - register_page_handler('livesearch', 'input_livesearch_page_handler'); + elgg_register_page_handler('livesearch', 'input_livesearch_page_handler'); if (ini_get_bool('magic_quotes_gpc')) { diff --git a/engine/lib/install.php b/engine/lib/install.php deleted file mode 100644 index cfb8ac7ec..000000000 --- a/engine/lib/install.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/** - * Elgg installation - * Various functions to assist with installing and upgrading the system - * - * @package Elgg.Core - * @subpackage Installation - */ - -// these were internal functions that perhaps can be removed rather than deprecated -function is_db_installed() { - elgg_deprecated_notice('is_db_installed() has been deprecated', 1.8); - return true; -} - -function is_installed() { - elgg_deprecated_notice('is_installed() has been deprecated', 1.8); - return true; -} - diff --git a/engine/lib/languages.php b/engine/lib/languages.php index d34a89707..61ba91ddb 100644 --- a/engine/lib/languages.php +++ b/engine/lib/languages.php @@ -8,6 +8,65 @@ */ /** + * Given a message key, returns an appropriately translated full-text string + * + * @param string $message_key The short message code + * @param array $args An array of arguments to pass through vsprintf(). + * @param string $language Optionally, the standard language code + * (defaults to site/user default, then English) + * + * @return string Either the translated string, the English string, + * or the original language string. + */ +function elgg_echo($message_key, $args = array(), $language = "") { + global $CONFIG; + + static $CURRENT_LANGUAGE; + + // old param order is deprecated + if (!is_array($args)) { + elgg_deprecated_notice( + 'As of Elgg 1.8, the 2nd arg to elgg_echo() is an array of string replacements and the 3rd arg is the language.', + 1.8 + ); + + $language = $args; + $args = array(); + } + + if (!isset($CONFIG->translations)) { + // this means we probably had an exception before translations were initialized + register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/"); + } + + if (!$CURRENT_LANGUAGE) { + $CURRENT_LANGUAGE = get_language(); + } + if (!$language) { + $language = $CURRENT_LANGUAGE; + } + + if (isset($CONFIG->translations[$language][$message_key])) { + $string = $CONFIG->translations[$language][$message_key]; + } else if (isset($CONFIG->translations["en"][$message_key])) { + $string = $CONFIG->translations["en"][$message_key]; + $lang = $CONFIG->translations["en"][$language]; + elgg_log(sprintf('Missing %s translation for "%s" language key', $lang, $message_key), 'NOTICE'); + } else { + $string = $message_key; + elgg_log(sprintf('Missing English translation for "%s" language key', $message_key), 'NOTICE'); + } + + // only pass through if we have arguments to allow backward compatibility + // with manual sprintf() calls. + if ($args) { + $string = vsprintf($string, $args); + } + + return $string; +} + +/** * Add a translation. * * Translations are arrays in the Zend Translation array format, eg: @@ -18,7 +77,7 @@ * @param string $country_code Standard country code (eg 'en', 'nl', 'es') * @param array $language_array Formatted array of strings * - * @return true|false Depending on success + * @return bool Depending on success */ function add_translation($country_code, $language_array) { global $CONFIG; @@ -45,8 +104,6 @@ function add_translation($country_code, $language_array) { * @return string The language code for the site/user or "en" if not set */ function get_current_language() { - global $CONFIG; - $language = get_language(); if (!$language) { @@ -64,7 +121,7 @@ function get_current_language() { function get_language() { global $CONFIG; - $user = get_loggedin_user(); + $user = elgg_get_logged_in_user_entity(); $language = false; if (($user) && ($user->language)) { @@ -83,55 +140,36 @@ function get_language() { } /** - * Given a message shortcode, returns an appropriately translated full-text string - * - * @param string $message_key The short message code - * @param array $args An array of arguments to pass through vsprintf(). - * @param string $language Optionally, the standard language code - * (defaults to site/user default, then English) - * - * @return string Either the translated string, the English string, - * or the original language string. + * @access private */ -function elgg_echo($message_key, $args = array(), $language = "") { +function _elgg_load_translations() { global $CONFIG; - static $CURRENT_LANGUAGE; - - // old param order is deprecated - if (!is_array($args)) { - elgg_deprecated_notice( - 'As of Elgg 1.8, the 2nd arg to elgg_echo() is an array of string replacements and the 3rd arg is the language.', - 1.8 - ); - - $language = $args; - $args = array(); - } + if ($CONFIG->system_cache_enabled) { + $loaded = true; + $languages = array_unique(array('en', get_current_language())); + foreach ($languages as $language) { + $data = elgg_load_system_cache("$language.lang"); + if ($data) { + add_translation($language, unserialize($data)); + } else { + $loaded = false; + } + } - if (!$CURRENT_LANGUAGE) { - $CURRENT_LANGUAGE = get_language(); - } - if (!$language) { - $language = $CURRENT_LANGUAGE; + if ($loaded) { + $CONFIG->i18n_loaded_from_cache = true; + // this is here to force + $CONFIG->language_paths[dirname(dirname(dirname(__FILE__))) . "/languages/"] = true; + return; + } } - if (isset($CONFIG->translations[$language][$message_key])) { - $string = $CONFIG->translations[$language][$message_key]; - } else if (isset($CONFIG->translations["en"][$message_key])) { - $string = $CONFIG->translations["en"][$message_key]; - } else { - $string = $message_key; - } + // load core translations from languages directory + register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/"); +} - // only pass through if we have arguments to allow backward compatibility - // with manual sprintf() calls. - if ($args) { - $string = vsprintf($string, $args); - } - return $string; -} /** * When given a full path, finds translation files and loads them @@ -140,11 +178,13 @@ function elgg_echo($message_key, $args = array(), $language = "") { * @param bool $load_all If true all languages are loaded, if * false only the current language + en are loaded * - * @return void + * @return bool success */ function register_translations($path, $load_all = false) { global $CONFIG; + $path = sanitise_filepath($path); + // Make a note of this path just incase we need to register this language later if (!isset($CONFIG->language_paths)) { $CONFIG->language_paths = array(); @@ -155,40 +195,72 @@ function register_translations($path, $load_all = false) { $current_language = get_current_language(); elgg_log("Translations loaded from: $path"); - if ($handle = opendir($path)) { - while ($language = readdir($handle)) { - if ( - ((in_array($language, array('en.php', $current_language . '.php'))) ) || - (($load_all) && (strpos($language, '.php') !== false)) - ) { - include_once($path . $language); + // only load these files unless $load_all is true. + $load_language_files = array( + 'en.php', + "$current_language.php" + ); + + $load_language_files = array_unique($load_language_files); + + $handle = opendir($path); + if (!$handle) { + elgg_log("Could not open language path: $path", 'ERROR'); + return false; + } + + $return = true; + while (false !== ($language = readdir($handle))) { + // ignore bad files + if (substr($language, 0, 1) == '.' || substr($language, -4) !== '.php') { + continue; + } + + if (in_array($language, $load_language_files) || $load_all) { + if (!include_once($path . $language)) { + $return = false; + continue; } } - } else { - elgg_log("Missing translation path $path", 'ERROR'); } + + return $return; } /** * Reload all translations from all registered paths. * - * This is only called by functions which need to know all possible translations, namely the - * statistic gathering ones. + * This is only called by functions which need to know all possible translations. * * @todo Better on demand loading based on language_paths array * - * @return bool + * @return void */ function reload_all_translations() { global $CONFIG; static $LANG_RELOAD_ALL_RUN; if ($LANG_RELOAD_ALL_RUN) { - return null; + return; } - foreach ($CONFIG->language_paths as $path => $dummy) { - register_translations($path, true); + if ($CONFIG->i18n_loaded_from_cache) { + $cache = elgg_get_system_cache(); + $cache_dir = $cache->getVariable("cache_path"); + $filenames = elgg_get_file_list($cache_dir, array(), array(), array(".lang")); + foreach ($filenames as $filename) { + if (preg_match('/([a-z]+)\.[^.]+$/', $filename, $matches)) { + $language = $matches[1]; + $data = elgg_load_system_cache("$language.lang"); + if ($data) { + add_translation($language, unserialize($data)); + } + } + } + } else { + foreach ($CONFIG->language_paths as $path => $dummy) { + register_translations($path, true); + } } $LANG_RELOAD_ALL_RUN = true; @@ -209,8 +281,8 @@ function get_installed_translations() { $installed = array(); foreach ($CONFIG->translations as $k => $v) { - $installed[$k] = elgg_echo($k, $k); - if (isadminloggedin()) { + $installed[$k] = elgg_echo($k, array(), $k); + if (elgg_is_admin_logged_in()) { $completeness = get_language_completeness($k); if (($completeness < 100) && ($k != 'en')) { $installed[$k] .= " (" . $completeness . "% " . elgg_echo('complete') . ")"; @@ -280,5 +352,3 @@ function get_missing_language_keys($language) { return false; } - -register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/");
\ No newline at end of file diff --git a/engine/lib/location.php b/engine/lib/location.php index 4819bc9b0..1534c7d7b 100644 --- a/engine/lib/location.php +++ b/engine/lib/location.php @@ -54,87 +54,6 @@ function elgg_geocode_location($location) { /** * Return entities within a given geographic area. * - * @param float $lat Latitude - * @param float $long Longitude - * @param float $radius The radius - * @param string $type The type of entity (eg "user", "object" etc) - * @param string $subtype The arbitrary subtype of the entity - * @param int $owner_guid The GUID of the owning user - * @param string $order_by The field to order by; by default, time_created desc - * @param int $limit The number of entities to return; 10 by default - * @param int $offset The indexing offset, 0 by default - * @param boolean $count Count entities - * @param int $site_guid Site GUID. 0 for current, -1 for any - * @param int|array $container_guid Container GUID - * - * @return array A list of entities. - * @deprecated 1.8 - */ -function get_entities_in_area($lat, $long, $radius, $type = "", $subtype = "", $owner_guid = 0, -$order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = NULL) { - elgg_deprecated_notice('get_entities_in_area() was deprecated by elgg_get_entities_from_location()!', 1.8); - - $options = array(); - - $options['latitude'] = $lat; - $options['longitude'] = $long; - $options['distance'] = $radius; - - // set container_guid to owner_guid to emulate old functionality - if ($owner_guid != "") { - if (is_null($container_guid)) { - $container_guid = $owner_guid; - } - } - - if ($type) { - $options['types'] = $type; - } - - if ($subtype) { - $options['subtypes'] = $subtype; - } - - if ($owner_guid) { - if (is_array($owner_guid)) { - $options['owner_guids'] = $owner_guid; - } else { - $options['owner_guid'] = $owner_guid; - } - } - - if ($container_guid) { - if (is_array($container_guid)) { - $options['container_guids'] = $container_guid; - } else { - $options['container_guid'] = $container_guid; - } - } - - $options['limit'] = $limit; - - if ($offset) { - $options['offset'] = $offset; - } - - if ($order_by) { - $options['order_by']; - } - - if ($site_guid) { - $options['site_guid']; - } - - if ($count) { - $options['count'] = $count; - } - - return elgg_get_entities_from_location($options); -} - -/** - * Return entities within a given geographic area. - * * Also accepts all options available to elgg_get_entities(). * * @see elgg_get_entities @@ -155,7 +74,7 @@ $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, $conta * * @see ElggEntity::setLatLong() * - * @return array + * @return mixed If count, int. If not count, array. false on errors. * @since 1.8.0 */ function elgg_get_entities_from_location(array $options = array()) { @@ -182,7 +101,7 @@ function elgg_get_entities_from_location(array $options = array()) { $long_min = $long - $long_distance; $long_max = $long + $long_distance; - $where = array(); + $wheres = array(); $wheres[] = "lat_name.string='geo:lat'"; $wheres[] = "lat_value.string >= $lat_min"; $wheres[] = "lat_value.string <= $lat_max"; @@ -218,32 +137,9 @@ function elgg_get_entities_from_location(array $options = array()) { } /** - * List entities in a given location - * - * @param string $location Location - * @param string $type The type of entity (eg "user", "object" etc) - * @param string $subtype The arbitrary subtype of the entity - * @param int $owner_guid The GUID of the owning user - * @param int $limit The number of entities to display per page (default: 10) - * @param bool $fullview Whether or not to display the full view (default: true) - * @param bool $listtypetoggle Whether or not to allow gallery view - * @param bool $navigation Display pagination? Default: true - * - * @return string A viewable list of entities - * @deprecated 1.8 - */ -function list_entities_location($location, $type= "", $subtype = "", $owner_guid = 0, $limit = 10, -$fullview = true, $listtypetoggle = false, $navigation = true) { - elgg_deprecated_notice('list_entities_location() was deprecated. Use elgg_list_entities_from_metadata()', 1.8); - - return list_entities_from_metadata('location', $location, $type, $subtype, $owner_guid, $limit, - $fullview, $listtypetoggle, $navigation); -} - -/** * Returns a viewable list of entities from location * - * @param array $options + * @param array $options Options array * * @see elgg_list_entities() * @see elgg_get_entities_from_location() @@ -255,58 +151,6 @@ function elgg_list_entities_from_location(array $options = array()) { return elgg_list_entities($options, 'elgg_get_entities_from_location'); } -/** - * List items within a given geographic area. - * - * @param real $lat Latitude - * @param real $long Longitude - * @param real $radius The radius - * @param string $type The type of entity (eg "user", "object" etc) - * @param string $subtype The arbitrary subtype of the entity - * @param int $owner_guid The GUID of the owning user - * @param int $limit The number of entities to display per page (default: 10) - * @param bool $fullview Whether or not to display the full view (default: true) - * @param bool $listtypetoggle Whether or not to allow gallery view - * @param bool $navigation Display pagination? Default: true - * - * @return string A viewable list of entities - * @deprecated 1.8 - */ -function list_entities_in_area($lat, $long, $radius, $type= "", $subtype = "", $owner_guid = 0, -$limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) { - elgg_deprecated_notice('list_entities_in_area() was deprecated. Use elgg_list_entities_from_location()', 1.8); - - $options = array(); - - $options['latitude'] = $lat; - $options['longitude'] = $long; - $options['distance'] = $radius; - - if ($type) { - $options['types'] = $type; - } - - if ($subtype) { - $options['subtypes'] = $subtype; - } - - if ($owner_guid) { - if (is_array($owner_guid)) { - $options['owner_guids'] = $owner_guid; - } else { - $options['owner_guid'] = $owner_guid; - } - } - - $options['limit'] = $limit; - - $options['full_view'] = $fullview; - $options['list_type_toggle'] = $listtypetoggle; - $options['pagination'] = $pagination; - - return elgg_list_entities_from_location($options); -} - // Some distances in degrees (approximate) // @todo huh? see warning on elgg_get_entities_from_location() define("MILE", 0.01515); diff --git a/engine/lib/mb_wrapper.php b/engine/lib/mb_wrapper.php index da7a96c1f..68fa69005 100644 --- a/engine/lib/mb_wrapper.php +++ b/engine/lib/mb_wrapper.php @@ -11,7 +11,7 @@ if (is_callable('mb_internal_encoding')) { * NOTE: This differs from parse_str() by returning the results * instead of placing them in the local scope! * - * @param str $str The string + * @param string $str The string * * @return array * @since 1.7.0 @@ -230,4 +230,4 @@ function elgg_substr() { return call_user_func_array('mb_substr', $args); } return call_user_func_array('substr', $args); -}
\ No newline at end of file +} diff --git a/engine/lib/memcache.php b/engine/lib/memcache.php index 6e905b0d4..79b87e850 100644 --- a/engine/lib/memcache.php +++ b/engine/lib/memcache.php @@ -34,4 +34,24 @@ function is_memcache_available() { } return $memcache_available; +} + +/** + * Invalidate an entity in memcache + * + * @param int $entity_guid The GUID of the entity to invalidate + * + * @return void + * @access private + */ +function _elgg_invalidate_memcache_for_entity($entity_guid) { + static $newentity_cache; +
+ if ((!$newentity_cache) && (is_memcache_available())) {
+ $newentity_cache = new ElggMemcache('new_entity_cache');
+ } +
+ if ($newentity_cache) {
+ $newentity_cache->delete($entity_guid);
+ } }
\ No newline at end of file diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php index 12c8a74af..fdb1b85f6 100644 --- a/engine/lib/metadata.php +++ b/engine/lib/metadata.php @@ -12,7 +12,8 @@ * * @param stdClass $row An object from the database * - * @return stdClass or ElggMetadata + * @return stdClass|ElggMetadata + * @access private */ function row_to_elggmetadata($row) { if (!($row instanceof stdClass)) { @@ -23,66 +24,30 @@ function row_to_elggmetadata($row) { } /** - * Get a specific metadata object. + * Get a specific metadata object by its id. + * If you want multiple metadata objects, use + * {@link elgg_get_metadata()}. * - * @param int $id The id of the metadata being retrieved. + * @param int $id The id of the metadata object being retrieved. * - * @return mixed False on failure or ElggMetadata + * @return ElggMetadata|false FALSE if not found */ -function get_metadata($id) { - global $CONFIG; - - $id = (int)$id; - $access = get_access_sql_suffix("e"); - $md_access = get_access_sql_suffix("m"); - - $query = "SELECT m.*, n.string as name, v.string as value from {$CONFIG->dbprefix}metadata m" - . " JOIN {$CONFIG->dbprefix}entities e on e.guid = m.entity_guid" - . " JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id" - . " JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id" - . " where m.id=$id and $access and $md_access"; - - - return row_to_elggmetadata(get_data_row($query)); +function elgg_get_metadata_from_id($id) { + return elgg_get_metastring_based_object_from_id($id, 'metadata'); } /** - * Removes metadata on an entity with a particular name, optionally with a given value. - * - * @param int $entity_guid The entity GUID - * @param string $name The name of the metadata - * @param string $value The value of the metadata (useful to remove a single item of a set) + * Deletes metadata using its ID. * - * @return bool Depending on success + * @param int $id The metadata ID to delete. + * @return bool */ -function remove_metadata($entity_guid, $name, $value = "") { - global $CONFIG; - $entity_guid = (int) $entity_guid; - $name = sanitise_string($name); - $value = sanitise_string($value); - - $name = get_metastring_id($name); - if ($name === FALSE) { - // name doesn't exist - return FALSE; - } - - $query = "SELECT * from {$CONFIG->dbprefix}metadata WHERE entity_guid = '$entity_guid' and name_id = '$name'"; - if ($value != "") { - $value = get_metastring_id($value); - if ($value !== FALSE) { - $query .= " AND value_id = '$value'"; - } - } - - if ($existing = get_data($query)) { - foreach ($existing as $ex) { - delete_metadata($ex->id); - } - return true; +function elgg_delete_metadata_by_id($id) { + $metadata = elgg_get_metadata_from_id($id); + if (!$metadata) { + return false; } - - return false; + return $metadata->delete(); } /** @@ -99,9 +64,9 @@ function remove_metadata($entity_guid, $name, $value = "") { * @param int $access_id Default is ACCESS_PRIVATE * @param bool $allow_multiple Allow multiple values for one key. Default is FALSE * - * @return int/bool id of metadata or FALSE if failure + * @return int|false id of metadata or FALSE if failure */ -function create_metadata($entity_guid, $name, $value, $value_type, $owner_guid, +function create_metadata($entity_guid, $name, $value, $value_type = '', $owner_guid = 0, $access_id = ACCESS_PRIVATE, $allow_multiple = false) { global $CONFIG; @@ -120,13 +85,11 @@ function create_metadata($entity_guid, $name, $value, $value_type, $owner_guid, } if ($owner_guid == 0) { - $owner_guid = get_loggedin_userid(); + $owner_guid = elgg_get_logged_in_user_guid(); } $access_id = (int)$access_id; - $id = false; - $query = "SELECT * from {$CONFIG->dbprefix}metadata" . " WHERE entity_guid = $entity_guid and name_id=" . add_metastring($name) . " limit 1"; @@ -141,37 +104,36 @@ function create_metadata($entity_guid, $name, $value, $value_type, $owner_guid, } else { // Support boolean types if (is_bool($value)) { - if ($value) { - $value = 1; - } else { - $value = 0; - } + $value = (int) $value; } // Add the metastrings - $value = add_metastring($value); - if (!$value) { + $value_id = add_metastring($value); + if (!$value_id) { return false; } - $name = add_metastring($name); - if (!$name) { + $name_id = add_metastring($name); + if (!$name_id) { return false; } // If ok then add it $query = "INSERT into {$CONFIG->dbprefix}metadata" . " (entity_guid, name_id, value_id, value_type, owner_guid, time_created, access_id)" - . " VALUES ($entity_guid, '$name','$value','$value_type', $owner_guid, $time, $access_id)"; + . " VALUES ($entity_guid, '$name_id','$value_id','$value_type', $owner_guid, $time, $access_id)"; $id = insert_data($query); if ($id !== false) { - $obj = get_metadata($id); + $obj = elgg_get_metadata_from_id($id); if (elgg_trigger_event('create', 'metadata', $obj)) { + + elgg_get_metadata_cache()->save($entity_guid, $name, $value, $allow_multiple); + return $id; } else { - delete_metadata($id); + elgg_delete_metadata_by_id($id); } } } @@ -182,7 +144,7 @@ function create_metadata($entity_guid, $name, $value, $value_type, $owner_guid, /** * Update a specific piece of metadata. * - * @param int $id Metadata id + * @param int $id ID of the metadata to update * @param string $name Metadata name * @param string $value Metadata value * @param string $value_type Value type @@ -196,7 +158,7 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i $id = (int)$id; - if (!$md = get_metadata($id)) { + if (!$md = elgg_get_metadata_from_id($id)) { return false; } if (!$md->canEdit()) { @@ -210,6 +172,7 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i } if ($metabyname_memcache) { + // @todo fix memcache (name_id is not a property of ElggMetadata) $metabyname_memcache->delete("{$md->entity_guid}:{$md->name_id}"); } @@ -217,46 +180,42 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i $owner_guid = (int)$owner_guid; if ($owner_guid == 0) { - $owner_guid = get_loggedin_userid(); + $owner_guid = elgg_get_logged_in_user_guid(); } $access_id = (int)$access_id; - $access = get_access_sql_suffix(); - // Support boolean types (as integers) if (is_bool($value)) { - if ($value) { - $value = 1; - } else { - $value = 0; - } + $value = (int) $value; } // Add the metastring - $value = add_metastring($value); - if (!$value) { + $value_id = add_metastring($value); + if (!$value_id) { return false; } - $name = add_metastring($name); - if (!$name) { + $name_id = add_metastring($name); + if (!$name_id) { return false; } // If ok then add it $query = "UPDATE {$CONFIG->dbprefix}metadata" - . " set value_id='$value', value_type='$value_type', access_id=$access_id," - . " owner_guid=$owner_guid where id=$id and name_id='$name'"; + . " set name_id='$name_id', value_id='$value_id', value_type='$value_type', access_id=$access_id," + . " owner_guid=$owner_guid where id=$id"; $result = update_data($query); if ($result !== false) { - $obj = get_metadata($id); - if (elgg_trigger_event('update', 'metadata', $obj)) { - return true; - } else { - delete_metadata($id); - } + + elgg_get_metadata_cache()->save($md->entity_guid, $name, $value); + + // @todo this event tells you the metadata has been updated, but does not + // let you do anything about it. What is needed is a plugin hook before + // the update that passes old and new values. + $obj = elgg_get_metadata_from_id($id); + elgg_trigger_event('update', 'metadata', $obj); } return $result; @@ -270,7 +229,7 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i * associative arrays and there is no guarantee on the ordering in the array. * * @param int $entity_guid The entity to attach the metadata to - * @param string $name_and_values Associative array - a value can be a string, number, bool + * @param array $name_and_values Associative array - a value can be a string, number, bool * @param string $value_type 'text', 'integer', or '' for automatic detection * @param int $owner_guid GUID of entity that owns the metadata * @param int $access_id Default is ACCESS_PRIVATE @@ -292,222 +251,148 @@ $access_id = ACCESS_PRIVATE, $allow_multiple = false) { } /** - * Delete a piece of metadata, where the current user has access. + * Returns metadata. Accepts all elgg_get_entities() options for entity + * restraints. + * + * @see elgg_get_entities * - * @param int $id The id of metadata to delete. + * @warning 1.7's find_metadata() didn't support limits and returned all metadata. + * This function defaults to a limit of 25. There is probably not a reason + * for you to return all metadata unless you're exporting an entity, + * have other restraints in place, or are doing something horribly + * wrong in your code. * - * @return bool + * @param array $options Array in format: + * + * metadata_names => NULL|ARR metadata names + * metadata_values => NULL|ARR metadata values + * metadata_ids => NULL|ARR metadata ids + * metadata_case_sensitive => BOOL Overall Case sensitive + * metadata_owner_guids => NULL|ARR guids for metadata owners + * metadata_created_time_lower => INT Lower limit for created time. + * metadata_created_time_upper => INT Upper limit for created time. + * metadata_calculation => STR Perform the MySQL function on the metadata values returned. + * The "metadata_calculation" option causes this function to + * return the result of performing a mathematical calculation on + * all metadata that match the query instead of returning + * ElggMetadata objects. + * + * @return ElggMetadata[]|mixed + * @since 1.8.0 */ -function delete_metadata($id) { - global $CONFIG; - - $id = (int)$id; - $metadata = get_metadata($id); - - if ($metadata) { - // Tidy up if memcache is enabled. - static $metabyname_memcache; - if ((!$metabyname_memcache) && (is_memcache_available())) { - $metabyname_memcache = new ElggMemcache('metabyname_memcache'); - } - - if ($metabyname_memcache) { - $metabyname_memcache->delete("{$metadata->entity_guid}:{$metadata->name_id}"); - } +function elgg_get_metadata(array $options = array()) { - if (($metadata->canEdit()) && (elgg_trigger_event('delete', 'metadata', $metadata))) { - return delete_data("DELETE from {$CONFIG->dbprefix}metadata where id=$id"); - } + // @todo remove support for count shortcut - see #4393 + // support shortcut of 'count' => true for 'metadata_calculation' => 'count' + if (isset($options['count']) && $options['count']) { + $options['metadata_calculation'] = 'count'; + unset($options['count']); } - return false; + $options['metastring_type'] = 'metadata'; + return elgg_get_metastring_based_objects($options); } /** - * Get metadata objects by name. + * Deletes metadata based on $options. * - * @param int $entity_guid Entity GUID - * @param string $meta_name Metadata name + * @warning Unlike elgg_get_metadata() this will not accept an empty options array! + * This requires at least one constraint: metadata_owner_guid(s), + * metadata_name(s), metadata_value(s), or guid(s) must be set. * - * @return mixed Either an ElggMetadata object, an array of ElggMetadata or false. + * @param array $options An options array. {@see elgg_get_metadata()} + * @return bool|null true on success, false on failure, null if no metadata to delete. + * @since 1.8.0 */ -function get_metadata_byname($entity_guid, $meta_name) { - global $CONFIG; - - $meta_name = get_metastring_id($meta_name); - - if (empty($meta_name)) { - return false; - } - - $entity_guid = (int)$entity_guid; - $access = get_access_sql_suffix("e"); - $md_access = get_access_sql_suffix("m"); - - // If memcache is available then cache this (cache only by name for now - // since this is the most common query) - $meta = null; - static $metabyname_memcache; - if ((!$metabyname_memcache) && (is_memcache_available())) { - $metabyname_memcache = new ElggMemcache('metabyname_memcache'); - } - if ($metabyname_memcache) { - $meta = $metabyname_memcache->load("{$entity_guid}:{$meta_name}"); - } - if ($meta) { - return $meta; - } - - $query = "SELECT m.*, n.string as name, v.string as value" - . " from {$CONFIG->dbprefix}metadata m" - . " JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.entity_guid" - . " JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id" - . " JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id" - . " where m.entity_guid=$entity_guid and m.name_id='$meta_name'" - . " and $access and $md_access ORDER BY m.id ASC" ; - - $result = get_data($query, "row_to_elggmetadata"); - - if (!$result) { +function elgg_delete_metadata(array $options) { + if (!elgg_is_valid_options_for_batch_operation($options, 'metadata')) { return false; } + $options['metastring_type'] = 'metadata'; + $result = elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false); - // Cache if memcache available - if ($metabyname_memcache) { - if (count($result) == 1) { - $r = $result[0]; - } else { - $r = $result; - } - // This is a bit of a hack - we shorten the expiry on object - // metadata so that it'll be gone in an hour. This means that - // deletions and more importantly updates will filter through eventually. - $metabyname_memcache->setDefaultExpiry(3600); - $metabyname_memcache->save("{$entity_guid}:{$meta_name}", $r); - } - if (count($result) == 1) { - return $result[0]; - } + // This moved last in case an object's constructor sets metadata. Currently the batch + // delete process has to create the entity to delete its metadata. See #5214 + elgg_get_metadata_cache()->invalidateByOptions('delete', $options); return $result; } /** - * Return all the metadata for a given GUID. + * Disables metadata based on $options. * - * @param int $entity_guid Entity GUID + * @warning Unlike elgg_get_metadata() this will not accept an empty options array! * - * @return mixed + * @param array $options An options array. {@See elgg_get_metadata()} + * @return bool|null true on success, false on failure, null if no metadata disabled. + * @since 1.8.0 */ -function get_metadata_for_entity($entity_guid) { - global $CONFIG; - - $entity_guid = (int)$entity_guid; - $access = get_access_sql_suffix("e"); - $md_access = get_access_sql_suffix("m"); +function elgg_disable_metadata(array $options) { + if (!elgg_is_valid_options_for_batch_operation($options, 'metadata')) { + return false; + } - $query = "SELECT m.*, n.string as name, v.string as value - from {$CONFIG->dbprefix}metadata m - JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.entity_guid - JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id - JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id - where m.entity_guid=$entity_guid and $access and $md_access"; + elgg_get_metadata_cache()->invalidateByOptions('disable', $options); + + // if we can see hidden (disabled) we need to use the offset + // otherwise we risk an infinite loop if there are more than 50 + $inc_offset = access_get_show_hidden_status(); - return get_data($query, "row_to_elggmetadata"); + $options['metastring_type'] = 'metadata'; + return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset); } /** - * Get the metadata where the entities they are referring to match a given criteria. + * Enables metadata based on $options. * - * @param mixed $meta_name Metadata name - * @param mixed $meta_value Metadata value - * @param string $entity_type The type of entity to look for, eg 'site' or 'object' - * @param string $entity_subtype The subtype of the entity. - * @param int $limit Limit - * @param int $offset Offset - * @param string $order_by Optional ordering. - * @param int $site_guid Site GUID. 0 for current, -1 for any + * @warning Unlike elgg_get_metadata() this will not accept an empty options array! * - * @return mixed + * @warning In order to enable metadata, you must first use + * {@link access_show_hidden_entities()}. + * + * @param array $options An options array. {@See elgg_get_metadata()} + * @return bool|null true on success, false on failure, null if no metadata enabled. + * @since 1.8.0 */ -function find_metadata($meta_name = "", $meta_value = "", $entity_type = "", $entity_subtype = "", - $limit = 10, $offset = 0, $order_by = "", $site_guid = 0) { - global $CONFIG; - - $meta_n = get_metastring_id($meta_name); - $meta_v = get_metastring_id($meta_value); - - $entity_type = sanitise_string($entity_type); - $entity_subtype = get_subtype_id($entity_type, $entity_subtype); - $limit = (int)$limit; - $offset = (int)$offset; - if ($order_by == "") { - $order_by = "e.time_created desc"; - } - - $order_by = sanitise_string($order_by); - $site_guid = (int) $site_guid; - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; - } - - $where = array(); - - if ($entity_type != "") { - $where[] = "e.type='$entity_type'"; - } - - if ($entity_subtype) { - $where[] = "e.subtype=$entity_subtype"; - } - - if ($meta_name != "") { - if (!$meta_v) { - // The value is set, but we didn't get a value... so something went wrong. - return false; - } - $where[] = "m.name_id='$meta_n'"; - } - if ($meta_value != "") { - // The value is set, but we didn't get a value... so something went wrong. - if (!$meta_v) { - return false; - } - $where[] = "m.value_id='$meta_v'"; - } - if ($site_guid > 0) { - $where[] = "e.site_guid = {$site_guid}"; +function elgg_enable_metadata(array $options) { + if (!$options || !is_array($options)) { + return false; } - $query = "SELECT m.*, n.string as name, v.string as value from {$CONFIG->dbprefix}entities e - JOIN {$CONFIG->dbprefix}metadata m on e.guid = m.entity_guid - JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id - JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id where"; - - foreach ($where as $w) { - $query .= " $w and "; - } - $query .= get_access_sql_suffix("e"); // Add access controls - $query .= ' and ' . get_access_sql_suffix("m"); // Add access controls - $query .= " order by $order_by limit $offset, $limit"; // Add order and limit + elgg_get_metadata_cache()->invalidateByOptions('enable', $options); - return get_data($query, "row_to_elggmetadata"); + $options['metastring_type'] = 'metadata'; + return elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback'); } /** + * ElggEntities interfaces + */ + +/** * Returns entities based upon metadata. Also accepts all * options available to elgg_get_entities(). Supports * the singular option shortcut. * - * NB: Using metadata_names and metadata_values results in a + * @note Using metadata_names and metadata_values results in a * "names IN (...) AND values IN (...)" clause. This is subtly * differently than default multiple metadata_name_value_pairs, which use * "(name = value) AND (name = value)" clauses. * * When in doubt, use name_value_pairs. * + * To ask for entities that do not have a metadata value, use a custom + * where clause like this: + * + * $options['wheres'][] = "NOT EXISTS ( + * SELECT 1 FROM {$dbprefix}metadata md + * WHERE md.entity_guid = e.guid + * AND md.name_id = $name_metastring_id + * AND md.value_id = $value_metastring_id)"; + * + * Note the metadata name and value has been denormalized in the above example. + * * @see elgg_get_entities - * @see elgg_get_entities_from_annotations * * @param array $options Array in format: * @@ -521,9 +406,11 @@ function find_metadata($meta_name = "", $meta_value = "", $entity_type = "", $en * 'operand' => '=', * 'case_sensitive' => TRUE * ) - * Currently if multiple values are sent via + * Currently if multiple values are sent via * an array (value => array('value1', 'value2') * the pair's operand will be forced to "IN". + * If passing "IN" as the operand and a string as the value, + * the value must be a properly quoted and escaped string. * * metadata_name_value_pairs_operator => NULL|STR The operator to use for combining * (name = value) OPERATOR (name = value); default AND @@ -539,20 +426,20 @@ function find_metadata($meta_name = "", $meta_value = "", $entity_type = "", $en * * metadata_owner_guids => NULL|ARR guids for metadata owners * - * @return array + * @return ElggEntity[]|mixed If count, int. If not count, array. false on errors. * @since 1.7.0 */ function elgg_get_entities_from_metadata(array $options = array()) { $defaults = array( - 'metadata_names' => ELGG_ENTITIES_ANY_VALUE, - 'metadata_values' => ELGG_ENTITIES_ANY_VALUE, - 'metadata_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, + 'metadata_names' => ELGG_ENTITIES_ANY_VALUE, + 'metadata_values' => ELGG_ENTITIES_ANY_VALUE, + 'metadata_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, - 'metadata_name_value_pairs_operator'=> 'AND', - 'metadata_case_sensitive' => TRUE, - 'order_by_metadata' => array(), + 'metadata_name_value_pairs_operator' => 'AND', + 'metadata_case_sensitive' => TRUE, + 'order_by_metadata' => array(), - 'metadata_owner_guids' => ELGG_ENTITIES_ANY_VALUE, + 'metadata_owner_guids' => ELGG_ENTITIES_ANY_VALUE, ); $options = array_merge($defaults, $options); @@ -570,66 +457,6 @@ function elgg_get_entities_from_metadata(array $options = array()) { } /** - * Returns options to pass to elgg_get_entities() for metastrings operations. - * - * @param string $type Metastring type: annotations or metadata - * @param array $options Options - * - * @return array - * @since 1.7.0 - */ -function elgg_entities_get_metastrings_options($type, $options) { - $valid_types = array('metadata', 'annotation'); - if (!in_array($type, $valid_types)) { - return FALSE; - } - - // the options for annotations are singular (annotation_name) but the table - // is plural (elgg_annotations) so rewrite for the table name. - $n_table = ($type == 'annotation') ? 'annotations' : $type; - - $singulars = array("{$type}_name", "{$type}_value", - "{$type}_name_value_pair", "{$type}_owner_guid"); - $options = elgg_normalise_plural_options_array($options, $singulars); - - $clauses = elgg_get_entity_metadata_where_sql('e', $n_table, $options["{$type}_names"], - $options["{$type}_values"], $options["{$type}_name_value_pairs"], - $options["{$type}_name_value_pairs_operator"], $options["{$type}_case_sensitive"], - $options["order_by_{$type}"], $options["{$type}_owner_guids"]); - - if ($clauses) { - // merge wheres to pass to get_entities() - if (isset($options['wheres']) && !is_array($options['wheres'])) { - $options['wheres'] = array($options['wheres']); - } elseif (!isset($options['wheres'])) { - $options['wheres'] = array(); - } - - $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']); - - // merge joins to pass to get_entities() - if (isset($options['joins']) && !is_array($options['joins'])) { - $options['joins'] = array($options['joins']); - } elseif (!isset($options['joins'])) { - $options['joins'] = array(); - } - - $options['joins'] = array_merge($options['joins'], $clauses['joins']); - - if ($clauses['orders']) { - $order_by_metadata = implode(", ", $clauses['orders']); - if (isset($options['order_by']) && $options['order_by']) { - $options['order_by'] = "$order_by_metadata, {$options['order_by']}"; - } else { - $options['order_by'] = "$order_by_metadata, e.time_created DESC"; - } - } - } - - return $options; -} - -/** * Returns metadata name and value SQL where for entities. * NB: $names and $values are not paired. Use $pairs for this. * Pairs default to '=' operand. @@ -637,19 +464,20 @@ function elgg_entities_get_metastrings_options($type, $options) { * This function is reused for annotations because the tables are * exactly the same. * - * @param string $e_table Entities table name - * @param string $n_table Normalized metastrings table name (Where entities, + * @param string $e_table Entities table name + * @param string $n_table Normalized metastrings table name (Where entities, * values, and names are joined. annotations / metadata) - * @param arr|null $names Array of names - * @param arr|null $values Array of values - * @param arr|null $pairs Array of names / values / operands - * @param and|or $pair_operator Operator to use to join the where clauses for pairs - * @param bool $case_sensitive Case sensitive metadata names? - * @param arr|null $order_by_metadata Array of names / direction - * @param arr|null $owner_guids Array of owner GUIDs - * - * @return FALSE|array False on fail, array('joins', 'wheres') + * @param array|null $names Array of names + * @param array|null $values Array of values + * @param array|null $pairs Array of names / values / operands + * @param string $pair_operator ("AND" or "OR") Operator to use to join the where clauses for pairs + * @param bool $case_sensitive Case sensitive metadata names? + * @param array|null $order_by_metadata Array of names / direction + * @param array|null $owner_guids Array of owner GUIDs + * + * @return false|array False on fail, array('joins', 'wheres') * @since 1.7.0 + * @access private */ function elgg_get_entity_metadata_where_sql($e_table, $n_table, $names = NULL, $values = NULL, $pairs = NULL, $pair_operator = 'AND', $case_sensitive = TRUE, $order_by_metadata = NULL, @@ -794,6 +622,8 @@ $owner_guids = NULL) { // if the operand is IN don't quote it because quoting should be done already. if (is_numeric($pair['value'])) { $value = sanitise_string($pair['value']); + } else if (is_bool($pair['value'])) { + $value = (int) $pair['value']; } else if (is_array($pair['value'])) { $values_array = array(); @@ -834,7 +664,7 @@ $owner_guids = NULL) { $i++; } - if ($where = implode (" $pair_operator ", $pair_wheres)) { + if ($where = implode(" $pair_operator ", $pair_wheres)) { $wheres[] = "($where)"; } } @@ -856,7 +686,7 @@ $owner_guids = NULL) { } if (is_array($order_by_metadata)) { - if ((count($order_by_metadata) > 0) && !is_array($order_by_metadata[0])) { + if ((count($order_by_metadata) > 0) && !isset($order_by_metadata[0])) { // singleton, so fix $order_by_metadata = array($order_by_metadata); } @@ -892,132 +722,6 @@ $owner_guids = NULL) { } /** - * Return a list of entities based on the given search criteria. - * - * @deprecated 1.7 use elgg_get_entities_from_metadata(). - * - * @param mixed $meta_name Metadat name - * @param mixed $meta_value Metadata value - * @param string $entity_type The type of entity to look for, eg 'site' or 'object' - * @param string $entity_subtype The subtype of the entity. - * @param int $owner_guid Owner GUID - * @param int $limit Limit - * @param int $offset Offset - * @param string $order_by Optional ordering. - * @param int $site_guid Site GUID. 0 for current, -1 for any. - * @param bool $count Return a count instead of entities - * @param bool $case_sensitive Metadata names case sensitivity - * - * @return int|array A list of entities, or a count if $count is set to true - */ -function get_entities_from_metadata($meta_name, $meta_value = "", $entity_type = "", -$entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", -$site_guid = 0, $count = FALSE, $case_sensitive = TRUE) { - - elgg_deprecated_notice('get_entities_from_metadata() was deprecated by elgg_get_entities_from_metadata()!', 1.7); - - $options = array(); - - $options['metadata_names'] = $meta_name; - - if ($meta_value) { - $options['metadata_values'] = $meta_value; - } - - if ($entity_type) { - $options['types'] = $entity_type; - } - - if ($entity_subtype) { - $options['subtypes'] = $entity_subtype; - } - - if ($owner_guid) { - if (is_array($owner_guid)) { - $options['owner_guids'] = $owner_guid; - } else { - $options['owner_guid'] = $owner_guid; - } - } - - if ($limit) { - $options['limit'] = $limit; - } - - if ($offset) { - $options['offset'] = $offset; - } - - if ($order_by) { - $options['order_by']; - } - - if ($site_guid) { - $options['site_guid']; - } - - if ($count) { - $options['count'] = $count; - } - - // need to be able to pass false - $options['metadata_case_sensitive'] = $case_sensitive; - - return elgg_get_entities_from_metadata($options); -} - -/** - * Return a list of entities suitable for display based on the given search criteria. - * - * @see elgg_view_entity_list - * - * @deprecated 1.8 Use elgg_list_entities_from_metadata - * - * @param mixed $meta_name Metadata name to search on - * @param mixed $meta_value The value to match, optionally - * @param string $entity_type The type of entity to look for, eg 'site' or 'object' - * @param string $entity_subtype The subtype of the entity - * @param int $owner_guid Owner GUID - * @param int $limit Number of entities to display per page - * @param bool $fullview WDisplay the full view (default: true) - * @param bool $listtypetoggle Allow users to toggle to the gallery view. Default: true - * @param bool $pagination Display pagination? Default: true - * @param bool $case_sensitive Case sensitive metadata names? - * - * @return string - * - * @return string A list of entities suitable for display - */ -function list_entities_from_metadata($meta_name, $meta_value = "", -$entity_type = ELGG_ENTITIES_ANY_VALUE, $entity_subtype = ELGG_ENTITIES_ANY_VALUE, -$owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, -$pagination = true, $case_sensitive = true) { - - elgg_deprecated_notice('list_entities_from_metadata() was deprecated by elgg_list_entities_from_metadata()!', 1.8); - - $offset = (int) get_input('offset'); - $limit = (int) $limit; - $options = array( - 'metadata_name' => $meta_name, - 'metadata_value' => $meta_value, - 'types' => $entity_type, - 'subtypes' => $entity_subtype, - 'owner_guid' => $owner_guid, - 'limit' => $limit, - 'offset' => $offset, - 'count' => TRUE, - 'metadata_case_sensitive' => $case_sensitive - ); - $count = elgg_get_entities_from_metadata($options); - - $options['count'] = FALSE; - $entities = elgg_get_entities_from_metadata($options); - - return elgg_view_entity_list($entities, $count, $offset, $limit, - $fullview, $listtypetoggle, $pagination); -} - -/** * Returns a list of entities filtered by provided metadata. * * @see elgg_get_entities_from_metadata @@ -1032,160 +736,8 @@ function elgg_list_entities_from_metadata($options) { } /** - * Return entities from metadata - * - * @deprecated 1.7. Use elgg_get_entities_from_metadata(). - * - * @param mixed $meta_array Metadata name - * @param string $entity_type The type of entity to look for, eg 'site' or 'object' - * @param string $entity_subtype The subtype of the entity. - * @param int $owner_guid Owner GUID - * @param int $limit Limit - * @param int $offset Offset - * @param string $order_by Optional ordering. - * @param int $site_guid Site GUID. 0 for current, -1 for any. - * @param bool $count Return a count instead of entities - * @param bool $meta_array_operator Operator for metadata values - * - * @return int|array A list of entities, or a count if $count is set to true + * Other functions */ -function get_entities_from_metadata_multi($meta_array, $entity_type = "", $entity_subtype = "", -$owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, -$count = false, $meta_array_operator = 'and') { - - elgg_deprecated_notice('get_entities_from_metadata_multi() was deprecated by elgg_get_entities_from_metadata()!', 1.7); - - if (!is_array($meta_array) || sizeof($meta_array) == 0) { - return false; - } - - $options = array(); - - $options['metadata_name_value_pairs'] = $meta_array; - - if ($entity_type) { - $options['types'] = $entity_type; - } - - if ($entity_subtype) { - $options['subtypes'] = $entity_subtype; - } - - if ($owner_guid) { - if (is_array($owner_guid)) { - $options['owner_guids'] = $owner_guid; - } else { - $options['owner_guid'] = $owner_guid; - } - } - - if ($limit) { - $options['limit'] = $limit; - } - - if ($offset) { - $options['offset'] = $offset; - } - - if ($order_by) { - $options['order_by']; - } - - if ($site_guid) { - $options['site_guid']; - } - - if ($count) { - $options['count'] = $count; - } - - $options['metadata_name_value_pairs_operator'] = $meta_array_operator; - - return elgg_get_entities_from_metadata($options); -} - -/** - * Returns a viewable list of entities based on the given search criteria. - * - * @see elgg_view_entity_list - * - * @param array $meta_array Array of 'name' => 'value' pairs - * @param string $entity_type The type of entity to look for, eg 'site' or 'object' - * @param string $entity_subtype The subtype of the entity. - * @param int $owner_guid Owner GUID - * @param int $limit Limit - * @param bool $fullview WDisplay the full view (default: true) - * @param bool $listtypetoggle Allow users to toggle to the gallery view. Default: true - * @param bool $pagination Display pagination? Default: true - * - * @return string List of ElggEntities suitable for display - * - * @deprecated Use elgg_list_entities_from_metadata() instead - */ -function list_entities_from_metadata_multi($meta_array, $entity_type = "", $entity_subtype = "", -$owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true) { - elgg_deprecated_notice(elgg_echo('deprecated:function', array( - 'list_entities_from_metadata_multi', - 'elgg_get_entities_from_metadata' - )), 1.8); - - - $offset = (int) get_input('offset'); - $limit = (int) $limit; - $count = get_entities_from_metadata_multi($meta_array, $entity_type, $entity_subtype, - $owner_guid, $limit, $offset, "", $site_guid, true); - $entities = get_entities_from_metadata_multi($meta_array, $entity_type, $entity_subtype, - $owner_guid, $limit, $offset, "", $site_guid, false); - - return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, - $listtypetoggle, $pagination); -} - -/** - * Clear all the metadata for a given entity, assuming you have access to that metadata. - * - * @param int $entity_guid Entity GUID - * - * @return bool - */ -function clear_metadata($entity_guid) { - global $CONFIG; - - $entity_guid = (int)$entity_guid; - if ($entity = get_entity($entity_guid)) { - if ($entity->canEdit()) { - return delete_data("DELETE from {$CONFIG->dbprefix}metadata where entity_guid={$entity_guid}"); - } - } - return false; -} - -/** - * Clear all annotations belonging to a given owner_guid - * - * @param int $owner_guid The owner - * - * @return bool - */ -function clear_metadata_by_owner($owner_guid) { - global $CONFIG; - - $owner_guid = (int)$owner_guid; - - $metas = get_data("SELECT id from {$CONFIG->dbprefix}metadata WHERE owner_guid=$owner_guid"); - $deleted = 0; - - if (is_array($metas)) { - foreach ($metas as $id) { - // Is this the best way? - if (delete_metadata($id->id)) { - $deleted++; - } - } - } - - return $deleted; -} /** * Handler called by trigger_plugin_hook on the "export" event. @@ -1196,6 +748,9 @@ function clear_metadata_by_owner($owner_guid) { * @param mixed $params Params * * @return array + * @access private + * + * @throws InvalidParameterException */ function export_metadata_plugin_hook($hook, $entity_type, $returnvalue, $params) { // Sanity check values @@ -1207,12 +762,13 @@ function export_metadata_plugin_hook($hook, $entity_type, $returnvalue, $params) throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonArrayReturnValue')); } - $guid = (int)$params['guid']; - $name = $params['name']; - - $result = get_metadata_for_entity($guid); + $result = elgg_get_metadata(array( + 'guid' => (int)$params['guid'], + 'limit' => 0, + )); if ($result) { + /* @var ElggMetadata[] $result */ foreach ($result as $r) { $returnvalue[] = $r->export(); } @@ -1223,7 +779,7 @@ function export_metadata_plugin_hook($hook, $entity_type, $returnvalue, $params) /** * Takes in a comma-separated string and returns an array of tags - * which have been trimmed and set to lower case + * which have been trimmed * * @param string $string Comma-separated tag string * @@ -1232,17 +788,12 @@ function export_metadata_plugin_hook($hook, $entity_type, $returnvalue, $params) function string_to_tag_array($string) { if (is_string($string)) { $ar = explode(",", $string); - // trim blank spaces $ar = array_map('trim', $ar); - // make lower case : [Marcus Povey 20090605 - Using mb wrapper function - // using UTF8 safe function where available] - $ar = array_map('elgg_strtolower', $ar); - // Remove null values $ar = array_filter($ar, 'is_not_null'); + $ar = array_map('strip_tags', $ar); return $ar; } return false; - } /** @@ -1277,7 +828,7 @@ function metadata_array_to_values($array) { function get_metadata_url($id) { $id = (int)$id; - if ($extender = get_metadata($id)) { + if ($extender = elgg_get_metadata_from_id($id)) { return get_extender_url($extender); } return false; @@ -1333,10 +884,10 @@ function is_metadata_independent($type, $subtype) { function metadata_update($event, $object_type, $object) { if ($object instanceof ElggEntity) { if (!is_metadata_independent($object->getType(), $object->getSubtype())) { - global $CONFIG; + $db_prefix = elgg_get_config('dbprefix'); $access_id = (int) $object->access_id; $guid = (int) $object->getGUID(); - $query = "update {$CONFIG->dbprefix}metadata set access_id = {$access_id} where entity_guid = {$guid}"; + $query = "update {$db_prefix}metadata set access_id = {$access_id} where entity_guid = {$guid}"; update_data($query); } } @@ -1346,13 +897,57 @@ function metadata_update($event, $object_type, $object) { /** * Register a metadata url handler. * - * @param string $function_name The function. * @param string $extender_name The name, default 'all'. + * @param string $function The function name. * * @return bool */ -function register_metadata_url_handler($function_name, $extender_name = "all") { - return register_extender_url_handler($function_name, 'metadata', $extender_name); +function elgg_register_metadata_url_handler($extender_name, $function) { + return elgg_register_extender_url_handler('metadata', $extender_name, $function); +} + +/** + * Get the global metadata cache instance + * + * @return ElggVolatileMetadataCache + * + * @access private + */ +function elgg_get_metadata_cache() { + global $CONFIG; + if (empty($CONFIG->local_metadata_cache)) { + $CONFIG->local_metadata_cache = new ElggVolatileMetadataCache(); + } + return $CONFIG->local_metadata_cache; +} + +/** + * Invalidate the metadata cache based on options passed to various *_metadata functions + * + * @param string $action Action performed on metadata. "delete", "disable", or "enable" + * @param array $options Options passed to elgg_(delete|disable|enable)_metadata + * @return void + */ +function elgg_invalidate_metadata_cache($action, array $options) { + // remove as little as possible, optimizing for common cases + $cache = elgg_get_metadata_cache(); + if (empty($options['guid'])) { + // safest to clear everything unless we want to make this even more complex :( + $cache->flush(); + } else { + if (empty($options['metadata_name'])) { + // safest to clear the whole entity + $cache->clear($options['guid']); + } else { + switch ($action) { + case 'delete': + $cache->markEmpty($options['guid'], $options['metadata_name']); + break; + default: + $cache->markUnknown($options['guid'], $options['metadata_name']); + } + } + } } /** Register the hook */ @@ -1373,9 +968,11 @@ elgg_register_plugin_hook_handler('unit_test', 'system', 'metadata_test'); * @param mixed $params Params * * @return array + * @access private */ function metadata_test($hook, $type, $value, $params) { global $CONFIG; - $value[] = $CONFIG->path . 'engine/tests/objects/metadata.php'; + $value[] = $CONFIG->path . 'engine/tests/api/metadata.php'; + $value[] = $CONFIG->path . 'engine/tests/api/metadata_cache.php'; return $value; } diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php index 8a105b822..57d876c06 100644 --- a/engine/lib/metastrings.php +++ b/engine/lib/metastrings.php @@ -8,11 +8,15 @@ */ /** Cache metastrings for a page */ +global $METASTRINGS_CACHE; $METASTRINGS_CACHE = array(); /** Keep a record of strings we know don't exist */ +global $METASTRINGS_DEADNAME_CACHE; $METASTRINGS_DEADNAME_CACHE = array(); + + /** * Return the meta string id for a given tag, or false. * @@ -63,7 +67,7 @@ function get_metastring_id($string, $case_sensitive = TRUE) { } $row = FALSE; - $metaStrings = get_data($query, "entity_row_to_elggstar"); + $metaStrings = get_data($query); if (is_array($metaStrings)) { if (sizeof($metaStrings) > 1) { $ids = array(); @@ -71,7 +75,7 @@ function get_metastring_id($string, $case_sensitive = TRUE) { $ids[] = $metaString->id; } return $ids; - } else { + } else if (isset($metaStrings[0])) { $row = $metaStrings[0]; } } @@ -157,6 +161,7 @@ function add_metastring($string, $case_sensitive = true) { * Delete any orphaned entries in metastrings. This is run by the garbage collector. * * @return bool + * @access private */ function delete_orphaned_metastrings() { global $CONFIG; @@ -197,4 +202,702 @@ function delete_orphaned_metastrings() { )"; return delete_data($query); -}
\ No newline at end of file +} + +/** + * Returns an array of either ElggAnnotation or ElggMetadata objects. + * Accepts all elgg_get_entities() options for entity restraints. + * + * @see elgg_get_entities + * + * @param array $options Array in format: + * + * metastring_names => NULL|ARR metastring names + * + * metastring_values => NULL|ARR metastring values + * + * metastring_ids => NULL|ARR metastring ids + * + * metastring_case_sensitive => BOOL Overall Case sensitive + * + * metastring_owner_guids => NULL|ARR Guids for metadata owners + * + * metastring_created_time_lower => INT Lower limit for created time. + * + * metastring_created_time_upper => INT Upper limit for created time. + * + * metastring_calculation => STR Perform the MySQL function on the metastring values + * returned. + * This differs from egef_annotation_calculation in that + * it returns only the calculation of all annotation values. + * You can sum, avg, count, etc. egef_annotation_calculation() + * returns ElggEntities ordered by a calculation on their + * annotation values. + * + * metastring_type => STR metadata or annotation(s) + * + * @return mixed + * @access private + */ +function elgg_get_metastring_based_objects($options) { + $options = elgg_normalize_metastrings_options($options); + + switch ($options['metastring_type']) { + case 'metadata': + $type = 'metadata'; + $callback = 'row_to_elggmetadata'; + break; + + case 'annotations': + case 'annotation': + $type = 'annotations'; + $callback = 'row_to_elggannotation'; + break; + + default: + return false; + } + + $defaults = array( + // entities + 'types' => ELGG_ENTITIES_ANY_VALUE, + 'subtypes' => ELGG_ENTITIES_ANY_VALUE, + 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, + + 'guids' => ELGG_ENTITIES_ANY_VALUE, + 'owner_guids' => ELGG_ENTITIES_ANY_VALUE, + 'container_guids' => ELGG_ENTITIES_ANY_VALUE, + 'site_guids' => get_config('site_guid'), + + 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE, + 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE, + 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE, + 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE, + + // options are normalized to the plural in case we ever add support for them. + 'metastring_names' => ELGG_ENTITIES_ANY_VALUE, + 'metastring_values' => ELGG_ENTITIES_ANY_VALUE, + //'metastring_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, + //'metastring_name_value_pairs_operator' => 'AND', + + 'metastring_case_sensitive' => TRUE, + //'order_by_metastring' => array(), + 'metastring_calculation' => ELGG_ENTITIES_NO_VALUE, + + 'metastring_created_time_lower' => ELGG_ENTITIES_ANY_VALUE, + 'metastring_created_time_upper' => ELGG_ENTITIES_ANY_VALUE, + + 'metastring_owner_guids' => ELGG_ENTITIES_ANY_VALUE, + + 'metastring_ids' => ELGG_ENTITIES_ANY_VALUE, + + // sql + 'order_by' => 'n_table.time_created asc', + 'limit' => 10, + 'offset' => 0, + 'count' => FALSE, + 'selects' => array(), + 'wheres' => array(), + 'joins' => array(), + + 'callback' => $callback + ); + + // @todo Ignore site_guid right now because of #2910 + $options['site_guid'] = ELGG_ENTITIES_ANY_VALUE; + + $options = array_merge($defaults, $options); + + // can't use helper function with type_subtype_pair because + // it's already an array...just need to merge it + if (isset($options['type_subtype_pair'])) { + if (isset($options['type_subtype_pairs'])) { + $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'], + $options['type_subtype_pair']); + } else { + $options['type_subtype_pairs'] = $options['type_subtype_pair']; + } + } + + $singulars = array( + 'type', 'subtype', 'type_subtype_pair', + 'guid', 'owner_guid', 'container_guid', 'site_guid', + 'metastring_name', 'metastring_value', + 'metastring_owner_guid', 'metastring_id', + 'select', 'where', 'join' + ); + + $options = elgg_normalise_plural_options_array($options, $singulars); + + if (!$options) { + return false; + } + + $db_prefix = elgg_get_config('dbprefix'); + + // evaluate where clauses + if (!is_array($options['wheres'])) { + $options['wheres'] = array($options['wheres']); + } + + $wheres = $options['wheres']; + + // entities + $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'], + $options['subtypes'], $options['type_subtype_pairs']); + + $wheres[] = elgg_get_guid_based_where_sql('e.guid', $options['guids']); + $wheres[] = elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']); + $wheres[] = elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']); + $wheres[] = elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']); + + $wheres[] = elgg_get_entity_time_where_sql('e', $options['created_time_upper'], + $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']); + + + $wheres[] = elgg_get_entity_time_where_sql('n_table', $options['metastring_created_time_upper'], + $options['metastring_created_time_lower'], null, null); + + $wheres[] = elgg_get_guid_based_where_sql('n_table.owner_guid', + $options['metastring_owner_guids']); + + // see if any functions failed + // remove empty strings on successful functions + foreach ($wheres as $i => $where) { + if ($where === FALSE) { + return FALSE; + } elseif (empty($where)) { + unset($wheres[$i]); + } + } + + // remove identical where clauses + $wheres = array_unique($wheres); + + // evaluate join clauses + if (!is_array($options['joins'])) { + $options['joins'] = array($options['joins']); + } + + $joins = $options['joins']; + $joins[] = "JOIN {$db_prefix}entities e ON n_table.entity_guid = e.guid"; + + // evaluate selects + if (!is_array($options['selects'])) { + $options['selects'] = array($options['selects']); + } + + $selects = $options['selects']; + + // For performance reasons we don't want the joins required for metadata / annotations + // unless we're going through one of their callbacks. + // this means we expect the functions passing different callbacks to pass their required joins. + // If we're doing a calculation + $custom_callback = ($options['callback'] == 'row_to_elggmetadata' + || $options['callback'] == 'row_to_elggannotation'); + $is_calculation = $options['metastring_calculation'] ? true : false; + + if ($custom_callback || $is_calculation) { + $joins[] = "JOIN {$db_prefix}metastrings n on n_table.name_id = n.id"; + $joins[] = "JOIN {$db_prefix}metastrings v on n_table.value_id = v.id"; + + $selects[] = 'n.string as name'; + $selects[] = 'v.string as value'; + } + + foreach ($joins as $i => $join) { + if ($join === FALSE) { + return FALSE; + } elseif (empty($join)) { + unset($joins[$i]); + } + } + + // metastrings + $metastring_clauses = elgg_get_metastring_sql('n_table', $options['metastring_names'], + $options['metastring_values'], null, $options['metastring_ids'], + $options['metastring_case_sensitive']); + + if ($metastring_clauses) { + $wheres = array_merge($wheres, $metastring_clauses['wheres']); + $joins = array_merge($joins, $metastring_clauses['joins']); + } else { + $wheres[] = get_access_sql_suffix('n_table'); + } + + if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) { + $selects = array_unique($selects); + // evalutate selects + $select_str = ''; + if ($selects) { + foreach ($selects as $select) { + $select_str .= ", $select"; + } + } + + $query = "SELECT DISTINCT n_table.*{$select_str} FROM {$db_prefix}$type n_table"; + } elseif ($options['count']) { + // count is over the entities + $query = "SELECT count(DISTINCT e.guid) as calculation FROM {$db_prefix}$type n_table"; + } else { + $query = "SELECT {$options['metastring_calculation']}(v.string) as calculation FROM {$db_prefix}$type n_table"; + } + + // remove identical join clauses + $joins = array_unique($joins); + + // add joins + foreach ($joins as $j) { + $query .= " $j "; + } + + // add wheres + $query .= ' WHERE '; + + foreach ($wheres as $w) { + $query .= " $w AND "; + } + + // Add access controls + $query .= get_access_sql_suffix('e'); + + // reverse order by + if (isset($options['reverse_order_by']) && $options['reverse_order_by']) { + $options['order_by'] = elgg_sql_reverse_order_by_clause($options['order_by'], + $defaults['order_by']); + } + + if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) { + if (isset($options['group_by'])) { + $options['group_by'] = sanitise_string($options['group_by']); + $query .= " GROUP BY {$options['group_by']}"; + } + + if (isset($options['order_by']) && $options['order_by']) { + $options['order_by'] = sanitise_string($options['order_by']); + $query .= " ORDER BY {$options['order_by']}, n_table.id"; + } + + if ($options['limit']) { + $limit = sanitise_int($options['limit']); + $offset = sanitise_int($options['offset'], false); + $query .= " LIMIT $offset, $limit"; + } + + $dt = get_data($query, $options['callback']); + return $dt; + } else { + $result = get_data_row($query); + return $result->calculation; + } +} + +/** + * Returns an array of joins and wheres for use in metastrings. + * + * @note The $pairs is reserved for name/value pairs if we want to implement those. + * + * @param string $table The annotation or metadata table name or alias + * @param array $names An array of names + * @param array $values An array of values + * @param array $pairs Name / value pairs. Not currently used. + * @param array $ids Metastring IDs + * @param bool $case_sensitive Should name and values be case sensitive? + * + * @return array + * @access private + */ +function elgg_get_metastring_sql($table, $names = null, $values = null, + $pairs = null, $ids = null, $case_sensitive = false) { + + if ((!$names && $names !== 0) + && (!$values && $values !== 0) + && !$ids + && (!$pairs && $pairs !== 0)) { + + return array(); + } + + $db_prefix = elgg_get_config('dbprefix'); + + // binary forces byte-to-byte comparision of strings, making + // it case- and diacritical-mark- sensitive. + // only supported on values. + $binary = ($case_sensitive) ? ' BINARY ' : ''; + + $return = array ( + 'joins' => array (), + 'wheres' => array() + ); + + $wheres = array(); + + // get names wheres and joins + $names_where = ''; + if ($names !== NULL) { + if (!is_array($names)) { + $names = array($names); + } + + $sanitised_names = array(); + foreach ($names as $name) { + // normalise to 0. + if (!$name) { + $name = '0'; + } + $sanitised_names[] = '\'' . sanitise_string($name) . '\''; + } + + if ($names_str = implode(',', $sanitised_names)) { + $return['joins'][] = "JOIN {$db_prefix}metastrings msn on $table.name_id = msn.id"; + $names_where = "(msn.string IN ($names_str))"; + } + } + + // get values wheres and joins + $values_where = ''; + if ($values !== NULL) { + if (!is_array($values)) { + $values = array($values); + } + + $sanitised_values = array(); + foreach ($values as $value) { + // normalize to 0 + if (!$value) { + $value = 0; + } + $sanitised_values[] = '\'' . sanitise_string($value) . '\''; + } + + if ($values_str = implode(',', $sanitised_values)) { + $return['joins'][] = "JOIN {$db_prefix}metastrings msv on $table.value_id = msv.id"; + $values_where = "({$binary}msv.string IN ($values_str))"; + } + } + + if ($ids !== NULL) { + if (!is_array($ids)) { + $ids = array($ids); + } + + $ids_str = implode(',', $ids); + + if ($ids_str) { + $wheres[] = "n_table.id IN ($ids_str)"; + } + } + + if ($names_where && $values_where) { + $wheres[] = "($names_where AND $values_where)"; + } elseif ($names_where) { + $wheres[] = $names_where; + } elseif ($values_where) { + $wheres[] = $values_where; + } + + $wheres[] = get_access_sql_suffix($table); + + if ($where = implode(' AND ', $wheres)) { + $return['wheres'][] = "($where)"; + } + + return $return; +} + +/** + * Normalizes metadata / annotation option names to their corresponding metastrings name. + * + * @param array $options An options array + * @since 1.8.0 + * @return array + * @access private + */ +function elgg_normalize_metastrings_options(array $options = array()) { + + // support either metastrings_type or metastring_type + // because I've made this mistake many times and hunting it down is a pain... + $type = elgg_extract('metastring_type', $options, null); + $type = elgg_extract('metastrings_type', $options, $type); + + $options['metastring_type'] = $type; + + // support annotation_ and annotations_ because they're way too easy to confuse + $prefixes = array('metadata_', 'annotation_', 'annotations_'); + + // map the metadata_* options to metastring_* options + $map = array( + 'names' => 'metastring_names', + 'values' => 'metastring_values', + 'case_sensitive' => 'metastring_case_sensitive', + 'owner_guids' => 'metastring_owner_guids', + 'created_time_lower' => 'metastring_created_time_lower', + 'created_time_upper' => 'metastring_created_time_upper', + 'calculation' => 'metastring_calculation', + 'ids' => 'metastring_ids' + ); + + foreach ($prefixes as $prefix) { + $singulars = array("{$prefix}name", "{$prefix}value", "{$prefix}owner_guid", "{$prefix}id"); + $options = elgg_normalise_plural_options_array($options, $singulars); + + foreach ($map as $specific => $normalized) { + $key = $prefix . $specific; + if (isset($options[$key])) { + $options[$normalized] = $options[$key]; + } + } + } + + return $options; +} + +/** + * Enables or disables a metastrings-based object by its id. + * + * @warning To enable disabled metastrings you must first use + * {@link access_show_hidden_entities()}. + * + * @param int $id The object's ID + * @param string $enabled Value to set to: yes or no + * @param string $type The type of table to use: metadata or annotations + * + * @return bool + * @throws InvalidParameterException + * @since 1.8.0 + * @access private + */ +function elgg_set_metastring_based_object_enabled_by_id($id, $enabled, $type) { + $id = (int)$id; + $db_prefix = elgg_get_config('dbprefix'); + + $object = elgg_get_metastring_based_object_from_id($id, $type); + + switch($type) { + case 'annotation': + case 'annotations': + $table = "{$db_prefix}annotations"; + break; + + case 'metadata': + $table = "{$db_prefix}metadata"; + break; + } + + if ($enabled === 'yes' || $enabled === 1 || $enabled === true) { + $enabled = 'yes'; + $event = 'enable'; + } elseif ($enabled === 'no' || $enabled === 0 || $enabled === false) { + $enabled = 'no'; + $event = 'disable'; + } else { + return false; + } + + $return = false; + + if ($object) { + // don't set it if it's already set. + if ($object->enabled == $enabled) { + $return = false; + } elseif ($object->canEdit() && (elgg_trigger_event($event, $type, $object))) { + $return = update_data("UPDATE $table SET enabled = '$enabled' where id = $id"); + } + } + + return $return; +} + +/** + * Runs metastrings-based objects found using $options through $callback + * + * @warning Unlike elgg_get_metastring_based_objects() this will not accept an + * empty options array! + * + * @warning This returns null on no ops. + * + * @param array $options An options array. {@See elgg_get_metastring_based_objects()} + * @param string $callback The callback to pass each result through + * @param bool $inc_offset Increment the offset? Pass false for callbacks that delete / disable + * + * @return bool|null true on success, false on failure, null if no objects are found. + * @since 1.8.0 + * @access private + */ +function elgg_batch_metastring_based_objects(array $options, $callback, $inc_offset = true) { + if (!$options || !is_array($options)) { + return false; + } + + $batch = new ElggBatch('elgg_get_metastring_based_objects', $options, $callback, 50, $inc_offset); + return $batch->callbackResult; +} + +/** + * Returns a singular metastring-based object by its ID. + * + * @param int $id The metastring-based object's ID + * @param string $type The type: annotation or metadata + * @return ElggMetadata|ElggAnnotation + * + * @since 1.8.0 + * @access private + */ +function elgg_get_metastring_based_object_from_id($id, $type) { + $id = (int)$id; + if (!$id) { + return false; + } + + $options = array( + 'metastring_type' => $type, + 'metastring_id' => $id + ); + + $obj = elgg_get_metastring_based_objects($options); + + if ($obj && count($obj) == 1) { + return $obj[0]; + } + + return false; +} + +/** + * Deletes a metastring-based object by its id + * + * @param int $id The object's ID + * @param string $type The object's metastring type: annotation or metadata + * @return bool + * + * @since 1.8.0 + * @access private + */ +function elgg_delete_metastring_based_object_by_id($id, $type) { + $id = (int)$id; + $db_prefix = elgg_get_config('dbprefix'); + + switch ($type) { + case 'annotation': + case 'annotations': + $type = 'annotations'; + break; + + case 'metadata': + $type = 'metadata'; + break; + + default: + return false; + } + + $obj = elgg_get_metastring_based_object_from_id($id, $type); + $table = $db_prefix . $type; + + if ($obj) { + // Tidy up if memcache is enabled. + // @todo only metadata is supported + if ($type == 'metadata') { + static $metabyname_memcache; + if ((!$metabyname_memcache) && (is_memcache_available())) { + $metabyname_memcache = new ElggMemcache('metabyname_memcache'); + } + + if ($metabyname_memcache) { + // @todo why name_id? is that even populated? + $metabyname_memcache->delete("{$obj->entity_guid}:{$obj->name_id}"); + } + } + + if (($obj->canEdit()) && (elgg_trigger_event('delete', $type, $obj))) { + return (bool)delete_data("DELETE from $table where id=$id"); + } + } + + return false; +} + +/** + * Entities interface helpers + */ + +/** + * Returns options to pass to elgg_get_entities() for metastrings operations. + * + * @param string $type Metastring type: annotations or metadata + * @param array $options Options + * + * @return array + * @since 1.7.0 + * @access private + */ +function elgg_entities_get_metastrings_options($type, $options) { + $valid_types = array('metadata', 'annotation'); + if (!in_array($type, $valid_types)) { + return FALSE; + } + + // the options for annotations are singular (annotation_name) but the table + // is plural (elgg_annotations) so rewrite for the table name. + $n_table = ($type == 'annotation') ? 'annotations' : $type; + + $singulars = array("{$type}_name", "{$type}_value", + "{$type}_name_value_pair", "{$type}_owner_guid"); + $options = elgg_normalise_plural_options_array($options, $singulars); + + $clauses = elgg_get_entity_metadata_where_sql('e', $n_table, $options["{$type}_names"], + $options["{$type}_values"], $options["{$type}_name_value_pairs"], + $options["{$type}_name_value_pairs_operator"], $options["{$type}_case_sensitive"], + $options["order_by_{$type}"], $options["{$type}_owner_guids"]); + + if ($clauses) { + // merge wheres to pass to get_entities() + if (isset($options['wheres']) && !is_array($options['wheres'])) { + $options['wheres'] = array($options['wheres']); + } elseif (!isset($options['wheres'])) { + $options['wheres'] = array(); + } + + $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']); + + // merge joins to pass to get_entities() + if (isset($options['joins']) && !is_array($options['joins'])) { + $options['joins'] = array($options['joins']); + } elseif (!isset($options['joins'])) { + $options['joins'] = array(); + } + + $options['joins'] = array_merge($options['joins'], $clauses['joins']); + + if ($clauses['orders']) { + $order_by_metadata = implode(", ", $clauses['orders']); + if (isset($options['order_by']) && $options['order_by']) { + $options['order_by'] = "$order_by_metadata, {$options['order_by']}"; + } else { + $options['order_by'] = "$order_by_metadata, e.time_created DESC"; + } + } + } + + return $options; +} + +// unit testing +elgg_register_plugin_hook_handler('unit_test', 'system', 'metastrings_test'); + +/** + * Metadata unit test + * + * @param string $hook unit_test + * @param string $type system + * @param mixed $value Array of other tests + * @param mixed $params Params + * + * @return array + * @access private + */ +function metastrings_test($hook, $type, $value, $params) { + global $CONFIG; + $value[] = $CONFIG->path . 'engine/tests/api/metastrings.php'; + return $value; +} diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php index 75c5958f4..ab9cc05e8 100644 --- a/engine/lib/navigation.php +++ b/engine/lib/navigation.php @@ -3,504 +3,525 @@ * Elgg navigation library * Functions for managing menus and other navigational elements * + * Breadcrumbs + * Elgg uses a breadcrumb stack. The page handlers (controllers in MVC terms) + * push the breadcrumb links onto the stack. @see elgg_push_breadcrumb() + * + * + * Pagination + * Automatically handled by Elgg when using elgg_list_entities* functions. + * @see elgg_list_entities() + * + * + * Tabs + * @see navigation/tabs view + * + * + * Menus + * Elgg uses a single interface to manage its menus. Menu items are added with + * {@link elgg_register_menu_item()}. This is generally used for menus that + * appear only once per page. For dynamic menus (such as the hover + * menu for user's avatar), a plugin hook is emitted when the menu is being + * created. The hook is 'register', 'menu:<menu_name>'. For more details on this, + * @see elgg_view_menu(). + * + * Menus supported by the Elgg core + * Standard menus: + * site Site navigation shown on every page. + * page Page menu usually shown in a sidebar. Uses Elgg's context. + * topbar Topbar menu shown on every page. The default has two sections. + * footer Like the topbar but in the footer. + * extras Links about content on the page. The RSS link is added to this. + * + * Dynamic menus (also called just-in-time menus): + * user_hover Avatar hover menu. The user entity is passed as a parameter. + * entity The set of links shown in the summary of an entity. + * river Links shown on river items. + * owner_block Links shown for a user or group in their owner block. + * filter The tab filter for content (all, mine, friends) + * title The buttons shown next to a content title. + * long-text The links shown above the input/longtext view. + * * @package Elgg.Core * @subpackage Navigation */ /** - * Deprecated by elgg_add_submenu_item() - * - * @see elgg_add_submenu_item() - * @deprecated 1.8 - * - * @param string $label The label - * @param string $link The link - * @param string $group The group to store item in - * @param boolean $onclick Add a confirmation when clicked? - * @param boolean $selected Is menu item selected + * Register an item for an Elgg menu + * + * @warning Generally you should not use this in response to the plugin hook: + * 'register', 'menu:<menu_name>'. If you do, you may end up with many incorrect + * links on a dynamic menu. + * + * @warning A menu item's name must be unique per menu. If more than one menu + * item with the same name are registered, the last menu item takes priority. + * + * @see elgg_view_menu() for the plugin hooks available for modifying a menu as + * it is being rendered. + * + * @param string $menu_name The name of the menu: site, page, userhover, + * userprofile, groupprofile, or any custom menu + * @param mixed $menu_item A ElggMenuItem object or an array of options in format: + * name => STR Menu item identifier (required) + * text => STR Menu item display text (required) + * href => STR Menu item URL (required) (false for non-links. + * @warning If you disable the href the <a> tag will + * not appear, so the link_class will not apply. If you + * put <a> tags in manually through the 'text' option + * the default CSS selector .elgg-menu-$menu > li > a + * may affect formatting. Wrap in a <span> if it does.) + * contexts => ARR Page context strings + * section => STR Menu section identifier + * title => STR Menu item tooltip + * selected => BOOL Is this menu item currently selected + * parent_name => STR Identifier of the parent menu item + * link_class => STR A class or classes for the <a> tag + * item_class => STR A class or classes for the <li> tag + * + * Additional options that the view output/url takes can be + * passed in the array. If the 'confirm' key is passed, the + * menu link uses the 'output/confirmlink' view. Custom + * options can be added by using the 'data' key with the + * value being an associative array. * * @return bool + * @since 1.8.0 */ -function add_submenu_item($label, $link, $group = 'default', $onclick = false, $selected = NULL) { - elgg_deprecated_notice('add_submenu_item was deprecated by elgg_add_submenu_item', 1.8); - - $item = array( - 'text' => $label, - 'href' => $link, - 'selected' => $selected - ); +function elgg_register_menu_item($menu_name, $menu_item) { + global $CONFIG; - if (!$group) { - $group = 'default'; + if (!isset($CONFIG->menus[$menu_name])) { + $CONFIG->menus[$menu_name] = array(); } - if ($onclick) { - $js = "onclick=\"javascript:return confirm('" . elgg_echo('deleteconfirm') . "')\""; - $item['vars'] = array('js' => $js); - } - // submenu items were added in the page setup hook usually by checking - // the context. We'll pass in the current context here, which will - // emulate that effect. - // if context == 'main' (default) it probably means they always wanted - // the menu item to show up everywhere. - $context = elgg_get_context(); - - if ($context == 'main') { - $context = 'all'; + if (is_array($menu_item)) { + $item = ElggMenuItem::factory($menu_item); + if (!$item) { + elgg_log("Unable to add menu item '{$menu_item['name']}' to '$menu_name' menu", 'WARNING'); + elgg_log(print_r($menu_item, true), 'DEBUG'); + return false; + } + } else { + $item = $menu_item; } - return elgg_add_submenu_item($item, $context, $group); + + $CONFIG->menus[$menu_name][] = $item; + return true; } /** - * Add an entry to the submenu. - * - * @param array $item The item as: - * <code> - * array( - * 'title' => 'Text to display', - * 'url' => 'URL of the link', - * 'id' => 'entry_unique_id' //used by children items to identify parents - * 'parent_id' => 'id_of_parent', - * 'selected' => BOOL // Is this item selected? (If NULL or unset will attempt to guess) - * 'vars' => array() // Array of vars to pass to the navigation/submenu_item view - * ) - * </code> + * Remove an item from a menu * - * @param string $context Context in which to display this menu item. 'all' - * will make it show up all the time. Use sparingly. - * @param string $group Group for the item. Each submenu group has its own <ul> + * @param string $menu_name The name of the menu + * @param string $item_name The unique identifier for this menu item * - * @return BOOL - * @since 1.8 - * @see elgg_prepare_submenu + * @return bool + * @since 1.8.0 */ -function elgg_add_submenu_item(array $item, $context = 'all', $group = 'default') { +function elgg_unregister_menu_item($menu_name, $item_name) { global $CONFIG; - if (!isset($CONFIG->submenu_items)) { - $CONFIG->submenu_items = array(); - } - - if (!isset($CONFIG->submenu_items[$context])) { - $CONFIG->submenu_items[$context] = array(); - } - - if (!isset($CONFIG->submenu_items[$context][$group])) { - $CONFIG->submenu_items[$context][$group] = array(); + if (!isset($CONFIG->menus[$menu_name])) { + return false; } - if (!isset($item['text'])) { - return FALSE; - } - - if (!empty($item['href'])) { - $item['href'] = elgg_normalize_url($item['href']); - } - - // we use persistent object properties in the submenu - // setup function, so normalize the array to an object. - // we pass it in as an array because this would be the only - // place in elgg that we ask for an object like this. - // consistency ftw. - $item_obj = new StdClass(); - - foreach ($item as $k => $v) { - switch ($k) { - case 'parent_id': - case 'id': - // make sure '' and false make sense - $v = (empty($v)) ? NULL : $v; - - default: - $item_obj->$k = $v; - break; + foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) { + /* @var ElggMenuItem $menu_object */ + if ($menu_object->getName() == $item_name) { + unset($CONFIG->menus[$menu_name][$index]); + return true; } } - $CONFIG->submenu_items[$context][$group][] = $item_obj; - - return TRUE; + return false; } /** - * Properly nest all submenu entries for contexts $context and 'all' - * - * @param string $context Context for menus - * @param bool $sort Sort the menu items alphabetically + * Check if a menu item has been registered * - * @since 1.8 - * @see elgg_add_submenu_item - * - * @return true + * @param string $menu_name The name of the menu + * @param string $item_name The unique identifier for this menu item + * + * @return bool + * @since 1.8.0 */ -function elgg_prepare_submenu($context = 'main', $sort = FALSE) { +function elgg_is_menu_item_registered($menu_name, $item_name) { global $CONFIG; - if (!isset($CONFIG->submenu_items) || !($CONFIG->submenu_items)) { - return FALSE; + if (!isset($CONFIG->menus[$menu_name])) { + return false; } - $groups = array(); - - if (isset($CONFIG->submenu_items['all'])) { - $groups = $CONFIG->submenu_items['all']; + foreach ($CONFIG->menus[$menu_name] as $menu_object) { + /* @var ElggMenuItem $menu_object */ + if ($menu_object->getName() == $item_name) { + return true; + } } - if (isset($CONFIG->submenu_items[$context])) { - $groups = array_merge_recursive($groups, $CONFIG->submenu_items[$context]); - } + return false; +} - if (!$groups) { - return FALSE; - } +/** + * Convenience function for registering a button to title menu + * + * The URL must be $handler/$name/$guid where $guid is the guid of the page owner. + * The label of the button is "$handler:$name" so that must be defined in a + * language file. + * + * This is used primarily to support adding an add content button + * + * @param string $handler The handler to use or null to autodetect from context + * @param string $name Name of the button + * @return void + * @since 1.8.0 + */ +function elgg_register_title_button($handler = null, $name = 'add') { + if (elgg_is_logged_in()) { - foreach ($groups as $group => $items) { - if ($sort) { - usort($items, 'elgg_submenu_item_cmp'); + if (!$handler) { + $handler = elgg_get_context(); } - $parsed_menu = array(); - // determin which children need to go in this item. - foreach ($items as $i => $item) { - // can only support children if there's an id - if (isset($item->id)) { - foreach ($items as $child_i => $child_item) { - // don't check ourselves or used children. - if ($child_i == $i || $child_item->used == TRUE) { - continue; - } - - if (isset($child_item->parent_id) && $child_item->parent_id == $item->id) { - if (!isset($item->children)) { - $item->children = array(); - } - $item->children[] = $child_item; - $child_item->parent = $item; - // don't unset because we still need to check this item for children - $child_item->used = TRUE; - } - } - - // if the parent doesn't have a url, make it the first child item. - if (isset($item->children) && $item->children && !$item->href) { - $child = $item->children[0]; - while ($child && !isset($child->href)) { - if (isset($child->children) && isset($child->children[0])) { - $child = $child->children[0]; - } else { - $child = NULL; - } - } - - if ($child && isset($child->href)) { - $item->href = $child->href; - } else { - // @todo There are no URLs anywhere in this tree. - $item->href = elgg_get_site_url(); - } - } - } - - // only add top-level elements to the menu. - // the rest are children. - if (!isset($item->parent_id)) { - $parsed_menu[] = $item; - } + $owner = elgg_get_page_owner_entity(); + if (!$owner) { + // no owns the page so this is probably an all site list page + $owner = elgg_get_logged_in_user_entity(); + } + if ($owner && $owner->canWriteToContainer()) { + $guid = $owner->getGUID(); + elgg_register_menu_item('title', array( + 'name' => $name, + 'href' => "$handler/$name/$guid", + 'text' => elgg_echo("$handler:$name"), + 'link_class' => 'elgg-button elgg-button-action', + )); } - - $CONFIG->submenu[$context][$group] = $parsed_menu; } - - return TRUE; } /** - * Helper function used to sort submenu items by their display text. + * Adds a breadcrumb to the breadcrumbs stack. + * + * @param string $title The title to display + * @param string $link Optional. The link for the title. * - * @param object $a First object - * @param object $b Second object + * @return void + * @since 1.8.0 * - * @return int - * @since 1.8 - * @see elgg_prepare_submenu + * @link http://docs.elgg.org/Tutorials/UI/Breadcrumbs */ -function elgg_submenu_item_cmp($a, $b) { - $a = $a->text; - $b = $b->text; +function elgg_push_breadcrumb($title, $link = NULL) { + global $CONFIG; + if (!isset($CONFIG->breadcrumbs)) { + $CONFIG->breadcrumbs = array(); + } - return strnatcmp($a, $b); + // avoid key collisions. + $CONFIG->breadcrumbs[] = array('title' => elgg_get_excerpt($title, 100), 'link' => $link); } /** - * Use elgg_get_submenu(). - * - * @see elgg_get_submenu() - * @deprecated 1.8 + * Removes last breadcrumb entry. * - * @return string + * @return array popped item. + * @since 1.8.0 + * @link http://docs.elgg.org/Tutorials/UI/Breadcrumbs */ -function get_submenu() { - elgg_deprecated_notice("get_submenu() has been deprecated by elgg_get_submenu()", 1.8); - return elgg_get_submenu(); +function elgg_pop_breadcrumb() { + global $CONFIG; + + if (is_array($CONFIG->breadcrumbs)) { + return array_pop($CONFIG->breadcrumbs); + } + + return FALSE; } /** - * Return the HTML for a sidemenu. - * - * @param string $context The context of the submenu (defaults to main) - * @param BOOL $sort Sort by display name? + * Returns all breadcrumbs as an array of array('title' => 'Readable Title', 'link' => 'URL') * - * @return string Formatted HTML. - * @since 1.8 - * @todo Rename to a view function. See {@trac #2320}. + * @return array Breadcrumbs + * @since 1.8.0 + * @link http://docs.elgg.org/Tutorials/UI/Breadcrumbs */ -function elgg_get_submenu($context = NULL, $sort = FALSE) { +function elgg_get_breadcrumbs() { global $CONFIG; - if (!$context) { - $context = elgg_get_context(); + if (isset($CONFIG->breadcrumbs) && is_array($CONFIG->breadcrumbs)) { + return $CONFIG->breadcrumbs; } - if (!elgg_prepare_submenu($context, $sort)) { - return ''; - } - - $groups = $CONFIG->submenu[$context]; - $submenu_html = ''; - - foreach ($groups as $group => $items) { - // how far down we are in children arrays - $depth = 0; - // push and pop parent items - $temp_items = array(); + return array(); +} - while ($item = current($items)) { - // ignore parents created by a child but parent never defined properly - if (!isset($item->text) || !($item->text)) { - next($items); - continue; +/** + * Set up the site menu + * + * Handles default, featured, and custom menu items + * + * @param string $hook + * @param string $type + * @param array $return Menu array + * @param array $params + * @return array + * @access private + */ +function elgg_site_menu_setup($hook, $type, $return, $params) { + + $featured_menu_names = elgg_get_config('site_featured_menu_names'); + $custom_menu_items = elgg_get_config('site_custom_menu_items'); + if ($featured_menu_names || $custom_menu_items) { + // we have featured or custom menu items + + $registered = $return['default']; + + // set up featured menu items + $featured = array(); + foreach ($featured_menu_names as $name) { + foreach ($registered as $index => $item) { + if ($item->getName() == $name) { + $featured[] = $item; + unset($registered[$index]); + } } + } - // try to guess if this should be selected if they don't specify - if ((!isset($item->selected) || $item->selected === NULL) && isset($item->href)) { - $item->selected = elgg_http_url_is_identical(full_url(), $item->href); - } + // add custom menu items + $n = 1; + foreach ($custom_menu_items as $title => $url) { + $item = new ElggMenuItem("custom$n", $title, $url); + $featured[] = $item; + $n++; + } - // traverse up the parent tree if matached to mark all parents as selected/expanded. - if ($item->selected && isset($item->parent)) { - $parent = $item->parent; - while ($parent) { - $parent->selected = TRUE; - if (isset($parent->parent)) { - $parent = $parent->parent; - } else { - $parent = NULL; - } - } + $return['default'] = $featured; + if (count($registered) > 0) { + $return['more'] = $registered; + } + } else { + // no featured menu items set + $max_display_items = 5; + + // the first n are shown, rest added to more list + // if only one item on more menu, stick it with the rest + $num_menu_items = count($return['default']); + if ($num_menu_items > ($max_display_items + 1)) { + $return['more'] = array_splice($return['default'], $max_display_items); + } + } + + // check if we have anything selected + $selected = false; + foreach ($return as $section) { + foreach ($section as $item) { + if ($item->getSelected()) { + $selected = true; + break 2; } - - // get the next item - if (isset($item->children) && $item->children) { - $depth++; - array_push($temp_items, $items); - $items = $item->children; - } elseif ($depth > 0) { - // check if there are more children elements in the current items - // pop back up to the parent(s) if not - if ($item = next($items)) { - continue; - } else { - while ($depth > 0) { - $depth--; - $items = array_pop($temp_items); - if ($item = next($items)) { - break; - } + } + } + + if (!$selected) { + // nothing selected, match name to context or match url + $current_url = current_page_url(); + foreach ($return as $section_name => $section) { + foreach ($section as $key => $item) { + // only highlight internal links + if (strpos($item->getHref(), elgg_get_site_url()) === 0) { + if ($item->getName() == elgg_get_context()) { + $return[$section_name][$key]->setSelected(true); + break 2; + } + if ($item->getHref() == $current_url) { + $return[$section_name][$key]->setSelected(true); + break 2; } } - } else { - next($items); } } - - $vars = array('group' => $group, 'items' => $items); - $submenu_html .= elgg_view('navigation/submenu_group', $vars); } - // include the JS for the expand menus too - return elgg_view('navigation/submenu_js') . $submenu_html; -} - -/** - * Registers any custom menu items with the main Site Menu. - * - * @note Custom menu items are added through the admin interface. Plugins - * can add standard menu items by using {@link add_menu()}. - * - * @since 1.8 - * @link http://docs.elgg.org/Tutorials/UI/SiteMenu - * @elgg_event_handler init system - * @return void - */ -function add_custom_menu_items() { - if ($custom_items = get_config('menu_items_custom_items')) { - foreach ($custom_items as $url => $name) { - add_menu($name, $url); - } - } + return $return; } /** - * Returns the main site menu. - * - * @note The main site menu is split into "featured" links and - * "more" links. - * - * @return array ('featured_urls' and 'more') - * @since 1.8 - * @link http://docs.elgg.org/Tutorials/UI/SiteMenu + * Add the comment and like links to river actions menu + * @access private */ -function elgg_get_nav_items() { - $menu_items = get_register('menu'); - $featured_urls_info = get_config('menu_items_featured_urls'); - - $more = array(); - $featured_urls = array(); - $featured_urls_sanitised = array(); - - // easier to compare with in_array() than embedded foreach()es - $valid_urls = array(); - foreach ($menu_items as $info) { - $valid_urls[] = $info->value->url; - } - - // make sure the url is a valid link. - // this prevents disabled plugins leaving behind - // valid links when not using a pagehandler. - if ($featured_urls_info) { - foreach ($featured_urls_info as $info) { - if (in_array($info->value->url, $valid_urls)) { - $featured_urls[] = $info->value->url; - $featured_urls_sanitised[] = $info; +function elgg_river_menu_setup($hook, $type, $return, $params) { + if (elgg_is_logged_in()) { + $item = $params['item']; + /* @var ElggRiverItem $item */ + $object = $item->getObjectEntity(); + // comments and non-objects cannot be commented on or liked + if (!elgg_in_context('widgets') && $item->annotation_id == 0) { + // comments + if ($object->canComment()) { + $options = array( + 'name' => 'comment', + 'href' => "#comments-add-$object->guid", + 'text' => elgg_view_icon('speech-bubble'), + 'title' => elgg_echo('comment:this'), + 'rel' => 'toggle', + 'priority' => 50, + ); + $return[] = ElggMenuItem::factory($options); } } - } - - // add toolbar entries if not hiding dupes. - foreach ($menu_items as $name => $info) { - if (!in_array($info->value->url, $featured_urls)) { - $more[] = $info; + + if (elgg_is_admin_logged_in()) { + $options = array( + 'name' => 'delete', + 'href' => elgg_add_action_tokens_to_url("action/river/delete?id=$item->id"), + 'text' => elgg_view_icon('delete'), + 'title' => elgg_echo('delete'), + 'confirm' => elgg_echo('deleteconfirm'), + 'priority' => 200, + ); + $return[] = ElggMenuItem::factory($options); } } - return array( - 'featured' => $featured_urls_sanitised, - 'more' => $more - ); + return $return; } /** - * Adds an item to the site-wide menu. - * - * You can obtain the menu array by calling {@link get_register('menu')} - * - * @param string $menu_name The name of the menu item - * @param string $menu_url The URL of the page - * @param array $menu_children Optionally, an array of submenu items (not currently used) - * @param string $context The context of the menu - * - * @return true|false Depending on success - * @todo Can be deprecated when the new menu system is introduced. + * Entity menu is list of links and info on any entity + * @access private */ -function add_menu($menu_name, $menu_url, $menu_children = array(), $context = "") { - global $CONFIG; - - if (!isset($CONFIG->menucontexts)) { - $CONFIG->menucontexts = array(); +function elgg_entity_menu_setup($hook, $type, $return, $params) { + if (elgg_in_context('widgets')) { + return $return; } - - if (empty($context)) { - $context = get_plugin_name(); + + $entity = $params['entity']; + /* @var ElggEntity $entity */ + $handler = elgg_extract('handler', $params, false); + + // access + $access = elgg_view('output/access', array('entity' => $entity)); + $options = array( + 'name' => 'access', + 'text' => $access, + 'href' => false, + 'priority' => 100, + ); + $return[] = ElggMenuItem::factory($options); + + if ($entity->canEdit() && $handler) { + // edit link + $options = array( + 'name' => 'edit', + 'text' => elgg_echo('edit'), + 'title' => elgg_echo('edit:this'), + 'href' => "$handler/edit/{$entity->getGUID()}", + 'priority' => 200, + ); + $return[] = ElggMenuItem::factory($options); + + // delete link + $options = array( + 'name' => 'delete', + 'text' => elgg_view_icon('delete'), + 'title' => elgg_echo('delete:this'), + 'href' => "action/$handler/delete?guid={$entity->getGUID()}", + 'confirm' => elgg_echo('deleteconfirm'), + 'priority' => 300, + ); + $return[] = ElggMenuItem::factory($options); } - $value = new stdClass(); - $value->url = elgg_normalize_url($menu_url); - $value->context = $context; - - $CONFIG->menucontexts[] = $context; - return add_to_register('menu', $menu_name, $value, $menu_children); + return $return; } /** - * Removes an item from the menu register - * - * @param string $menu_name The name of the menu item - * - * @return true|false Depending on success - */ -function remove_menu($menu_name) { - return remove_from_register('menu', $menu_name); -} - -/** - * Returns a menu item for use in the children section of add_menu() - * This is not currently used in the Elgg core. - * - * @param string $menu_name The name of the menu item - * @param string $menu_url Its URL - * - * @return stdClass|false Depending on success - * @todo Can be deprecated when the new menu system is introduced. - */ -function menu_item($menu_name, $menu_url) { - elgg_deprecated_notice('menu_item() is deprecated by add_submenu_item', 1.7); - return make_register_object($menu_name, $menu_url); -} - -/** - * Adds a breadcrumb to the breadcrumbs stack. - * - * @param string $title The title to display - * @param string $link Optional. The link for the title. - * - * @return void - * - * @link http://docs.elgg.org/Tutorials/UI/Breadcrumbs + * Widget menu is a set of widget controls + * @access private */ -function elgg_push_breadcrumb($title, $link = NULL) { - global $CONFIG; - if (!is_array($CONFIG->breadcrumbs)) { - $CONFIG->breadcrumbs = array(); +function elgg_widget_menu_setup($hook, $type, $return, $params) { + + $widget = $params['entity']; + /* @var ElggWidget $widget */ + $show_edit = elgg_extract('show_edit', $params, true); + + $collapse = array( + 'name' => 'collapse', + 'text' => ' ', + 'href' => "#elgg-widget-content-$widget->guid", + 'class' => 'elgg-widget-collapse-button', + 'rel' => 'toggle', + 'priority' => 1 + ); + $return[] = ElggMenuItem::factory($collapse); + + if ($widget->canEdit()) { + $delete = array( + 'name' => 'delete', + 'text' => elgg_view_icon('delete-alt'), + 'title' => elgg_echo('widget:delete', array($widget->getTitle())), + 'href' => "action/widgets/delete?widget_guid=$widget->guid", + 'is_action' => true, + 'class' => 'elgg-widget-delete-button', + 'id' => "elgg-widget-delete-button-$widget->guid", + 'priority' => 900 + ); + $return[] = ElggMenuItem::factory($delete); + + if ($show_edit) { + $edit = array( + 'name' => 'settings', + 'text' => elgg_view_icon('settings-alt'), + 'title' => elgg_echo('widget:edit'), + 'href' => "#widget-edit-$widget->guid", + 'class' => "elgg-widget-edit-button", + 'rel' => 'toggle', + 'priority' => 800, + ); + $return[] = ElggMenuItem::factory($edit); + } } - // avoid key collisions. - $CONFIG->breadcrumbs[] = array('title' => $title, 'link' => $link); + return $return; } /** - * Removes last breadcrumb entry. - * - * @return array popped item. - * @link http://docs.elgg.org/Tutorials/UI/Breadcrumbs + * Adds a delete link to "generic_comment" annotations + * @access private */ -function elgg_pop_breadcrumb() { - global $CONFIG; - - if (is_array($CONFIG->breadcrumbs)) { - array_pop($CONFIG->breadcrumbs); +function elgg_annotation_menu_setup($hook, $type, $return, $params) { + $annotation = $params['annotation']; + /* @var ElggAnnotation $annotation */ + + if ($annotation->name == 'generic_comment' && $annotation->canEdit()) { + $url = elgg_http_add_url_query_elements('action/comments/delete', array( + 'annotation_id' => $annotation->id, + )); + + $options = array( + 'name' => 'delete', + 'href' => $url, + 'text' => "<span class=\"elgg-icon elgg-icon-delete\"></span>", + 'confirm' => elgg_echo('deleteconfirm'), + 'encode_text' => false + ); + $return[] = ElggMenuItem::factory($options); } - return FALSE; + return $return; } + /** - * Returns all breadcrumbs as an array of array('title' => 'Readable Title', 'link' => 'URL') - * - * @return array Breadcrumbs - * @link http://docs.elgg.org/Tutorials/UI/Breadcrumbs + * Navigation initialization + * @access private */ -function elgg_get_breadcrumbs() { - global $CONFIG; - - return (is_array($CONFIG->breadcrumbs)) ? $CONFIG->breadcrumbs : array(); +function elgg_nav_init() { + elgg_register_plugin_hook_handler('prepare', 'menu:site', 'elgg_site_menu_setup'); + elgg_register_plugin_hook_handler('register', 'menu:river', 'elgg_river_menu_setup'); + elgg_register_plugin_hook_handler('register', 'menu:entity', 'elgg_entity_menu_setup'); + elgg_register_plugin_hook_handler('register', 'menu:widget', 'elgg_widget_menu_setup'); + elgg_register_plugin_hook_handler('register', 'menu:annotation', 'elgg_annotation_menu_setup'); } + +elgg_register_event_handler('init', 'system', 'elgg_nav_init'); diff --git a/engine/lib/notification.php b/engine/lib/notification.php index e75e6b34b..be0c359d4 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -19,6 +19,7 @@ */ /** Notification handlers */ +global $NOTIFICATION_HANDLERS; $NOTIFICATION_HANDLERS = array(); /** @@ -37,7 +38,7 @@ $NOTIFICATION_HANDLERS = array(); function register_notification_handler($method, $handler, $params = NULL) { global $NOTIFICATION_HANDLERS; - if (is_callable($handler)) { + if (is_callable($handler, true)) { $NOTIFICATION_HANDLERS[$method] = new stdClass; $NOTIFICATION_HANDLERS[$method]->handler = $handler; @@ -85,7 +86,7 @@ function unregister_notification_handler($method) { * @throws NotificationException */ function notify_user($to, $from, $subject, $message, array $params = NULL, $methods_override = "") { - global $NOTIFICATION_HANDLERS, $CONFIG; + global $NOTIFICATION_HANDLERS; // Sanitise if (!is_array($to)) { @@ -109,12 +110,15 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth // Are we overriding delivery? $methods = $methods_override; if (!$methods) { - $tmp = (array)get_user_notification_settings($guid); + $tmp = get_user_notification_settings($guid); $methods = array(); - foreach ($tmp as $k => $v) { - // Add method if method is turned on for user! - if ($v) { - $methods[] = $k; + // $tmp may be false. don't cast + if (is_object($tmp)) { + foreach ($tmp as $k => $v) { + // Add method if method is turned on for user! + if ($v) { + $methods[] = $k; + } } } } @@ -130,8 +134,9 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth // Extract method details from list $details = $NOTIFICATION_HANDLERS[$method]; $handler = $details->handler; + /* @var callable $handler */ - if ((!$NOTIFICATION_HANDLERS[$method]) || (!$handler)) { + if ((!$NOTIFICATION_HANDLERS[$method]) || (!$handler) || (!is_callable($handler))) { error_log(elgg_echo('NotificationException:NoHandlerFound', array($method))); } @@ -139,7 +144,7 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth // Trigger handler and retrieve result. try { - $result[$guid][$method] = $handler( + $result[$guid][$method] = call_user_func($handler, $from ? get_entity($from) : NULL, // From entity get_entity($guid), // To entity $subject, // The subject @@ -163,16 +168,21 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth * * @param int $user_guid The user id * - * @return stdClass + * @return stdClass|false */ function get_user_notification_settings($user_guid = 0) { $user_guid = (int)$user_guid; if ($user_guid == 0) { - $user_guid = get_loggedin_userid(); + $user_guid = elgg_get_logged_in_user_guid(); } - $all_metadata = get_metadata_for_entity($user_guid); + // @todo: there should be a better way now that metadata is cached. E.g. just query for MD names, then + // query user object directly + $all_metadata = elgg_get_metadata(array( + 'guid' => $user_guid, + 'limit' => 0 + )); if ($all_metadata) { $prefix = "notification:method:"; $return = new stdClass; @@ -207,7 +217,7 @@ function set_user_notification_setting($user_guid, $method, $value) { $user = get_entity($user_guid); if (!$user) { - $user = get_loggedin_user(); + $user = elgg_get_logged_in_user_entity(); } if (($user) && ($user instanceof ElggUser)) { @@ -231,6 +241,8 @@ function set_user_notification_setting($user_guid, $method, $value) { * @param array $params Optional parameters (none taken in this instance) * * @return bool + * @throws NotificationException + * @access private */ function email_notify_handler(ElggEntity $from, ElggUser $to, $subject, $message, array $params = NULL) { @@ -256,7 +268,7 @@ array $params = NULL) { $to = $to->email; // From - $site = get_entity($CONFIG->site_guid); + $site = elgg_get_site_entity(); // If there's an email address, use it - but only if its not from a user. if (!($from instanceof ElggUser) && $from->email) { $from = $from->email; @@ -281,18 +293,19 @@ array $params = NULL) { * @param array $params Optional parameters (none used in this function) * * @return bool + * @throws NotificationException * @since 1.7.2 */ function elgg_send_email($from, $to, $subject, $body, array $params = NULL) { global $CONFIG; if (!$from) { - $msg = elgg_echo('NotificationException:NoEmailAddress', array('from')); + $msg = elgg_echo('NotificationException:MissingParameter', array('from')); throw new NotificationException($msg); } if (!$to) { - $msg = elgg_echo('NotificationException:NoEmailAddress', array('to')); + $msg = elgg_echo('NotificationException:MissingParameter', array('to')); throw new NotificationException($msg); } @@ -337,6 +350,8 @@ function elgg_send_email($from, $to, $subject, $body, array $params = NULL) { // Sanitise subject by stripping line endings $subject = preg_replace("/(\r\n|\r|\n)/", " ", $subject); + // this is because Elgg encodes everything and matches what is done with body + $subject = html_entity_decode($subject, ENT_COMPAT, 'UTF-8'); // Decode any html entities if (is_callable('mb_encode_mimeheader')) { $subject = mb_encode_mimeheader($subject, "UTF-8", "B"); } @@ -354,13 +369,14 @@ function elgg_send_email($from, $to, $subject, $body, array $params = NULL) { * Correctly initialise notifications and register the email handler. * * @return void + * @access private */ function notification_init() { // Register a notification handler for the default email method register_notification_handler("email", "email_notify_handler"); // Add settings view to user settings & register action - extend_elgg_settings_page('notifications/settings/usersettings', 'usersettings/user'); + elgg_extend_view('forms/account/settings', 'core/settings/account/notifications'); elgg_register_plugin_hook_handler('usersettings:save', 'user', 'notification_user_settings_save'); } @@ -370,6 +386,7 @@ function notification_init() { * * @return void * @todo why can't this call action(...)? + * @access private */ function notification_user_settings_save() { global $CONFIG; @@ -413,7 +430,7 @@ function register_notification_object($entity_type, $object_subtype, $language_n * @param int $user_guid The GUID of the user who wants to follow a user's content * @param int $author_guid The GUID of the user whose content the user wants to follow * - * @return true|false Depending on success + * @return bool Depending on success */ function register_notification_interest($user_guid, $author_guid) { return add_entity_relationship($user_guid, 'notify', $author_guid); @@ -425,7 +442,7 @@ function register_notification_interest($user_guid, $author_guid) { * @param int $user_guid The GUID of the user who is following a user's content * @param int $author_guid The GUID of the user whose content the user wants to unfollow * - * @return true|false Depending on success + * @return bool Depending on success */ function remove_notification_interest($user_guid, $author_guid) { return remove_entity_relationship($user_guid, 'notify', $author_guid); @@ -441,11 +458,13 @@ function remove_notification_interest($user_guid, $author_guid) { * @param string $object_type mixed * @param mixed $object The object created * - * @return void + * @return bool + * @access private */ function object_notifications($event, $object_type, $object) { // We only want to trigger notification events for ElggEntities if ($object instanceof ElggEntity) { + /* @var ElggEntity $object */ // Get config data global $CONFIG, $SESSION, $NOTIFICATION_HANDLERS; @@ -471,35 +490,37 @@ function object_notifications($event, $object_type, $object) { } if (isset($CONFIG->register_objects[$object_type][$object_subtype])) { - $descr = $CONFIG->register_objects[$object_type][$object_subtype]; - $string = $descr . ": " . $object->getURL(); + $subject = $CONFIG->register_objects[$object_type][$object_subtype]; + $string = $subject . ": " . $object->getURL(); // Get users interested in content from this person and notify them // (Person defined by container_guid so we can also subscribe to groups if we want) foreach ($NOTIFICATION_HANDLERS as $method => $foo) { $interested_users = elgg_get_entities_from_relationship(array( + 'site_guids' => ELGG_ENTITIES_ANY_VALUE, 'relationship' => 'notify' . $method, 'relationship_guid' => $object->container_guid, 'inverse_relationship' => TRUE, - 'types' => 'user', - 'limit' => 99999 + 'type' => 'user', + 'limit' => false )); + /* @var ElggUser[] $interested_users */ if ($interested_users && is_array($interested_users)) { foreach ($interested_users as $user) { if ($user instanceof ElggUser && !$user->isBanned()) { if (($user->guid != $SESSION['user']->guid) && has_access_to_entity($object, $user) && $object->access_id != ACCESS_PRIVATE) { - $methodstring = elgg_trigger_plugin_hook('notify:entity:message', $object->getType(), array( + $body = elgg_trigger_plugin_hook('notify:entity:message', $object->getType(), array( 'entity' => $object, 'to_entity' => $user, 'method' => $method), $string); - if (empty($methodstring) && $methodstring !== false) { - $methodstring = $string; + if (empty($body) && $body !== false) { + $body = $string; } - if ($methodstring !== false) { - notify_user($user->guid, $object->container_guid, $descr, $methodstring, - NULL, array($method)); + if ($body !== false) { + notify_user($user->guid, $object->container_guid, $subject, $body, + null, array($method)); } } } @@ -512,4 +533,4 @@ function object_notifications($event, $object_type, $object) { // Register a startup event elgg_register_event_handler('init', 'system', 'notification_init', 0); -elgg_register_event_handler('create', 'object', 'object_notifications');
\ No newline at end of file +elgg_register_event_handler('create', 'object', 'object_notifications'); diff --git a/engine/lib/objects.php b/engine/lib/objects.php index 51b47df2a..ff3cc733f 100644 --- a/engine/lib/objects.php +++ b/engine/lib/objects.php @@ -13,6 +13,7 @@ * @param int $guid The guid to retreive * * @return bool + * @access private */ function get_object_entity_as_row($guid) { global $CONFIG; @@ -30,6 +31,7 @@ function get_object_entity_as_row($guid) { * @param string $description The object's description * * @return bool + * @access private */ function create_object_entity($guid, $title, $description) { global $CONFIG; @@ -51,11 +53,8 @@ function create_object_entity($guid, $title, $description) { if ($result != false) { // Update succeeded, continue $entity = get_entity($guid); - if (elgg_trigger_event('update', $entity->type, $entity)) { - return $guid; - } else { - $entity->delete(); - } + elgg_trigger_event('update', $entity->type, $entity); + return $guid; } } else { // Update failed, attempt an insert. @@ -78,76 +77,6 @@ function create_object_entity($guid, $title, $description) { } /** - * THIS FUNCTION IS DEPRECATED. - * - * Delete a object's extra data. - * - * @todo - this should be removed - was deprecated in 1.5 or earlier - * - * @param int $guid GUID - * - * @return 1 - */ -function delete_object_entity($guid) { - system_message(elgg_echo('deprecatedfunction', array('delete_user_entity'))); - - return 1; // Always return that we have deleted one row in order to not break existing code. -} - -/** - * Searches for an object based on a complete or partial title - * or description using full text searching. - * - * IMPORTANT NOTE: With MySQL's default setup: - * 1) $criteria must be 4 or more characters long - * 2) If $criteria matches greater than 50% of results NO RESULTS ARE RETURNED! - * - * @param string $criteria The partial or full name or username. - * @param int $limit Limit of the search. - * @param int $offset Offset. - * @param string $order_by The order. - * @param boolean $count Whether to return the count of results or just the results. - * - * @return int|false - * @deprecated 1.7 - */ -function search_for_object($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) { - elgg_deprecated_notice('search_for_object() was deprecated by new search plugin.', 1.7); - global $CONFIG; - - $criteria = sanitise_string($criteria); - $limit = (int)$limit; - $offset = (int)$offset; - $order_by = sanitise_string($order_by); - $container_guid = (int)$container_guid; - - $access = get_access_sql_suffix("e"); - - if ($order_by == "") { - $order_by = "e.time_created desc"; - } - - if ($count) { - $query = "SELECT count(e.guid) as total "; - } else { - $query = "SELECT e.* "; - } - $query .= "from {$CONFIG->dbprefix}entities e - join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid - where match(o.title,o.description) against ('$criteria') and $access"; - - if (!$count) { - $query .= " order by $order_by limit $offset, $limit"; // Add order and limit - return get_data($query, "entity_row_to_elggstar"); - } else { - if ($count = get_data_row($query)) { - return $count->total; - } - } - return false; -} - -/** * Get the sites this object is part of * * @param int $object_guid The object's GUID @@ -164,21 +93,22 @@ function get_object_sites($object_guid, $limit = 10, $offset = 0) { return elgg_get_entities_from_relationship(array( 'relationship' => 'member_of_site', 'relationship_guid' => $object_guid, - 'types' => 'site', + 'type' => 'site', 'limit' => $limit, - 'offset' => $offset + 'offset' => $offset, )); } /** * Runs unit tests for ElggObject * - * @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 objects_test($hook, $type, $value, $params) { global $CONFIG; @@ -186,43 +116,5 @@ function objects_test($hook, $type, $value, $params) { return $value; } - -/** - * Returns a formatted list of objects suitable for injecting into search. - * - * @deprecated 1.7 - * - * @param sting $hook Hook - * @param string $user user - * @param mixed $returnvalue Previous return value - * @param mixed $tag Search term - * - * @return array - */ -function search_list_objects_by_name($hook, $user, $returnvalue, $tag) { - elgg_deprecated_notice('search_list_objects_by_name was deprecated by new search plugin.', 1.7); - - // Change this to set the number of users that display on the search page - $threshold = 4; - - $object = get_input('object'); - - if (!get_input('offset') && (empty($object) || $object == 'user')) { - if ($users = search_for_user($tag, $threshold)) { - $countusers = search_for_user($tag, 0, 0, "", true); - - $return = elgg_view('user/search/startblurb', array('count' => $countusers, 'tag' => $tag)); - foreach ($users as $user) { - $return .= elgg_view_entity($user); - } - $return .= elgg_view('user/search/finishblurb', - array('count' => $countusers, 'threshold' => $threshold, 'tag' => $tag)); - - return $return; - - } - } -} - elgg_register_event_handler('init', 'system', 'objects_init', 0); -elgg_register_plugin_hook_handler('unit_test', 'system', 'objects_test');
\ No newline at end of file +elgg_register_plugin_hook_handler('unit_test', 'system', 'objects_test'); diff --git a/engine/lib/opendd.php b/engine/lib/opendd.php index d856806f5..7d635a295 100644 --- a/engine/lib/opendd.php +++ b/engine/lib/opendd.php @@ -7,12 +7,15 @@ * @version 0.4 */ +// @codingStandardsIgnoreStart + /** * Attempt to construct an ODD object out of a XmlElement or sub-elements. * * @param XmlElement $element The element(s) * * @return mixed An ODD object if the element can be handled, or false. + * @access private */ function ODD_factory (XmlElement $element) { $name = $element->name; @@ -57,6 +60,7 @@ function ODD_factory (XmlElement $element) { * @param string $xml The XML ODD. * * @return ODDDocument + * @access private */ function ODD_Import($xml) { // Parse XML to an array @@ -96,7 +100,10 @@ function ODD_Import($xml) { * @param ODDDocument $document The Document. * * @return string + * @access private */ function ODD_Export(ODDDocument $document) { return "$document"; -}
\ No newline at end of file +} + +// @codingStandardsIgnoreEnd diff --git a/engine/lib/output.php b/engine/lib/output.php index 8df4bf089..de4f911fb 100644 --- a/engine/lib/output.php +++ b/engine/lib/output.php @@ -12,29 +12,34 @@ * * @param string $text The input string * - * @return string The output stirng with formatted links - **/ + * @return string The output string with formatted links + */ function parse_urls($text) { + + // URI specification: http://www.ietf.org/rfc/rfc3986.txt + // This varies from the specification in the following ways: + // * Supports non-ascii characters + // * Does not allow parentheses and single quotes + // * Cuts off commas, exclamation points, and periods off as last character + // @todo this causes problems with <attr = "val"> - // must be ing <attr="val"> format (no space). + // must be in <attr="val"> format (no space). // By default htmlawed rewrites tags to this format. // if PHP supported conditional negative lookbehinds we could use this: // $r = preg_replace_callback('/(?<!=)(?<![ ])?(?<!["\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\'\!\(\),]+)/i', - // - // we can put , in the list of excluded char but need to keep . because of domain names. - // it is removed in the callback. - $r = preg_replace_callback('/(?<!=)(?<!["\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\'\!\(\),]+)/i', + $r = preg_replace_callback('/(?<![=\/"\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\']+)/i', create_function( '$matches', ' $url = $matches[1]; - $period = \'\'; - if (substr($url, -1, 1) == \'.\') { - $period = \'.\'; - $url = trim($url, \'.\'); + $punc = ""; + $last = substr($url, -1, 1); + if (in_array($last, array(".", "!", ",", "(", ")"))) { + $punc = $last; + $url = rtrim($url, ".!,()"); } $urltext = str_replace("/", "/<wbr />", $url); - return "<a href=\"$url\" style=\"text-decoration:underline;\">$urltext</a>$period"; + return "<a href=\"$url\" rel=\"nofollow\">$urltext</a>$punc"; ' ), $text); @@ -43,51 +48,26 @@ function parse_urls($text) { /** * Create paragraphs from text with line spacing - * Borrowed from Wordpress. * * @param string $pee The string - * @param bool $br Add BRs? + * @deprecated Use elgg_autop instead + * @todo Add deprecation warning in 1.9 * - * @todo Rewrite * @return string **/ -function autop($pee, $br = 1) { - $pee = $pee . "\n"; // just to make things a little easier, pad the end - $pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee); - // Space things out a little - $allblocks = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)'; - $pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee); - $pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee); - $pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines - if ( strpos($pee, '<object') !== false ) { - $pee = preg_replace('|\s*<param([^>]*)>\s*|', "<param$1>", $pee); // no pee inside object/embed - $pee = preg_replace('|\s*</embed>\s*|', '</embed>', $pee); - } - $pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates - $pee = preg_replace('/\n?(.+?)(?:\n\s*\n|\z)/s', "<p>$1</p>\n", $pee); // make paragraphs, including one at the end - $pee = preg_replace('|<p>\s*?</p>|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace - $pee = preg_replace('!<p>([^<]+)\s*?(</(?:div|address|form)[^>]*>)!', "<p>$1</p>$2", $pee); - $pee = preg_replace( '|<p>|', "$1<p>", $pee ); - $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); // don't pee all over a tag - $pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problem with nested lists - $pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee); - $pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee); - $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee); - $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); - if ($br) { - $pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', create_function('$matches', 'return str_replace("\n", "<WPPreserveNewline />", $matches[0]);'), $pee); - $pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee); // optionally make line breaks - $pee = str_replace('<WPPreserveNewline />', "\n", $pee); - } - $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee); - $pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee); -// if (strpos($pee, '<pre') !== false) { -// mind the space between the ? and >. Only there because of the comment. -// $pee = preg_replace_callback('!(<pre.*? >)(.*?)</pre>!is', 'clean_pre', $pee ); -// } - $pee = preg_replace( "|\n</p>$|", '</p>', $pee ); - - return $pee; +function autop($pee) { + return elgg_autop($pee); +} + +/** + * Create paragraphs from text with line spacing + * + * @param string $string The string + * + * @return string + **/ +function elgg_autop($string) { + return ElggAutoP::getInstance()->process($string); } /** @@ -170,7 +150,7 @@ function elgg_format_attributes(array $attrs) { } // ignore $vars['entity'] => ElggEntity stuff - if (is_not_null($val) && (is_array($val) || is_string($val))) { + if ($val !== NULL && $val !== false && (is_array($val) || !is_object($val))) { // allow $vars['class'] => array('one', 'two'); // @todo what about $vars['style']? Needs to be semi-colon separated... @@ -178,7 +158,7 @@ function elgg_format_attributes(array $attrs) { $val = implode(' ', $val); } - $val = htmlspecialchars($val); + $val = htmlspecialchars($val, ENT_QUOTES, 'UTF-8', false); $attributes[] = "$attr=\"$val\""; } } @@ -186,7 +166,6 @@ function elgg_format_attributes(array $attrs) { return implode(' ', $attributes); } - /** * Preps an associative array for use in {@link elgg_format_attributes()}. * @@ -199,10 +178,12 @@ function elgg_format_attributes(array $attrs) { * @param array $vars The raw $vars array with all it's dirtiness (config, url, etc.) * * @return array The array, ready to be used in elgg_format_attributes(). + * @access private */ function elgg_clean_vars(array $vars = array()) { unset($vars['config']); unset($vars['url']); + unset($vars['user']); // backwards compatibility code if (isset($vars['internalname'])) { @@ -215,6 +196,14 @@ function elgg_clean_vars(array $vars = array()) { unset($vars['internalid']); } + if (isset($vars['__ignoreInternalid'])) { + unset($vars['__ignoreInternalid']); + } + + if (isset($vars['__ignoreInternalname'])) { + unset($vars['__ignoreInternalname']); + } + return $vars; } @@ -225,7 +214,7 @@ function elgg_clean_vars(array $vars = array()) { * * @example * elgg_normalize_url(''); // 'http://my.site.com/' - * elgg_normalize_url('pg/dashboard'); // 'http://my.site.com/pg/dashboard' + * elgg_normalize_url('dashboard'); // 'http://my.site.com/dashboard' * elgg_normalize_url('http://google.com/'); // no change * elgg_normalize_url('//google.com/'); // no change * @@ -234,23 +223,49 @@ function elgg_clean_vars(array $vars = array()) { * @return string The absolute url */ function elgg_normalize_url($url) { - // 'http://example.com', 'https://example.com', '//example.com' - if (preg_match("#^(https?:)?//#i", $url)) { - return $url; + // see https://bugs.php.net/bug.php?id=51192 + // from the bookmarks save action. + $php_5_2_13_and_below = version_compare(PHP_VERSION, '5.2.14', '<'); + $php_5_3_0_to_5_3_2 = version_compare(PHP_VERSION, '5.3.0', '>=') && + version_compare(PHP_VERSION, '5.3.3', '<'); + + if ($php_5_2_13_and_below || $php_5_3_0_to_5_3_2) { + $tmp_address = str_replace("-", "", $url); + $validated = filter_var($tmp_address, FILTER_VALIDATE_URL); + } else { + $validated = filter_var($url, FILTER_VALIDATE_URL); } - // 'install.php', 'install.php?step=step' - elseif (preg_match("#^[^/]*\.php(\?.*)?$#i", $url)) { - return elgg_get_site_url().$url; + // work around for handling absoluate IRIs (RFC 3987) - see #4190 + if (!$validated && (strpos($url, 'http:') === 0) || (strpos($url, 'https:') === 0)) { + $validated = true; } - // 'example.com', 'example.com/subpage' - elseif (preg_match("#^[^/]*\.#i", $url)) { + if ($validated) { + // all normal URLs including mailto: + return $url; + + } elseif (preg_match("#^(\#|\?|//)#i", $url)) { + // '//example.com' (Shortcut for protocol.) + // '?query=test', #target + return $url; + + } elseif (stripos($url, 'javascript:') === 0 || stripos($url, 'mailto:') === 0) { + // 'javascript:' and 'mailto:' + // Not covered in FILTER_VALIDATE_URL + return $url; + + } elseif (preg_match("#^[^/]*\.php(\?.*)?$#i", $url)) { + // 'install.php', 'install.php?step=step' + return elgg_get_site_url() . $url; + + } elseif (preg_match("#^[^/]*\.#i", $url)) { + // 'example.com', 'example.com/subpage' return "http://$url"; - } - // 'pg/page/handler', 'mod/plugin/file.php' - else { + } else { + // 'page/handler', 'mod/plugin/file.php' + // trim off any leading / because the site URL is stored // with a trailing / return elgg_get_site_url() . ltrim($url, '/'); @@ -263,19 +278,6 @@ function elgg_normalize_url($url) { * @param string $title The title * * @return string The optimised title - * @deprecated 1.8 - */ -function friendly_title($title) { - elgg_deprecated_notice('friendly_title was deprecated by elgg_get_friendly_title', 1.8); - return elgg_get_friendly_title($title); -} - -/** - * When given a title, returns a version suitable for inclusion in a URL - * - * @param string $title The title - * - * @return string The optimised title * @since 1.7.2 */ function elgg_get_friendly_title($title) { @@ -287,26 +289,12 @@ function elgg_get_friendly_title($title) { return $result; } - //$title = iconv('UTF-8', 'ASCII//TRANSLIT', $title); - $title = preg_replace("/[^\w ]/", "", $title); - $title = str_replace(" ", "-", $title); - $title = str_replace("--", "-", $title); - $title = trim($title); - $title = strtolower($title); - return $title; -} + // titles are often stored HTML encoded + $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8'); + + $title = ElggTranslit::urlize($title); -/** - * Displays a UNIX timestamp in a friendly way (eg "less than a minute ago") - * - * @param int $time A UNIX epoch timestamp - * - * @return string The friendly time - * @deprecated 1.8 - */ -function friendly_time($time) { - elgg_deprecated_notice('friendly_time was deprecated by elgg_view_friendly_time', 1.8); - return elgg_view_friendly_time($time); + return $title; } /** @@ -375,7 +363,7 @@ function elgg_get_friendly_time($time) { /** * Strip tags and offer plugins the chance. * Plugins register for output:strip_tags plugin hook. - * Original string included in $params['original_string'] + * Original string included in $params['original_string'] * * @param string $string Formatted string * @@ -390,70 +378,92 @@ function elgg_strip_tags($string) { return $string; } - - /** - * Filters a string into an array of significant words - * - * @deprecated 1.8 - * - * @param string $string A string - * - * @return array - */ -function filter_string($string) { - elgg_deprecated_notice('filter_string() was deprecated!', 1.8); - - // Convert it to lower and trim - $string = strtolower($string); - $string = trim($string); - - // Remove links and email addresses - // match protocol://address/path/file.extension?some=variable&another=asf% - $string = preg_replace("/\s([a-zA-Z]+:\/\/[a-z][a-z0-9\_\.\-]*[a-z]{2,6}" - . "[a-zA-Z0-9\/\*\-\?\&\%\=]*)([\s|\.|\,])/iu", " ", $string); - - // match www.something.domain/path/file.extension?some=variable&another=asf% - $string = preg_replace("/\s(www\.[a-z][a-z0-9\_\.\-]*[a-z]{2,6}" - . "[a-zA-Z0-9\/\*\-\?\&\%\=]*)([\s|\.|\,])/iu", " ", $string); - - // match name@address - $string = preg_replace("/\s([a-zA-Z][a-zA-Z0-9\_\.\-]*[a-zA-Z]" - . "*\@[a-zA-Z][a-zA-Z0-9\_\.\-]*[a-zA-Z]{2,6})([\s|\.|\,])/iu", " ", $string); - - // Sanitise the string; remove unwanted characters - $string = preg_replace('/\W/ui', ' ', $string); - - // Explode it into an array - $terms = explode(' ', $string); - - // Remove any blacklist terms - //$terms = array_filter($terms, 'remove_blacklist'); - - return $terms; + * Apply html_entity_decode() to a string while re-entitising HTML + * special char entities to prevent them from being decoded back to their + * unsafe original forms. + * + * This relies on html_entity_decode() not translating entities when + * doing so leaves behind another entity, e.g. &gt; if decoded would + * create > which is another entity itself. This seems to escape the + * usual behaviour where any two paired entities creating a HTML tag are + * usually decoded, i.e. a lone > is not decoded, but <foo> would + * be decoded to <foo> since it creates a full tag. + * + * Note: This function is poorly explained in the manual - which is really + * bad given its potential for misuse on user input already escaped elsewhere. + * Stackoverflow is littered with advice to use this function in the precise + * way that would lead to user input being capable of injecting arbitrary HTML. + * + * @param string $string + * + * @return string + * + * @author Pádraic Brady + * @copyright Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com) + * @license Released under dual-license GPL2/MIT by explicit permission of Pádraic Brady + * + * @access private + */ +function _elgg_html_decode($string) { + $string = str_replace( + array('>', '<', '&', '"', '''), + array('&gt;', '&lt;', '&amp;', '&quot;', '&#039;'), + $string + ); + $string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8'); + $string = str_replace( + array('&gt;', '&lt;', '&amp;', '&quot;', '&#039;'), + array('>', '<', '&', '"', '''), + $string + ); + return $string; } /** - * Returns true if the word in $input is considered significant + * Prepares query string for output to prevent CSRF attacks. + * + * @param string $string + * @return string * - * @deprecated 1.8 + * @access private + */ +function _elgg_get_display_query($string) { + //encode <,>,&, quotes and characters above 127 + if (function_exists('mb_convert_encoding')) {
+ $display_query = mb_convert_encoding($string, 'HTML-ENTITIES', 'UTF-8');
+ } else {
+ // if no mbstring extension, we just strip characters
+ $display_query = preg_replace("/[^\x01-\x7F]/", "", $string);
+ }
+ return htmlspecialchars($display_query, ENT_QUOTES, 'UTF-8', false); +} + +/** + * Unit tests for Output * - * @param string $input A word + * @param string $hook unit_test + * @param string $type system + * @param mixed $value Array of tests + * @param mixed $params Params * - * @return true|false + * @return array + * @access private */ -function remove_blacklist($input) { - elgg_deprecated_notice('remove_blacklist() was deprecated!', 1.8); - +function output_unit_test($hook, $type, $value, $params) { global $CONFIG; + $value[] = $CONFIG->path . 'engine/tests/api/output.php'; + return $value; +} - if (!is_array($CONFIG->wordblacklist)) { - return $input; - } - - if (strlen($input) < 3 || in_array($input, $CONFIG->wordblacklist)) { - return false; - } +/** + * Initialise the Output subsystem. + * + * @return void + * @access private + */ +function output_init() { + elgg_register_plugin_hook_handler('unit_test', 'system', 'output_unit_test'); +} - return true; -}
\ No newline at end of file +elgg_register_event_handler('init', 'system', 'output_init'); diff --git a/engine/lib/pagehandler.php b/engine/lib/pagehandler.php index 727ace54d..0cf99b6fe 100644 --- a/engine/lib/pagehandler.php +++ b/engine/lib/pagehandler.php @@ -7,14 +7,17 @@ */ /** - * Turns the current page over to the page handler, allowing registered handlers to take over. + * Routes the request to a registered page handler * - * If a page handler returns FALSE, the request is handed over to the default_page_handler. + * This function sets the context based on the handler name (first segment of the + * URL). It also triggers a plugin hook 'route', $handler so that plugins can + * modify the routing or handle a request. * * @param string $handler The name of the handler type (eg 'blog') * @param array $page The parameters to the page, as an array (exploded by '/' slashes) * - * @return true|false Depending on whether a registered page handler was found + * @return bool + * @access private */ function page_handler($handler, $page) { global $CONFIG; @@ -27,57 +30,64 @@ function page_handler($handler, $page) { array_pop($page); } - if (!isset($CONFIG->pagehandler) || empty($handler)) { - $result = false; - } else if (isset($CONFIG->pagehandler[$handler]) && is_callable($CONFIG->pagehandler[$handler])) { - $function = $CONFIG->pagehandler[$handler]; - $result = $function($page, $handler); - if ($result !== false) { - $result = true; - } - } else { - $result = false; + // return false to stop processing the request (because you handled it) + // return a new $request array if you want to route the request differently + $request = array( + 'handler' => $handler, + 'segments' => $page, + ); + $request = elgg_trigger_plugin_hook('route', $handler, null, $request); + if ($request === false) { + return true; } - if (!$result) { - $result = default_page_handler($page, $handler); - } - if ($result !== false) { - $result = true; + $handler = $request['handler']; + $page = $request['segments']; + + $result = false; + if (isset($CONFIG->pagehandler) + && !empty($handler) + && isset($CONFIG->pagehandler[$handler]) + && is_callable($CONFIG->pagehandler[$handler])) { + $function = $CONFIG->pagehandler[$handler]; + $result = call_user_func($function, $page, $handler); } - return $result; + return $result || headers_sent(); } /** * Registers a page handler for a particular identifier * * For example, you can register a function called 'blog_page_handler' for handler type 'blog' - * For all URLs http://yoururl/pg/blog/*, the blog_page_handler() function will be called. + * For all URLs http://yoururl/blog/*, the blog_page_handler() function will be called. * The part of the URL marked with * above will be exploded on '/' characters and passed as an * array to that function. * For example, the URL http://yoururl/blog/username/friends/ would result in the call: * blog_page_handler(array('username','friends'), blog); * - * Page handler functions should return true or the default page handler will be called. - * * A request to register a page handler with the same identifier as previously registered * handler will replace the previous one. * * The context is set to the page handler identifier before the registered * page handler function is called. For the above example, the context is set to 'blog'. * + * Page handlers should return true to indicate that they handled the request. + * Requests not handled are forwarded to the front page with a reason of 404. + * Plugins can register for the 'forward', '404' plugin hook. @see forward() + * * @param string $handler The page type to handle * @param string $function Your function name * - * @return true|false Depending on success + * @return bool Depending on success */ -function register_page_handler($handler, $function) { +function elgg_register_page_handler($handler, $function) { global $CONFIG; + if (!isset($CONFIG->pagehandler)) { $CONFIG->pagehandler = array(); } - if (is_callable($function)) { + if (is_callable($function, true)) { $CONFIG->pagehandler[$handler] = $function; return true; } @@ -88,14 +98,14 @@ function register_page_handler($handler, $function) { /** * Unregister a page handler for an identifier * - * Note: to replace a page handler, call register_page_handler() + * Note: to replace a page handler, call elgg_register_page_handler() * * @param string $handler The page type identifier * * @since 1.7.2 * @return void */ -function unregister_page_handler($handler) { +function elgg_unregister_page_handler($handler) { global $CONFIG; if (!isset($CONFIG->pagehandler)) { @@ -106,35 +116,35 @@ function unregister_page_handler($handler) { } /** - * A default page handler - * Tries to locate a suitable file to include. Only works for core pages, not plugins. + * Serve an error page * - * @param array $page The page URL elements - * @param string $handler The base handler + * @todo not sending status codes yet * - * @return true|false Depending on success + * @param string $hook The name of the hook + * @param string $type The type of the hook + * @param bool $result The current value of the hook + * @param array $params Parameters related to the hook + * @return void */ -function default_page_handler($page, $handler) { - global $CONFIG; - - $page = implode('/', $page); - - // protect against including arbitary files - $page = str_replace("..", "", $page); - - $callpath = $CONFIG->path . $handler . "/" . $page; - if (is_dir($callpath)) { - $callpath = sanitise_filepath($callpath); - $callpath .= "index.php"; - if (file_exists($callpath)) { - if (include($callpath)) { - return TRUE; - } - } - } else if (file_exists($callpath)) { - include($callpath); - return TRUE; +function elgg_error_page_handler($hook, $type, $result, $params) { + if (elgg_view_exists("errors/$type")) { + $content = elgg_view("errors/$type", $params); + } else { + $content = elgg_view("errors/default", $params); } + $body = elgg_view_layout('error', array('content' => $content)); + echo elgg_view_page('', $body, 'error'); + exit; +} - return FALSE; +/** + * Initializes the page handler/routing system + * + * @return void + * @access private + */ +function page_handler_init() { + elgg_register_plugin_hook_handler('forward', '404', 'elgg_error_page_handler'); } + +elgg_register_event_handler('init', 'system', 'page_handler_init'); diff --git a/engine/lib/pageowner.php b/engine/lib/pageowner.php index 649e77e40..bd63d08c6 100644 --- a/engine/lib/pageowner.php +++ b/engine/lib/pageowner.php @@ -13,7 +13,7 @@ * @param int $guid Optional parameter used by elgg_set_page_owner_guid(). * * @return int The current page owner guid (0 if none). - * @since 1.8 + * @since 1.8.0 */ function elgg_get_page_owner_guid($guid = 0) { static $page_owner_guid; @@ -26,100 +26,73 @@ function elgg_get_page_owner_guid($guid = 0) { return $page_owner_guid; } + // return guid of page owner entity $guid = elgg_trigger_plugin_hook('page_owner', 'system', NULL, 0); - $page_owner_guid = $guid; + if ($guid) { + $page_owner_guid = $guid; + } return $guid; } /** - * Gets the guid of the entity that owns the current page. - * - * @deprecated 1.8 Use elgg_get_page_owner_guid() - * - * @return int The current page owner guid (0 if none). - */ -function page_owner() { - elgg_deprecated_notice('page_owner() was deprecated by elgg_get_page_owner_guid().', 1.8); - return elgg_get_page_owner_guid(); -} - -/** * Gets the owner entity for the current page. * - * @return ElggEntity|false The current page owner or false if none. + * @note Access is disabled when getting the page owner entity. * - * @since 1.8 + * @return ElggUser|ElggGroup|false The current page owner or false if none. + * + * @since 1.8.0 */ -function elgg_get_page_owner() { +function elgg_get_page_owner_entity() { $guid = elgg_get_page_owner_guid(); if ($guid > 0) { - return get_entity($guid); - } + $ia = elgg_set_ignore_access(true); + $owner = get_entity($guid); + elgg_set_ignore_access($ia); - return FALSE; -} + return $owner; + } -/** - * Gets the owner entity for the current page. - * - * @deprecated 1.8 Use elgg_get_page_owner() - * @return ElggEntity|false The current page owner or false if none. - * - * @since 1.8 - */ -function page_owner_entity() { - elgg_deprecated_notice('page_owner_entity() was deprecated by elgg_get_page_owner().', 1.8); - return elgg_get_page_owner(); + return false; } /** * Set the guid of the entity that owns this page * * @param int $guid The guid of the page owner - * - * @since 1.8 + * @return void + * @since 1.8.0 */ function elgg_set_page_owner_guid($guid) { elgg_get_page_owner_guid($guid); } - /** - * Registers a page owner handler function + * Sets the page owner based on request * - * @param string $functionname The callback function + * Tries to figure out the page owner by looking at the URL or a request + * parameter. The request parameters used are 'username' and 'owner_guid'. If + * the page request is going through the page handling system, this function + * attempts to figure out the owner if the url fits the patterns of: + * <handler>/owner/<username> + * <handler>/friends/<username> + * <handler>/view/<entity guid> + * <handler>/add/<container guid> + * <handler>/edit/<entity guid> + * <handler>/group/<group guid> * - * @deprecated 1.8 Use the 'page_owner', 'system' plugin hook - * @return void - */ -function add_page_owner_handler($functionname) { - elgg_deprecated_notice("add_page_owner_handler() was deprecated by the plugin hook 'page_owner', 'system'.", 1.8); -} - -/** - * Set a page owner entity + * @note Access is disabled while finding the page owner for the group gatekeeper functions. * - * @param int $entitytoset The GUID of the entity - * - * @deprecated 1.8 Use elgg_set_page_owner_guid() - * @return void - */ -function set_page_owner($entitytoset = -1) { - elgg_deprecated_notice('set_page_owner() was deprecated by elgg_set_page_owner_guid().', 1.8); - elgg_set_page_owner_guid($entitytoset); -} - -/** - * Handles default page owners * - * @param string $hook page_owner - * @param string $entity_type system - * @param mixed $returnvalue Previous function's return value - * @param mixed $params Params + * @param string $hook 'page_owner' + * @param string $entity_type 'system' + * @param int $returnvalue Previous function's return value + * @param array $params no parameters * - * @return int + * @return int GUID + * @access private */ function default_page_owner_handler($hook, $entity_type, $returnvalue, $params) { @@ -127,17 +100,22 @@ function default_page_owner_handler($hook, $entity_type, $returnvalue, $params) return $returnvalue; } + $ia = elgg_set_ignore_access(true); + $username = get_input("username"); if ($username) { + // @todo using a username of group:<guid> is deprecated if (substr_count($username, 'group:')) { preg_match('/group\:([0-9]+)/i', $username, $matches); $guid = $matches[1]; if ($entity = get_entity($guid)) { + elgg_set_ignore_access($ia); return $entity->getGUID(); } } if ($user = get_user_by_username($username)) { + elgg_set_ignore_access($ia); return $user->getGUID(); } } @@ -145,11 +123,53 @@ function default_page_owner_handler($hook, $entity_type, $returnvalue, $params) $owner = get_input("owner_guid"); if ($owner) { if ($user = get_entity($owner)) { + elgg_set_ignore_access($ia); return $user->getGUID(); } } - return $returnvalue; + // ignore root and query + $uri = current_page_url(); + $path = str_replace(elgg_get_site_url(), '', $uri); + $path = trim($path, "/"); + if (strpos($path, "?")) { + $path = substr($path, 0, strpos($path, "?")); + } + + // @todo feels hacky + if (get_input('page', FALSE)) { + $segments = explode('/', $path); + if (isset($segments[1]) && isset($segments[2])) { + switch ($segments[1]) { + case 'owner': + case 'friends': + $user = get_user_by_username($segments[2]); + if ($user) { + elgg_set_ignore_access($ia); + return $user->getGUID(); + } + break; + case 'view': + case 'edit': + $entity = get_entity($segments[2]); + if ($entity) { + elgg_set_ignore_access($ia); + return $entity->getContainerGUID(); + } + break; + case 'add': + case 'group': + $entity = get_entity($segments[2]); + if ($entity) { + elgg_set_ignore_access($ia); + return $entity->getGUID(); + } + break; + } + } + } + + elgg_set_ignore_access($ia); } /** @@ -160,8 +180,8 @@ function default_page_owner_handler($hook, $entity_type, $returnvalue, $params) * output could be different for those two contexts ('blog' vs 'widget'). * * Pages that pass through the page handling system set the context to the - * first string after 'pg'. Example: http://elgg.org/pg/bookmarks/ results in - * the initial context being set to 'bookmarks'. + * first string after the root url. Example: http://example.org/elgg/bookmarks/ + * results in the initial context being set to 'bookmarks'. * * The context is a stack so that for a widget on a profile, the context stack * may contain first 'profile' and then 'widget'. @@ -171,9 +191,9 @@ function default_page_owner_handler($hook, $entity_type, $returnvalue, $params) * @warning The context is not available until the page_handler runs (after * the 'init, system' event processing has completed). * - * @param string $context The context of the page + * @param string $context The context of the page * @return bool - * @since 1.8 + * @since 1.8.0 */ function elgg_set_context($context) { global $CONFIG; @@ -198,11 +218,15 @@ function elgg_set_context($context) { * Since context is a stack, this is equivalent to a peek. * * @return string|NULL - * @since 1.8 + * @since 1.8.0 */ function elgg_get_context() { global $CONFIG; + if (!$CONFIG->context) { + return null; + } + return $CONFIG->context[count($CONFIG->context) - 1]; } @@ -210,7 +234,8 @@ function elgg_get_context() { * Push a context onto the top of the stack * * @param string $context The context string to add to the context stack - * @since 1.8 + * @return void + * @since 1.8.0 */ function elgg_push_context($context) { global $CONFIG; @@ -222,7 +247,7 @@ function elgg_push_context($context) { * Removes and returns the top context string from the stack * * @return string|NULL - * @since 1.8 + * @since 1.8.0 */ function elgg_pop_context() { global $CONFIG; @@ -238,9 +263,9 @@ function elgg_pop_context() { * itself differently based on being on the dashboard or profile pages, it * can check the stack. * - * @param string $context The context string to check for + * @param string $context The context string to check for * @return bool - * @since 1.8 + * @since 1.8.0 */ function elgg_in_context($context) { global $CONFIG; @@ -249,53 +274,24 @@ function elgg_in_context($context) { } /** - * Sets the functional context of a page - * - * @deprecated 1.8 Use elgg_set_context() - * - * @param string $context The context of the page - * - * @return mixed Either the context string, or false on failure - */ -function set_context($context) { - elgg_deprecated_notice('set_context() was deprecated by elgg_set_context().', 1.8); - elgg_set_context($context); - if (empty($context)) { - return false; - } - return $context; -} - -/** - * Returns the functional context of a page - * - * @deprecated 1.8 Use elgg_get_context() - * - * @return string The context, or 'main' if no context has been provided - */ -function get_context() { - elgg_deprecated_notice('get_context() was deprecated by elgg_get_context().', 1.8); - return elgg_get_context(); - - // @todo - used to set context based on calling script - // $context = get_plugin_name(true) -} - - -/** * Initializes the page owner functions * * @note This is on the 'boot, system' event so that the context is set up quickly. * * @return void + * @access private */ function page_owner_boot() { - global $CONFIG; elgg_register_plugin_hook_handler('page_owner', 'system', 'default_page_owner_handler'); - - // initial context - will be replaced by page handler - $CONFIG->context = array('main'); + + // Bootstrap the context stack by setting its first entry to the handler. + // This is the first segment of the URL and the handler is set by the rewrite rules. + // @todo this does not work for actions + $handler = get_input('handler', FALSE); + if ($handler) { + elgg_set_context($handler); + } } -elgg_register_event_handler('boot', 'system', 'page_owner_boot');
\ No newline at end of file +elgg_register_event_handler('boot', 'system', 'page_owner_boot'); diff --git a/engine/lib/pam.php b/engine/lib/pam.php index f6db28355..1c9c3bfe1 100644 --- a/engine/lib/pam.php +++ b/engine/lib/pam.php @@ -20,6 +20,7 @@ * @subpackage Authentication.PAM */ +global $_PAM_HANDLERS; $_PAM_HANDLERS = array(); /** @@ -29,7 +30,9 @@ $_PAM_HANDLERS = array(); * failure, return false or throw an exception. Returning nothing indicates that * the handler wants to be skipped. * - * @param string $handler The handler function in the format + * Note, $handler must be string callback (not an array/Closure). + * + * @param string $handler Callable global handler function in the format () * pam_handler($credentials = NULL); * @param string $importance The importance - "sufficient" (default) or "required" * @param string $policy The policy type, default is "user" @@ -44,7 +47,8 @@ function register_pam_handler($handler, $importance = "sufficient", $policy = "u $_PAM_HANDLERS[$policy] = array(); } - if (is_callable($handler)) { + // @todo remove requirement that $handle be a global function + if (is_string($handler) && is_callable($handler, true)) { $_PAM_HANDLERS[$policy][$handler] = new stdClass; $_PAM_HANDLERS[$policy][$handler]->handler = $handler; @@ -70,43 +74,3 @@ function unregister_pam_handler($handler, $policy = "user") { unset($_PAM_HANDLERS[$policy][$handler]); } - -function pam_authenticate($credentials = NULL, $policy = "user") { - elgg_deprecated_notice('pam_authenticate has been deprecated for ElggPAM', 1.8); - global $_PAM_HANDLERS, $_PAM_HANDLERS_MSG; - - $_PAM_HANDLERS_MSG = array(); - - $authenticated = false; - - foreach ($_PAM_HANDLERS[$policy] as $k => $v) { - $handler = $v->handler; - $importance = $v->importance; - - try { - // Execute the handler - if ($handler($credentials)) { - // Explicitly returned true - $_PAM_HANDLERS_MSG[$k] = "Authenticated!"; - - $authenticated = true; - } else { - $_PAM_HANDLERS_MSG[$k] = "Not Authenticated."; - - // If this is required then abort. - if ($importance == 'required') { - return false; - } - } - } catch (Exception $e) { - $_PAM_HANDLERS_MSG[$k] = "$e"; - - // If this is required then abort. - if ($importance == 'required') { - return false; - } - } - } - - return $authenticated; -}
\ No newline at end of file diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index 1c91a7776..d5d3db466 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -7,220 +7,538 @@ * @subpackage Plugins */ -/// Cache enabled plugins per page -$ENABLED_PLUGINS_CACHE = NULL; +/** + * Tells ElggPlugin::start() to include the start.php file. + */ +define('ELGG_PLUGIN_INCLUDE_START', 1); /** - * Returns a list of plugins to load, in the order that they should be loaded. + * Tells ElggPlugin::start() to automatically register the plugin's views. + */ +define('ELGG_PLUGIN_REGISTER_VIEWS', 2); + +/** + * Tells ElggPlugin::start() to automatically register the plugin's languages. + */ +define('ELGG_PLUGIN_REGISTER_LANGUAGES', 4); + +/** + * Tells ElggPlugin::start() to automatically register the plugin's classes. + */ +define('ELGG_PLUGIN_REGISTER_CLASSES', 8); + +/** + * Prefix for plugin setting names * - * @return array List of plugins + * @todo Can't namespace these because many plugins directly call + * private settings via $entity->$name. */ -function get_plugin_list() { - global $CONFIG; +//define('ELGG_PLUGIN_SETTING_PREFIX', 'plugin:setting:'); - if (!empty($CONFIG->pluginlistcache)) { - return $CONFIG->pluginlistcache; +/** + * Prefix for plugin user setting names + */ +define('ELGG_PLUGIN_USER_SETTING_PREFIX', 'plugin:user_setting:'); + +/** + * Internal settings prefix + * + * @todo This could be resolved by promoting ElggPlugin to a 5th type. + */ +define('ELGG_PLUGIN_INTERNAL_PREFIX', 'elgg:internal:'); + + +/** + * Returns a list of plugin IDs (dir names) from a dir. + * + * @param string $dir A dir to scan for plugins. Defaults to config's plugins_path. + * + * @return array + * @since 1.8.0 + * @access private + */ +function elgg_get_plugin_ids_in_dir($dir = null) { + if (!$dir) { + $dir = elgg_get_plugins_path(); } - if ($site = get_entity($CONFIG->site_guid)) { - $pluginorder = $site->pluginorder; - if (!empty($pluginorder)) { - $plugins = unserialize($pluginorder); + $plugin_ids = array(); + $handle = opendir($dir); - $CONFIG->pluginlistcache = $plugins; - return $plugins; - } else { - // this only runs on install, otherwise uses serialized plugin order - $plugins = array(); - - if ($handle = opendir($CONFIG->pluginspath)) { - while ($mod = readdir($handle)) { - // must be directory and not begin with a . - if (substr($mod, 0, 1) !== '.' && is_dir($CONFIG->pluginspath . "/" . $mod)) { - $plugins[] = $mod; - } - } + if ($handle) { + while ($plugin_id = readdir($handle)) { + // must be directory and not begin with a . + if (substr($plugin_id, 0, 1) !== '.' && is_dir($dir . $plugin_id)) { + $plugin_ids[] = $plugin_id; } + } + } + + sort($plugin_ids); + + return $plugin_ids; +} + +/** + * Discovers plugins in the plugins_path setting and creates ElggPlugin + * entities for them if they don't exist. If there are plugins with entities + * but not actual files, will disable the ElggPlugin entities and mark as inactive. + * The ElggPlugin object holds config data, so don't delete. + * + * @todo Crappy name? + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_generate_plugin_entities() { + // @todo $site unused, can remove? + $site = get_config('site'); + + $dir = elgg_get_plugins_path(); + $db_prefix = elgg_get_config('dbprefix'); + + $options = array( + 'type' => 'object', + 'subtype' => 'plugin', + 'selects' => array('plugin_oe.*'), + 'joins' => array("JOIN {$db_prefix}objects_entity plugin_oe on plugin_oe.guid = e.guid"), + 'limit' => ELGG_ENTITIES_NO_VALUE + ); + + $old_ia = elgg_set_ignore_access(true); + $old_access = access_get_show_hidden_status(); + access_show_hidden_entities(true); + $known_plugins = elgg_get_entities_from_relationship($options); + /* @var ElggPlugin[] $known_plugins */ + + if (!$known_plugins) { + $known_plugins = array(); + } + + // map paths to indexes + $id_map = array(); + foreach ($known_plugins as $i => $plugin) { + // if the ID is wrong, delete the plugin because we can never load it. + $id = $plugin->getID(); + if (!$id) { + $plugin->delete(); + unset($known_plugins[$i]); + continue; + } + $id_map[$plugin->getID()] = $i; + } + + $physical_plugins = elgg_get_plugin_ids_in_dir($dir); + + if (!$physical_plugins) { + return false; + } - sort($plugins); + // check real plugins against known ones + foreach ($physical_plugins as $plugin_id) { + // is this already in the db? + if (array_key_exists($plugin_id, $id_map)) { + $index = $id_map[$plugin_id]; + $plugin = $known_plugins[$index]; + // was this plugin deleted and its entity disabled? + if (!$plugin->isEnabled()) { + $plugin->enable(); + $plugin->deactivate(); + $plugin->setPriority('last'); + } - $CONFIG->pluginlistcache = $plugins; - return $plugins; + // remove from the list of plugins to disable + unset($known_plugins[$index]); + } else { + // add new plugins + // priority is force to last in save() if not set. + $plugin = new ElggPlugin($plugin_id); + $plugin->save(); } } + // everything remaining in $known_plugins needs to be disabled + // because they are entities, but their dirs were removed. + // don't delete the entities because they hold settings. + foreach ($known_plugins as $plugin) { + if ($plugin->isActive()) { + $plugin->deactivate(); + } + // remove the priority. + $name = elgg_namespace_plugin_private_setting('internal', 'priority'); + remove_private_setting($plugin->guid, $name); + $plugin->disable(); + } + + access_show_hidden_entities($old_access); + elgg_set_ignore_access($old_ia); + + elgg_reindex_plugin_priorities(); + + return true; +} + +/** + * Cache a reference to this plugin by its ID + * + * @param ElggPlugin $plugin + * + * @access private + */ +function _elgg_cache_plugin_by_id(ElggPlugin $plugin) { + $map = (array) elgg_get_config('plugins_by_id_map'); + $map[$plugin->getID()] = $plugin; + elgg_set_config('plugins_by_id_map', $map); +} + +/** + * Returns an ElggPlugin object with the path $path. + * + * @param string $plugin_id The id (dir name) of the plugin. NOT the guid. + * @return ElggPlugin|false + * @since 1.8.0 + */ +function elgg_get_plugin_from_id($plugin_id) { + $map = (array) elgg_get_config('plugins_by_id_map'); + if (isset($map[$plugin_id])) { + return $map[$plugin_id]; + } + + $plugin_id = sanitize_string($plugin_id); + $db_prefix = get_config('dbprefix'); + + $options = array( + 'type' => 'object', + 'subtype' => 'plugin', + 'joins' => array("JOIN {$db_prefix}objects_entity oe on oe.guid = e.guid"), + 'selects' => array("oe.title", "oe.description"), + 'wheres' => array("oe.title = '$plugin_id'"), + 'limit' => 1 + ); + + $plugins = elgg_get_entities($options); + + if ($plugins) { + return $plugins[0]; + } + return false; } /** - * Regenerates the list of known plugins and saves it to the current site + * Returns if a plugin exists in the system. * - * Important: You should regenerate simplecache and the viewpath cache after executing this function - * otherwise you may experience view display artifacts. Do this with the following code: + * @warning This checks only plugins that are registered in the system! + * If the plugin cache is outdated, be sure to regenerate it with + * {@link elgg_generate_plugin_objects()} first. * - * elgg_view_regenerate_simplecache(); - * elgg_filepath_cache_reset(); + * @param string $id The plugin ID. + * @since 1.8.0 + * @return bool + */ +function elgg_plugin_exists($id) { + $plugin = elgg_get_plugin_from_id($id); + + return ($plugin) ? true : false; +} + +/** + * Returns the highest priority of the plugins * - * @param array $pluginorder Optionally, a list of existing plugins and their orders + * @return int + * @since 1.8.0 + * @access private + */ +function elgg_get_max_plugin_priority() { + $db_prefix = get_config('dbprefix'); + $priority = elgg_namespace_plugin_private_setting('internal', 'priority'); + $plugin_subtype = get_subtype_id('object', 'plugin'); + + $q = "SELECT MAX(CAST(ps.value AS unsigned)) as max + FROM {$db_prefix}entities e, {$db_prefix}private_settings ps + WHERE ps.name = '$priority' + AND ps.entity_guid = e.guid + AND e.type = 'object' and e.subtype = $plugin_subtype"; + + $data = get_data($q); + if ($data) { + $max = $data[0]->max; + } else { + $max = 1; + } + + // can't have a priority of 0. + return ($max) ? $max : 1; +} + +/** + * Returns if a plugin is active for a current site. * - * @return array The new list of plugins and their orders + * @param string $plugin_id The plugin ID + * @param int $site_guid The site guid + * @since 1.8.0 + * @return bool */ -function regenerate_plugin_list($pluginorder = FALSE) { - global $CONFIG; +function elgg_is_active_plugin($plugin_id, $site_guid = null) { + if ($site_guid) { + $site = get_entity($site_guid); + } else { + $site = elgg_get_site_entity(); + } - $CONFIG->pluginlistcache = NULL; + if (!($site instanceof ElggSite)) { + return false; + } - if ($site = get_entity($CONFIG->site_guid)) { - if (empty($pluginorder)) { - $pluginorder = $site->pluginorder; - $pluginorder = unserialize($pluginorder); - } else { - ksort($pluginorder); - } + $plugin = elgg_get_plugin_from_id($plugin_id); - if (empty($pluginorder)) { - $pluginorder = array(); - } + if (!$plugin) { + return false; + } - $max = 0; - if (sizeof($pluginorder)) { - foreach ($pluginorder as $key => $plugin) { - if (is_dir($CONFIG->pluginspath . "/" . $plugin)) { - if ($key > $max) { - $max = $key; - } - } else { - unset($pluginorder[$key]); - } - } - } - // Add new plugins to the end - if ($handle = opendir($CONFIG->pluginspath)) { - while ($mod = readdir($handle)) { - // must be directory and not begin with a . - if (substr($mod, 0, 1) !== '.' && is_dir($CONFIG->pluginspath . "/" . $mod)) { - if (!in_array($mod, $pluginorder)) { - $max = $max + 10; - $pluginorder[$max] = $mod; - } - } - } + return $plugin->isActive($site->guid); +} + +/** + * Loads all active plugins in the order specified in the tool admin panel. + * + * @note This is called on every page load. If a plugin is active and problematic, it + * will be disabled and a visible error emitted. This does not check the deps system because + * that was too slow. + * + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_load_plugins() { + $plugins_path = elgg_get_plugins_path(); + $start_flags = ELGG_PLUGIN_INCLUDE_START | + ELGG_PLUGIN_REGISTER_VIEWS | + ELGG_PLUGIN_REGISTER_LANGUAGES | + ELGG_PLUGIN_REGISTER_CLASSES; + + if (!$plugins_path) { + return false; + } + + // temporary disable all plugins if there is a file called 'disabled' in the plugin dir + if (file_exists("$plugins_path/disabled")) { + if (elgg_is_admin_logged_in() && elgg_in_context('admin')) { + system_message(elgg_echo('plugins:disabled')); } + return false; + } + + if (elgg_get_config('system_cache_loaded')) { + $start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_VIEWS; + } - ksort($pluginorder); + if (elgg_get_config('i18n_loaded_from_cache')) { + $start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_LANGUAGES; + } + + $return = true; + $plugins = elgg_get_plugins('active'); + if ($plugins) { + foreach ($plugins as $plugin) { + try { + $plugin->start($start_flags); + } catch (Exception $e) { + $plugin->deactivate(); + $msg = elgg_echo('PluginException:CannotStart', + array($plugin->getID(), $plugin->guid, $e->getMessage())); + elgg_add_admin_notice('cannot_start' . $plugin->getID(), $msg); + $return = false; - // Now reorder the keys .. - $key = 10; - $plugins = array(); - if (sizeof($pluginorder)) { - foreach ($pluginorder as $plugin) { - $plugins[$key] = $plugin; - $key = $key + 10; + continue; } } + } - $plugins = serialize($plugins); + return $return; +} - $site->pluginorder = $plugins; +/** + * Returns an ordered list of plugins + * + * @param string $status The status of the plugins. active, inactive, or all. + * @param mixed $site_guid Optional site guid + * @return ElggPlugin[] + * @since 1.8.0 + * @access private + */ +function elgg_get_plugins($status = 'active', $site_guid = null) { + $db_prefix = get_config('dbprefix'); + $priority = elgg_namespace_plugin_private_setting('internal', 'priority'); - return $plugins; + if (!$site_guid) { + $site = get_config('site'); + $site_guid = $site->guid; } - return FALSE; -} + // grab plugins + $options = array( + 'type' => 'object', + 'subtype' => 'plugin', + 'limit' => ELGG_ENTITIES_NO_VALUE, + 'selects' => array('plugin_oe.*'), + 'joins' => array( + "JOIN {$db_prefix}private_settings ps on ps.entity_guid = e.guid", + "JOIN {$db_prefix}objects_entity plugin_oe on plugin_oe.guid = e.guid" + ), + 'wheres' => array("ps.name = '$priority'"), + 'order_by' => "CAST(ps.value as unsigned), e.guid" + ); + + switch ($status) { + case 'active': + $options['relationship'] = 'active_plugin'; + $options['relationship_guid'] = $site_guid; + $options['inverse_relationship'] = true; + break; + + case 'inactive': + $options['wheres'][] = "NOT EXISTS ( + SELECT 1 FROM {$db_prefix}entity_relationships active_er + WHERE active_er.guid_one = e.guid + AND active_er.relationship = 'active_plugin' + AND active_er.guid_two = $site_guid)"; + break; + + case 'all': + default: + break; + } + + $old_ia = elgg_set_ignore_access(true); + $plugins = elgg_get_entities_from_relationship($options); + elgg_set_ignore_access($old_ia); + return $plugins; +} /** - * For now, loads plugins directly + * Reorder plugins to an order specified by the array. + * Plugins not included in this array will be appended to the end. * - * @todo Add proper plugin handler that launches plugins in an - * admin-defined order and activates them on admin request + * @note This doesn't use the ElggPlugin->setPriority() method because + * all plugins are being changed and we don't want it to automatically + * reorder plugins. * - * @return void + * @param array $order An array of plugin ids in the order to set them + * @return bool + * @since 1.8.0 + * @access private */ -function load_plugins() { - global $CONFIG; +function elgg_set_plugin_priorities(array $order) { + $name = elgg_namespace_plugin_private_setting('internal', 'priority'); - if (!empty($CONFIG->pluginspath)) { - // See if we have cached values for things - $cached_view_paths = elgg_filepath_cache_load(); - if ($cached_view_paths) { - $CONFIG->views = unserialize($cached_view_paths); + $plugins = elgg_get_plugins('any'); + if (!$plugins) { + return false; + } + + $return = true; + + // reindex to get standard counting. no need to increment by 10. + // though we do start with 1 + $order = array_values($order); + + $missing_plugins = array(); + foreach ($plugins as $plugin) { + $plugin_id = $plugin->getID(); + + if (!in_array($plugin_id, $order)) { + $missing_plugins[] = $plugin; + continue; } - // temporary disable all plugins if there is a file called 'disabled' in the plugin dir - if (file_exists($CONFIG->pluginspath . "disabled")) { - return; + $priority = array_search($plugin_id, $order) + 1; + + if (!$plugin->set($name, $priority)) { + $return = false; + break; } + } - $plugins = get_plugin_list(); - - if (sizeof($plugins)) { - foreach ($plugins as $mod) { - if (is_plugin_enabled($mod)) { - if (file_exists($CONFIG->pluginspath . $mod)) { - if (!include($CONFIG->pluginspath . $mod . "/start.php")) { - // automatically disable the bad plugin - disable_plugin($mod); - - // register error rather than rendering the site unusable with exception - register_error(elgg_echo('PluginException:MisconfiguredPlugin', array($mod))); - - // continue loading remaining plugins - continue; - } - - if (!$cached_view_paths) { - $view_dir = $CONFIG->pluginspath . $mod . '/views/'; - - if (is_dir($view_dir) && ($handle = opendir($view_dir))) { - while (FALSE !== ($view_type = readdir($handle))) { - $view_type_dir = $view_dir . $view_type; - - if ('.' !== substr($view_type, 0, 1) && is_dir($view_type_dir)) { - if (autoregister_views('', $view_type_dir, $view_dir, $view_type)) { - // add the valid view type. - if (!in_array($view_type, $CONFIG->view_types)) { - $CONFIG->view_types[] = $view_type; - } - } - } - } - } - } - - if (is_dir($CONFIG->pluginspath . $mod . "/languages")) { - register_translations($CONFIG->pluginspath . $mod . "/languages/"); - } - - if (is_dir($CONFIG->pluginspath . "$mod/classes")) { - elgg_register_classes($CONFIG->pluginspath . "$mod/classes"); - } - } - } + // set the missing plugins' priorities + if ($return && $missing_plugins) { + if (!isset($priority)) { + $priority = 0; + } + foreach ($missing_plugins as $plugin) { + $priority++; + if (!$plugin->set($name, $priority)) { + $return = false; + break; } } + } - // Cache results - if (!$cached_view_paths) { - elgg_filepath_cache_save(serialize($CONFIG->views)); - } + return $return; +} + +/** + * Reindexes all plugin priorities starting at 1. + * + * @todo Can this be done in a single sql command? + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_reindex_plugin_priorities() { + return elgg_set_plugin_priorities(array()); +} + +/** + * Namespaces a string to be used as a private setting for a plugin. + * + * @param string $type The type of value: user_setting or internal. + * @param string $name The name to namespace. + * @param string $id The plugin's ID to namespace with. Required for user_setting. + * @return string + * @since 1.8.0 + * @access private + */ +function elgg_namespace_plugin_private_setting($type, $name, $id = null) { + switch ($type) { + // commented out because it breaks $plugin->$name access to variables + //case 'setting': + // $name = ELGG_PLUGIN_SETTING_PREFIX . $name; + // break; + + case 'user_setting': + if (!$id) { + $id = elgg_get_calling_plugin_id(); + } + $name = ELGG_PLUGIN_USER_SETTING_PREFIX . "$id:$name"; + break; + + case 'internal': + $name = ELGG_PLUGIN_INTERNAL_PREFIX . $name; + break; } + + return $name; } /** * Get the name of the most recent plugin to be called in the * call stack (or the plugin that owns the current page, if any). * - * i.e., if the last plugin was in /mod/foobar/, get_plugin_name would return foo_bar. + * i.e., if the last plugin was in /mod/foobar/, this would return foo_bar. * * @param boolean $mainfilename If set to true, this will instead determine the * context from the main script filename called by * the browser. Default = false. * * @return string|false Plugin name, or false if no plugin name was called + * @since 1.8.0 + * @access private + * + * @todo get rid of this */ -function get_plugin_name($mainfilename = false) { +function elgg_get_calling_plugin_id($mainfilename = false) { if (!$mainfilename) { if ($backtrace = debug_backtrace()) { foreach ($backtrace as $step) { @@ -233,8 +551,9 @@ function get_plugin_name($mainfilename = false) { } } } else { - if (preg_match("/pg\/([a-zA-Z0-9\-\_]*)\//", $_SERVER['REQUEST_URI'], $matches)) { - return $matches[1]; + //@todo this is a hack -- plugins do not have to match their page handler names! + if ($handler = get_input('handler', FALSE)) { + return $handler; } else { $file = $_SERVER["SCRIPT_NAME"]; $file = str_replace("\\", "/", $file); @@ -248,64 +567,6 @@ function get_plugin_name($mainfilename = false) { } /** - * Load and parse a plugin manifest from a plugin XML file. - * - * Example file: - * - * <plugin_manifest> - * <!-- Basic information --> - * <field key="name" value="My Plugin" /> - * <field key="description" value="My Plugin's concise description" /> - * <field key="version" value="1.0" /> - * <field key="category" value="theme" /> - * <field key="category" value="bundled" /> - * <field key="screenshot" value="path/relative/to/my_plugin.jpg" /> - * <field key="screenshot" value="path/relative/to/my_plugin_2.jpg" /> - * - * <field key="author" value="Curverider Ltd" /> - * <field key="website" value="http://www.elgg.org/" /> - * <field key="copyright" value="(C) Curverider 2008-2010" /> - * <field key="licence" value="GNU Public License version 2" /> - * </plugin_manifest> - * - * @param string $plugin Plugin name. - * - * @return array of values - */ -function load_plugin_manifest($plugin) { - $xml_file = get_config('pluginspath') . "$plugin/manifest.xml"; - - try { - $manifest = new ElggPluginManifest($xml_file, $plugin); - } catch(Exception $e) { - return false; - } - - return $manifest->getManifest(); -} - -/** - * This function checks a plugin manifest 'elgg_version' value against the current install - * returning TRUE if the elgg_version is >= the current install's version. - * - * @param string $manifest_elgg_version_string The build version (eg 2009010201). - * - * @return bool - */ -function check_plugin_compatibility($manifest_elgg_version_string) { - $version = get_version(); - - if (strpos($manifest_elgg_version_string, '.') === false) { - // Using version - $req_version = (int)$manifest_elgg_version_string; - - return ($version >= $req_version); - } - - return false; -} - -/** * Returns an array of all provides from all active plugins. * * Array in the form array( @@ -321,23 +582,27 @@ function check_plugin_compatibility($manifest_elgg_version_string) { * @param string $name A specific provided name to return. Requires $provide_type. * * @return array + * @since 1.8.0 + * @access private */ function elgg_get_plugins_provides($type = null, $name = null) { static $provides = null; - $active_plugins = get_installed_plugins('enabled'); + $active_plugins = elgg_get_plugins('active'); if (!isset($provides)) { $provides = array(); - foreach ($active_plugins as $plugin_id => $plugin_info) { - // @todo remove this when fully converted to ElggPluginPackage. - $package = new ElggPluginPackage($plugin_id); - - if ($plugin_provides = $package->getManifest()->getProvides()) { + foreach ($active_plugins as $plugin) { + $plugin_provides = array(); + $manifest = $plugin->getManifest(); + if ($manifest instanceof ElggPluginManifest) { + $plugin_provides = $plugin->getManifest()->getProvides(); + } + if ($plugin_provides) { foreach ($plugin_provides as $provided) { $provides[$provided['type']][$provided['name']] = array( 'version' => $provided['version'], - 'provided_by' => $plugin_id + 'provided_by' => $plugin->getID() ); } } @@ -370,579 +635,468 @@ function elgg_get_plugins_provides($type = null, $name = null) { * @param string $version A version to check against * @param string $comparison The comparison operator to use in version_compare() * - * @return bool + * @return array An array in the form array( + * 'status' => bool Does the provide exist?, + * 'value' => string The version provided + * ) + * @since 1.8.0 + * @access private */ function elgg_check_plugins_provides($type, $name, $version = null, $comparison = 'ge') { - if (!$provided = elgg_get_plugins_provides($type, $name)) { - return false; + $provided = elgg_get_plugins_provides($type, $name); + if (!$provided) { + return array( + 'status' => false, + 'version' => '' + ); } - if ($provided) { - if ($version) { - return version_compare($provided['version'], $version, $comparison); - } else { - return true; - } + if ($version) { + $status = version_compare($provided['version'], $version, $comparison); + } else { + $status = true; } + + return array( + 'status' => $status, + 'value' => $provided['version'] + ); } /** - * Shorthand function for finding the plugin settings. - * - * @param string $plugin_name Optional plugin name, if not specified - * then it is detected from where you are calling from. + * Returns an array of parsed strings for a dependency in the + * format: array( + * 'type' => requires, conflicts, or provides. + * 'name' => The name of the requirement / conflict + * 'value' => A string representing the expected value: <1, >=3, !=enabled + * 'local_value' => The current value, ("Not installed") + * 'comment' => Free form text to help resovle the problem ("Enable / Search for plugin <link>") + * ) * - * @return mixed + * @param array $dep An ElggPluginPackage dependency array + * @return array + * @since 1.8.0 + * @access private */ -function find_plugin_settings($plugin_name = "") { - $options = array('type' => 'object', 'subtype' => 'plugin', 'limit' => 9999); - $plugins = elgg_get_entities($options); - $plugin_name = sanitise_string($plugin_name); - if (!$plugin_name) { - $plugin_name = get_plugin_name(); +function elgg_get_plugin_dependency_strings($dep) { + $dep_system = elgg_extract('type', $dep); + $info = elgg_extract('dep', $dep); + $type = elgg_extract('type', $info); + + if (!$dep_system || !$info || !$type) { + return false; } - if ($plugins) { - foreach ($plugins as $plugin) { - if (strcmp($plugin->title, $plugin_name) == 0) { - return $plugin; + // rewrite some of these to be more readable + switch($info['comparison']) { + case 'lt': + $comparison = '<'; + break; + case 'gt': + $comparison = '>'; + break; + case 'ge': + $comparison = '>='; + break; + case 'le': + $comparison = '<='; + break; + default; + $comparison = $info['comparison']; + break; + } + + /* + 'requires' 'plugin oauth_lib' <1.3 1.3 'downgrade' + 'requires' 'php setting bob' >3 3 'change it' + 'conflicts' 'php setting' >3 4 'change it' + 'conflicted''plugin profile' any 1.8 'disable profile' + 'provides' 'plugin oauth_lib' 1.3 -- -- + 'priority' 'before blog' -- after 'move it' + */ + $strings = array(); + $strings['type'] = elgg_echo('ElggPlugin:Dependencies:' . ucwords($dep_system)); + + switch ($type) { + case 'elgg_version': + case 'elgg_release': + // 'Elgg Version' + $strings['name'] = elgg_echo('ElggPlugin:Dependencies:Elgg'); + $strings['expected_value'] = "$comparison {$info['version']}"; + $strings['local_value'] = $dep['value']; + $strings['comment'] = ''; + break; + + case 'php_extension': + // PHP Extension %s [version] + $strings['name'] = elgg_echo('ElggPlugin:Dependencies:PhpExtension', array($info['name'])); + if ($info['version']) { + $strings['expected_value'] = "$comparison {$info['version']}"; + $strings['local_value'] = $dep['value']; + } else { + $strings['expected_value'] = ''; + $strings['local_value'] = ''; } + $strings['comment'] = ''; + break; + + case 'php_ini': + $strings['name'] = elgg_echo('ElggPlugin:Dependencies:PhpIni', array($info['name'])); + $strings['expected_value'] = "$comparison {$info['value']}"; + $strings['local_value'] = $dep['value']; + $strings['comment'] = ''; + break; + + case 'plugin': + $strings['name'] = elgg_echo('ElggPlugin:Dependencies:Plugin', array($info['name'])); + $expected = $info['version'] ? "$comparison {$info['version']}" : elgg_echo('any'); + $strings['expected_value'] = $expected; + $strings['local_value'] = $dep['value'] ? $dep['value'] : '--'; + $strings['comment'] = ''; + break; + + case 'priority': + $expected_priority = ucwords($info['priority']); + $real_priority = ucwords($dep['value']); + $strings['name'] = elgg_echo('ElggPlugin:Dependencies:Priority'); + $strings['expected_value'] = elgg_echo("ElggPlugin:Dependencies:Priority:$expected_priority", array($info['plugin'])); + $strings['local_value'] = elgg_echo("ElggPlugin:Dependencies:Priority:$real_priority", array($info['plugin'])); + $strings['comment'] = ''; + break; + } + + if ($dep['type'] == 'suggests') { + if ($dep['status']) { + $strings['comment'] = elgg_echo('ok'); + } else { + $strings['comment'] = elgg_echo('ElggPlugin:Dependencies:Suggests:Unsatisfied'); + } + } else { + if ($dep['status']) { + $strings['comment'] = elgg_echo('ok'); + } else { + $strings['comment'] = elgg_echo('error'); } } - return false; + return $strings; } /** - * Find the plugin settings for a user. - * - * @param string $plugin_name Plugin name. - * @param int $user_guid The guid who's settings to retrieve. + * Returns the ElggPlugin entity of the last plugin called. * - * @return array of settings in an associative array minus prefix. + * @return mixed ElggPlugin or false + * @since 1.8.0 + * @access private */ -function find_plugin_usersettings($plugin_name = "", $user_guid = 0) { - $plugin_name = sanitise_string($plugin_name); - $user_guid = (int)$user_guid; +function elgg_get_calling_plugin_entity() { + $plugin_id = elgg_get_calling_plugin_id(); - if (!$plugin_name) { - $plugin_name = get_plugin_name(); + if ($plugin_id) { + return elgg_get_plugin_from_id($plugin_id); } - if ($user_guid == 0) { - $user_guid = get_loggedin_userid(); + return false; +} + +/** + * Returns an array of all plugin settings for a user. + * + * @param mixed $user_guid The user GUID or null for the currently logged in user. + * @param string $plugin_id The plugin ID + * @param bool $return_obj Return settings as an object? This can be used to in reusable + * views where the settings are passed as $vars['entity']. + * @return array + * @since 1.8.0 + */ +function elgg_get_all_plugin_user_settings($user_guid = null, $plugin_id = null, $return_obj = false) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - // Get metadata for user - $all_metadata = get_all_private_settings($user_guid); //get_metadata_for_entity($user_guid); - if ($all_metadata) { - $prefix = "plugin:settings:$plugin_name:"; - $return = new stdClass; + if (!$plugin instanceof ElggPlugin) { + return false; + } - foreach ($all_metadata as $key => $meta) { - $name = substr($key, strlen($prefix)); - $value = $meta; + $settings = $plugin->getAllUserSettings($user_guid); - if (strpos($key, $prefix) === 0) { - $return->$name = $value; - } + if ($settings && $return_obj) { + $return = new stdClass; + + foreach ($settings as $k => $v) { + $return->$k = $v; } return $return; + } else { + return $settings; } - - return false; } /** * Set a user specific setting for a plugin. * - * @param string $name The name - note, can't be "title". - * @param mixed $value The value. - * @param int $user_guid Optional user. - * @param string $plugin_name Optional plugin name, if not specified then it - * is detected from where you are calling from. + * @param string $name The name - note, can't be "title". + * @param mixed $value The value. + * @param int $user_guid Optional user. + * @param string $plugin_id Optional plugin name, if not specified then it + * is detected from where you are calling from. * * @return bool + * @since 1.8.0 */ -function set_plugin_usersetting($name, $value, $user_guid = 0, $plugin_name = "") { - $plugin_name = sanitise_string($plugin_name); - $user_guid = (int)$user_guid; - $name = sanitise_string($name); - - if (!$plugin_name) { - $plugin_name = get_plugin_name(); - } - - $user = get_entity($user_guid); - if (!$user) { - $user = get_loggedin_user(); +function elgg_set_plugin_user_setting($name, $value, $user_guid = null, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - if (($user) && ($user instanceof ElggUser)) { - $prefix = "plugin:settings:$plugin_name:$name"; - //$user->$prefix = $value; - //$user->save(); - - // Hook to validate setting - $value = elgg_trigger_plugin_hook('plugin:usersetting', 'user', array( - 'user' => $user, - 'plugin' => $plugin_name, - 'name' => $name, - 'value' => $value - ), $value); - - return set_private_setting($user->guid, $prefix, $value); + if (!$plugin) { + return false; } - return false; + return $plugin->setUserSetting($name, $value, $user_guid); } /** - * Clears a user-specific plugin setting + * Unsets a user-specific plugin setting * - * @param str $name Name of the plugin setting - * @param int $user_guid Defaults to logged in user - * @param str $plugin_name Defaults to contextual plugin name + * @param string $name Name of the setting + * @param int $user_guid Defaults to logged in user + * @param string $plugin_id Defaults to contextual plugin name * - * @return bool Success + * @return bool + * @since 1.8.0 */ -function clear_plugin_usersetting($name, $user_guid = 0, $plugin_name = '') { - $plugin_name = sanitise_string($plugin_name); - $name = sanitise_string($name); - - if (!$plugin_name) { - $plugin_name = get_plugin_name(); - } - - $user = get_entity((int) $user_guid); - if (!$user) { - $user = get_loggedin_user(); +function elgg_unset_plugin_user_setting($name, $user_guid = null, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - if (($user) && ($user instanceof ElggUser)) { - $prefix = "plugin:settings:$plugin_name:$name"; - - return remove_private_setting($user->getGUID(), $prefix); + if (!$plugin) { + return false; } - return FALSE; + return $plugin->unsetUserSetting($name, $user_guid); } /** * Get a user specific setting for a plugin. * - * @param string $name The name. - * @param int $user_guid Guid of owning user - * @param string $plugin_name Optional plugin name, if not specified - * then it is detected from where you are calling from. + * @param string $name The name of the setting. + * @param int $user_guid Guid of owning user + * @param string $plugin_id Optional plugin name, if not specified + * it is detected from where you are calling. * * @return mixed + * @since 1.8.0 */ -function get_plugin_usersetting($name, $user_guid = 0, $plugin_name = "") { - $plugin_name = sanitise_string($plugin_name); - $user_guid = (int)$user_guid; - $name = sanitise_string($name); - - if (!$plugin_name) { - $plugin_name = get_plugin_name(); - } - - $user = get_entity($user_guid); - if (!$user) { - $user = get_loggedin_user(); +function elgg_get_plugin_user_setting($name, $user_guid = null, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - if (($user) && ($user instanceof ElggUser)) { - $prefix = "plugin:settings:$plugin_name:$name"; - return get_private_setting($user->guid, $prefix); + if (!$plugin) { + return false; } - return false; + return $plugin->getUserSetting($name, $user_guid); } /** * Set a setting for a plugin. * - * @param string $name The name - note, can't be "title". - * @param mixed $value The value. - * @param string $plugin_name Optional plugin name, if not specified - * then it is detected from where you are calling from. + * @param string $name The name of the setting - note, can't be "title". + * @param mixed $value The value. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. * - * @return int|false + * @return bool + * @since 1.8.0 */ -function set_plugin_setting($name, $value, $plugin_name = "") { - if (!$plugin_name) { - $plugin_name = get_plugin_name(); +function elgg_set_plugin_setting($name, $value, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - $plugin = find_plugin_settings($plugin_name); if (!$plugin) { - $plugin = new ElggPlugin(); - } - - if ($name != 'title') { - // Hook to validate setting - $value = elgg_trigger_plugin_hook('plugin:setting', 'plugin', array( - 'plugin' => $plugin_name, - 'name' => $name, - 'value' => $value - ), $value); - - $plugin->title = $plugin_name; - $plugin->access_id = ACCESS_PUBLIC; - $plugin->save(); - $plugin->$name = $value; - - return $plugin->getGUID(); + return false; } - return false; + return $plugin->setSetting($name, $value); } /** * Get setting for a plugin. * - * @param string $name The name. - * @param string $plugin_name Optional plugin name, if not specified - * then it is detected from where you are calling from. + * @param string $name The name of the setting. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. * * @return mixed + * @since 1.8.0 + * @todo make $plugin_id required in future version */ -function get_plugin_setting($name, $plugin_name = "") { - $plugin = find_plugin_settings($plugin_name); +function elgg_get_plugin_setting($name, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); + } - if ($plugin) { - return $plugin->$name; + if (!$plugin) { + return false; } - return false; + return $plugin->getSetting($name); } /** - * Clear a plugin setting. + * Unsets a plugin setting. * - * @param string $name The name. - * @param string $plugin_name Optional plugin name, if not specified - * then it is detected from where you are calling from. + * @param string $name The name of the setting. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. * * @return bool + * @since 1.8.0 */ -function clear_plugin_setting($name, $plugin_name = "") { - $plugin = find_plugin_settings($plugin_name); +function elgg_unset_plugin_setting($name, $plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); + } - if ($plugin) { - return remove_private_setting($plugin->guid, $name); + if (!$plugin) { + return false; } - return FALSE; + return $plugin->unsetSetting($name); } /** - * Clear all plugin settings. + * Unsets all plugin settings for a plugin. * - * @param string $plugin_name Optional plugin name, if not specified - * then it is detected from where you are calling from. + * @param string $plugin_id Optional plugin name, if not specified + * then it is detected from where you are calling from. * * @return bool - * @since 1.7.0 + * @since 1.8.0 */ -function clear_all_plugin_settings($plugin_name = "") { - $plugin = find_plugin_settings($plugin_name); - - if ($plugin) { - return remove_all_private_settings($plugin->guid); +function elgg_unset_all_plugin_settings($plugin_id = null) { + if ($plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + } else { + $plugin = elgg_get_calling_plugin_entity(); } - return FALSE; -} - -/** - * Return an array of installed plugins. - * - * @param string $status any|enabled|disabled - * @return array - */ -function get_installed_plugins($status = 'any') { - global $CONFIG; - - $installed_plugins = array(); - - if (!empty($CONFIG->pluginspath)) { - $plugins = get_plugin_list(); - - foreach ($plugins as $mod) { - // require manifest. - if (!$manifest = load_plugin_manifest($mod)) { - continue; - } - - $enabled = is_plugin_enabled($mod); - - switch ($status) { - case 'enabled': - if ($enabled != true) { - continue 2; - } - break; - - case 'disabled': - if ($enabled == true) { - continue 2; - } - break; - - case 'any': - default: - break; - } - - $installed_plugins[$mod] = array(); - $installed_plugins[$mod]['active'] = $enabled; - $installed_plugins[$mod]['manifest'] = $manifest; - } + if (!$plugin) { + return false; } - return $installed_plugins; + return $plugin->unsetAllSettings(); } /** - * Enable a plugin for a site (default current site) + * Returns entities based upon plugin settings. + * Takes all the options for {@see elgg_get_entities_from_private_settings()} + * in addition to the ones below. * - * Important: You should regenerate simplecache and the viewpath cache after executing this function - * otherwise you may experience view display artifacts. Do this with the following code: + * @param array $options Array in the format: * - * elgg_view_regenerate_simplecache(); - * elgg_filepath_cache_reset(); + * plugin_id => NULL|STR The plugin id. Defaults to calling plugin * - * @param string $plugin The plugin name. - * @param int $site_guid The site id, if not specified then this is detected. + * plugin_user_setting_names => NULL|ARR private setting names * - * @return array - * @throws InvalidClassException - */ -function enable_plugin($plugin, $site_guid = 0) { - global $CONFIG, $ENABLED_PLUGINS_CACHE; - - $plugin = sanitise_string($plugin); - $site_guid = (int) $site_guid; - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; - } - - $site = get_entity($site_guid); - if (!($site instanceof ElggSite)) { - $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($site_guid, "ElggSite")); - throw new InvalidClassException($msg); - } - - if (!$plugin_info = load_plugin_manifest($plugin)) { - return FALSE; - } - - // getMetadata() doesn't return an array if only one plugin is enabled - if ($enabled = $site->enabled_plugins) { - if (!is_array($enabled)) { - $enabled = array($enabled); - } - } else { - $enabled = array(); - } - - $enabled[] = $plugin; - $enabled = array_unique($enabled); - - if ($return = $site->setMetaData('enabled_plugins', $enabled)) { - - // for other plugins that want to hook into this. - $params = array('plugin' => $plugin, 'manifest' => $plugin_info); - if ($return && !elgg_trigger_event('enable', 'plugin', $params)) { - $return = FALSE; - } - - // for this plugin's on_enable - if ($return && isset($plugin_info['on_enable'])) { - // pull in the actual plugin's start so the on_enable function is callable - // NB: this will not run re-run the init hooks! - $start = "{$CONFIG->pluginspath}$plugin/start.php"; - if (!file_exists($start) || !include($start)) { - $return = FALSE; - } - - // need language files for the messages - $translations = "{$CONFIG->pluginspath}$plugin/languages/"; - register_translations($translations); - if (!is_callable($plugin_info['on_enable'])) { - $return = FALSE; - } else { - $on_enable = call_user_func($plugin_info['on_enable']); - // allow null to mean "I don't care" like other subsystems - $return = ($on_disable === FALSE) ? FALSE : TRUE; - } - } - - // disable the plugin if the on_enable or trigger results failed - if (!$return) { - array_pop($enabled); - $site->setMetaData('enabled_plugins', $enabled); - } - - $ENABLED_PLUGINS_CACHE = $enabled; - } - - return $return; -} - -/** - * Disable a plugin for a site (default current site) - * - * Important: You should regenerate simplecache and the viewpath cache after executing this function - * otherwise you may experience view display artifacts. Do this with the following code: + * plugin_user_setting_values => NULL|ARR metadata values * - * elgg_view_regenerate_simplecache(); - * elgg_filepath_cache_reset(); + * plugin_user_setting_name_value_pairs => NULL|ARR ( + * name => 'name', + * value => 'value', + * 'operand' => '=', + * ) + * Currently if multiple values are sent via + * an array (value => array('value1', 'value2') + * the pair's operand will be forced to "IN". * - * @param string $plugin The plugin name. - * @param int $site_guid The site id, if not specified then this is detected. + * plugin_user_setting_name_value_pairs_operator => NULL|STR The operator to use for combining + * (name = value) OPERATOR (name = value); default AND * - * @return bool - * @throws InvalidClassException + * @return mixed int If count, int. If not count, array. false on errors. */ -function disable_plugin($plugin, $site_guid = 0) { - global $CONFIG, $ENABLED_PLUGINS_CACHE; - - $plugin = sanitise_string($plugin); - $site_guid = (int) $site_guid; - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; - } - - $site = get_entity($site_guid); - if (!($site instanceof ElggSite)) { - $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($site_guid, "ElggSite")); - throw new InvalidClassException(); +function elgg_get_entities_from_plugin_user_settings(array $options = array()) { + // if they're passing it don't bother + if (!isset($options['plugin_id'])) { + $options['plugin_id'] = elgg_get_calling_plugin_id(); } - if (!$plugin_info = load_plugin_manifest($plugin)) { - return FALSE; - } + $singulars = array('plugin_user_setting_name', 'plugin_user_setting_value', + 'plugin_user_setting_name_value_pair'); - // getMetadata() doesn't return an array if only one plugin is enabled - if ($enabled = $site->enabled_plugins) { - if (!is_array($enabled)) { - $enabled = array($enabled); - } - } else { - $enabled = array(); - } + $options = elgg_normalise_plural_options_array($options, $singulars); - $old_enabled = $enabled; + // rewrite plugin_user_setting_name_* to the right PS ones. + $map = array( + 'plugin_user_setting_names' => 'private_setting_names', + 'plugin_user_setting_values' => 'private_setting_values', + 'plugin_user_setting_name_value_pairs' => 'private_setting_name_value_pairs', + 'plugin_user_setting_name_value_pairs_operator' => 'private_setting_name_value_pairs_operator' + ); - // remove the disabled plugin from the array - if (FALSE !== $i = array_search($plugin, $enabled)) { - unset($enabled[$i]); - } - - // if we're unsetting all the plugins, this will return an empty array. - // it will fail with FALSE, though. - $return = (FALSE === $site->enabled_plugins = $enabled) ? FALSE : TRUE; - - if ($return) { - // for other plugins that want to hook into this. - $params = array('plugin' => $plugin, 'manifest' => $plugin_info); - if ($return && !elgg_trigger_event('disable', 'plugin', $params)) { - $return = FALSE; + foreach ($map as $plugin => $private) { + if (!isset($options[$plugin])) { + continue; } - // for this plugin's on_disable - if ($return && isset($plugin_info['on_disable'])) { - if (!is_callable($plugin_info['on_disable'])) { - $return = FALSE; - } else { - $on_disable = call_user_func($plugin_info['on_disable']); - // allow null to mean "I don't care" like other subsystems - $return = ($on_disable === FALSE) ? FALSE : TRUE; + if (isset($options[$private])) { + if (!is_array($options[$private])) { + $options[$private] = array($options[$private]); } - } - // disable the plugin if the on_enable or trigger results failed - if (!$return) { - $site->enabled_plugins = $old_enabled; - $ENABLED_PLUGINS_CACHE = $old_enabled; + $options[$private] = array_merge($options[$private], $options[$plugin]); } else { - $ENABLED_PLUGINS_CACHE = $enabled; + $options[$private] = $options[$plugin]; } } - return $return; -} - -/** - * Return whether a plugin is enabled or not. - * - * @param string $plugin The plugin name. - * @param int $site_guid The site id, if not specified then this is detected. - * - * @return bool - * @throws InvalidClassException - */ -function is_plugin_enabled($plugin, $site_guid = 0) { - global $CONFIG, $ENABLED_PLUGINS_CACHE; - - if (!file_exists($CONFIG->pluginspath . $plugin)) { - return false; - } - - $site_guid = (int) $site_guid; - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; - } - - if (!$ENABLED_PLUGINS_CACHE) { - $site = get_entity($site_guid); - if (!($site instanceof ElggSite)) { - $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($site_guid, "ElggSite")); - throw new InvalidClassException($msg); - } - - $enabled_plugins = $site->enabled_plugins; - if ($enabled_plugins && !is_array($enabled_plugins)) { - $enabled_plugins = array($enabled_plugins); - } - $ENABLED_PLUGINS_CACHE = $enabled_plugins; - } - if (is_array($ENABLED_PLUGINS_CACHE)) { - foreach ($ENABLED_PLUGINS_CACHE as $e) { - if ($e == $plugin) { - return true; - } - } - } + $plugin_id = $options['plugin_id']; + $prefix = elgg_namespace_plugin_private_setting('user_setting', '', $plugin_id); + $options['private_setting_name_prefix'] = $prefix; - return false; + return elgg_get_entities_from_private_settings($options); } /** * Register object, plugin entities as ElggPlugin classes * - * @return void + * @return void + * @access private */ function plugin_run_once() { - // Register a class add_subtype("object", "plugin", "ElggPlugin"); } - /** * 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 plugins_test($hook, $type, $value, $params) { global $CONFIG; @@ -951,25 +1105,75 @@ function plugins_test($hook, $type, $value, $params) { } /** - * Initialise the file modules. - * Listens to system boot and registers any appropriate file types and classes + * Checks on deactivate plugin event if disabling it won't create unmet dependencies and blocks disable in such case. + * + * @param string $event deactivate + * @param string $type plugin + * @param array $params Parameters array containing entry with ELggPlugin instance under 'plugin_entity' key + * @return bool false to block plugin deactivation action + * + * @access private + */ +function _plugins_deactivate_dependency_check($event, $type, $params) { + $plugin_id = $params['plugin_entity']->getManifest()->getPluginID(); + $plugin_name = $params['plugin_entity']->getManifest()->getName(); + + $active_plugins = elgg_get_plugins(); + + $dependents = array(); + foreach ($active_plugins as $plugin) { + $manifest = $plugin->getManifest(); + $requires = $manifest->getRequires(); + + foreach ($requires as $required) { + if ($required['type'] == 'plugin' && $required['name'] == $plugin_id) { + // there are active dependents + $dependents[$manifest->getPluginID()] = $plugin; + } + } + } + + if ($dependents) { + $list = '<ul>'; + // construct error message and prevent disabling + foreach ($dependents as $dependent) { + $list .= '<li>' . $dependent->getManifest()->getName() . '</li>'; + } + $list .= '</ul>'; + + register_error(elgg_echo('ElggPlugin:Dependencies:ActiveDependent', array($plugin_name, $list))); + + return false; + } +} + +/** + * Initialize the plugin system + * Listens to system init and registers actions * * @return void + * @access private */ function plugin_init() { run_function_once("plugin_run_once"); elgg_register_plugin_hook_handler('unit_test', 'system', 'plugins_test'); + + // note - plugins are booted by the time this handler is registered + // deactivation due to error may have already occurred + elgg_register_event_handler('deactivate', 'plugin', '_plugins_deactivate_dependency_check'); elgg_register_action("plugins/settings/save", '', 'admin'); elgg_register_action("plugins/usersettings/save"); - elgg_register_action('admin/plugins/enable', '', 'admin'); - elgg_register_action('admin/plugins/disable', '', 'admin'); - elgg_register_action('admin/plugins/enableall', '', 'admin'); - elgg_register_action('admin/plugins/disableall', '', 'admin'); + elgg_register_action('admin/plugins/activate', '', 'admin'); + elgg_register_action('admin/plugins/deactivate', '', 'admin'); + elgg_register_action('admin/plugins/activate_all', '', 'admin'); + elgg_register_action('admin/plugins/deactivate_all', '', 'admin'); + + elgg_register_action('admin/plugins/set_priority', '', 'admin'); - elgg_register_action('admin/plugins/reorder', '', 'admin'); + elgg_register_library('elgg:markdown', elgg_get_root_path() . 'vendors/markdown/markdown.php'); } elgg_register_event_handler('init', 'system', 'plugin_init'); diff --git a/engine/lib/private_settings.php b/engine/lib/private_settings.php index 0d86b93a1..7541f7b3b 100644 --- a/engine/lib/private_settings.php +++ b/engine/lib/private_settings.php @@ -9,169 +9,6 @@ */ /** - * Get entities based on their private data. - * - * @param string $name The name of the setting - * @param string $value The value of the setting - * @param string $type The type of entity (eg "user", "object" etc) - * @param string $subtype The arbitrary subtype of the entity - * @param int $owner_guid The GUID of the owning user - * @param string $order_by The field to order by; by default, time_created desc - * @param int $limit The number of entities to return; 10 by default - * @param int $offset The indexing offset, 0 by default - * @param boolean $count Return a count of entities - * @param int $site_guid The site to get entities for. 0 for current, -1 for any - * @param mixed $container_guid The container(s) GUIDs - * - * @return array A list of entities. - * @deprecated 1.8 - */ -function get_entities_from_private_setting($name = "", $value = "", $type = "", $subtype = "", -$owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, -$container_guid = null) { - elgg_deprecated_notice('get_entities_from_private_setting() was deprecated by elgg_get_entities_from_private_setting()!', 1.8); - - $options = array(); - - $options['private_setting_name'] = $name; - $options['private_setting_value'] = $value; - - // set container_guid to owner_guid to emulate old functionality - if ($owner_guid != "") { - if (is_null($container_guid)) { - $container_guid = $owner_guid; - } - } - - if ($type) { - $options['types'] = $type; - } - - if ($subtype) { - $options['subtypes'] = $subtype; - } - - if ($owner_guid) { - if (is_array($owner_guid)) { - $options['owner_guids'] = $owner_guid; - } else { - $options['owner_guid'] = $owner_guid; - } - } - - if ($container_guid) { - if (is_array($container_guid)) { - $options['container_guids'] = $container_guid; - } else { - $options['container_guid'] = $container_guid; - } - } - - $options['limit'] = $limit; - - if ($offset) { - $options['offset'] = $offset; - } - - if ($order_by) { - $options['order_by']; - } - - if ($site_guid) { - $options['site_guid']; - } - - if ($count) { - $options['count'] = $count; - } - - return elgg_get_entities_from_private_settings($options); -} - -/** - * Get entities based on their private data by multiple keys. - * - * @param string $name The name of the setting - * @param mixed $type Entity type - * @param string $subtype Entity subtype - * @param int $owner_guid The GUID of the owning user - * @param string $order_by The field to order by; by default, time_created desc - * @param int $limit The number of entities to return; 10 by default - * @param int $offset The indexing offset, 0 by default - * @param bool $count Count entities - * @param int $site_guid Site GUID. 0 for current, -1 for any. - * @param mixed $container_guid Container GUID - * - * @return array A list of entities. - * @deprecated 1.8 - */ -function get_entities_from_private_setting_multi(array $name, $type = "", $subtype = "", -$owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, -$site_guid = 0, $container_guid = null) { - - elgg_deprecated_notice('get_entities_from_private_setting_multi() was deprecated by elgg_get_entities_from_private_setting()!', 1.8); - - $options = array(); - - $pairs = array(); - foreach ($name as $setting_name => $setting_value) { - $pairs[] = array('name' => $setting_name, 'value' => $setting_value); - } - $options['private_setting_name_value_pairs'] = $pairs; - - // set container_guid to owner_guid to emulate old functionality - if ($owner_guid != "") { - if (is_null($container_guid)) { - $container_guid = $owner_guid; - } - } - - if ($type) { - $options['types'] = $type; - } - - if ($subtype) { - $options['subtypes'] = $subtype; - } - - if ($owner_guid) { - if (is_array($owner_guid)) { - $options['owner_guids'] = $owner_guid; - } else { - $options['owner_guid'] = $owner_guid; - } - } - - if ($container_guid) { - if (is_array($container_guid)) { - $options['container_guids'] = $container_guid; - } else { - $options['container_guid'] = $container_guid; - } - } - - $options['limit'] = $limit; - - if ($offset) { - $options['offset'] = $offset; - } - - if ($order_by) { - $options['order_by']; - } - - if ($site_guid) { - $options['site_guid']; - } - - if ($count) { - $options['count'] = $count; - } - - return elgg_get_entities_from_private_settings($options); -} - -/** * Returns entities based upon private settings. Also accepts all * options available to elgg_get_entities(). Supports * the singular option shortcut. @@ -196,8 +33,12 @@ $site_guid = 0, $container_guid = null) { * private_setting_name_value_pairs_operator => NULL|STR The operator to use for combining * (name = value) OPERATOR (name = value); default AND * + * private_setting_name_prefix => STR A prefix to apply to all private settings. Used to + * namespace plugin user settings or by plugins to namespace + * their own settings. * - * @return array + * + * @return mixed int If count, int. If not count, array. false on errors. * @since 1.8.0 */ function elgg_get_entities_from_private_settings(array $options = array()) { @@ -206,6 +47,7 @@ function elgg_get_entities_from_private_settings(array $options = array()) { 'private_setting_values' => ELGG_ENTITIES_ANY_VALUE, 'private_setting_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, 'private_setting_name_value_pairs_operator' => 'AND', + 'private_setting_name_prefix' => '', ); $options = array_merge($defaults, $options); @@ -217,7 +59,7 @@ function elgg_get_entities_from_private_settings(array $options = array()) { $clauses = elgg_get_entity_private_settings_where_sql('e', $options['private_setting_names'], $options['private_setting_values'], $options['private_setting_name_value_pairs'], - $options['private_setting_name_value_pairs_operator']); + $options['private_setting_name_value_pairs_operator'], $options['private_setting_name_prefix']); if ($clauses) { // merge wheres to pass to get_entities() @@ -228,7 +70,7 @@ function elgg_get_entities_from_private_settings(array $options = array()) { } $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']); - + // merge joins to pass to get_entities() if (isset($options['joins']) && !is_array($options['joins'])) { $options['joins'] = array($options['joins']); @@ -250,11 +92,13 @@ function elgg_get_entities_from_private_settings(array $options = array()) { * @param array|null $values Array of values * @param array|null $pairs Array of names / values / operands * @param string $pair_operator Operator for joining pairs where clauses + * @param string $name_prefix A string to prefix all names with * @return array * @since 1.8.0 + * @access private */ function elgg_get_entity_private_settings_where_sql($table, $names = NULL, $values = NULL, -$pairs = NULL, $pair_operator = 'AND') { +$pairs = NULL, $pair_operator = 'AND', $name_prefix = '') { global $CONFIG; @@ -279,6 +123,7 @@ $pairs = NULL, $pair_operator = 'AND') { $sanitised_names = array(); foreach ($names as $name) { + $name = $name_prefix . $name; $sanitised_names[] = '\'' . sanitise_string($name) . '\''; } @@ -322,7 +167,7 @@ $pairs = NULL, $pair_operator = 'AND') { if (is_array($pairs)) { // join counter for incremental joins in pairs $i = 1; - + // check if this is an array of pairs or just a single pair. if (isset($pairs['name']) || isset($pairs['value'])) { $pairs = array($pairs); @@ -383,7 +228,7 @@ $pairs = NULL, $pair_operator = 'AND') { $value = "'" . sanitise_string($pair['value']) . "'"; } - $name = sanitise_string($pair['name']); + $name = sanitise_string($name_prefix . $pair['name']); // @todo The multiple joins are only needed when the operator is AND $return['joins'][] = "JOIN {$CONFIG->dbprefix}private_settings ps{$i} @@ -395,7 +240,7 @@ $pairs = NULL, $pair_operator = 'AND') { $i++; } - $where = implode (" $pair_operator ", $pair_wheres); + $where = implode(" $pair_operator ", $pair_wheres); if ($where) { $wheres[] = "($where)"; } @@ -405,7 +250,7 @@ $pairs = NULL, $pair_operator = 'AND') { if ($where) { $return['wheres'][] = "($where)"; } - + return $return; } @@ -433,6 +278,11 @@ function get_private_setting($entity_guid, $name) { $entity_guid = (int) $entity_guid; $name = sanitise_string($name); + $entity = get_entity($entity_guid); + if (!$entity instanceof ElggEntity) { + return false; + } + $query = "SELECT value from {$CONFIG->dbprefix}private_settings where name = '{$name}' and entity_guid = {$entity_guid}"; $setting = get_data_row($query); @@ -459,6 +309,10 @@ function get_all_private_settings($entity_guid) { global $CONFIG; $entity_guid = (int) $entity_guid; + $entity = get_entity($entity_guid); + if (!$entity instanceof ElggEntity) { + return false; + } $query = "SELECT * from {$CONFIG->dbprefix}private_settings where entity_guid = {$entity_guid}"; $result = get_data($query); @@ -481,7 +335,7 @@ function get_all_private_settings($entity_guid) { * @param string $name The name of the setting * @param string $value The value of the setting * - * @return mixed The setting ID, or false on failure + * @return bool * @see get_private_setting() * @see get_all_private_settings() * @see remove_private_setting() @@ -497,12 +351,10 @@ function set_private_setting($entity_guid, $name, $value) { $result = insert_data("INSERT into {$CONFIG->dbprefix}private_settings (entity_guid, name, value) VALUES - ($entity_guid, '{$name}', '{$value}') + ($entity_guid, '$name', '$value') ON DUPLICATE KEY UPDATE value='$value'"); - if ($result === 0) { - return true; - } - return $result; + + return $result !== false; } /** @@ -511,7 +363,7 @@ function set_private_setting($entity_guid, $name, $value) { * @param int $entity_guid The Entity GUID * @param string $name The name of the setting * - * @return true|false depending on success + * @return bool * @see get_private_setting() * @see get_all_private_settings() * @see set_private_setting() @@ -522,11 +374,17 @@ function remove_private_setting($entity_guid, $name) { global $CONFIG; $entity_guid = (int) $entity_guid; + + $entity = get_entity($entity_guid); + if (!$entity instanceof ElggEntity) { + return false; + } + $name = sanitise_string($name); return delete_data("DELETE from {$CONFIG->dbprefix}private_settings - where name = '{$name}' - and entity_guid = {$entity_guid}"); + WHERE name = '{$name}' + AND entity_guid = {$entity_guid}"); } /** @@ -534,7 +392,7 @@ function remove_private_setting($entity_guid, $name) { * * @param int $entity_guid The Entity GUID * - * @return true|false depending on success + * @return bool * @see get_private_setting() * @see get_all_private_settings() * @see set_private_setting() @@ -545,6 +403,12 @@ function remove_all_private_settings($entity_guid) { global $CONFIG; $entity_guid = (int) $entity_guid; + + $entity = get_entity($entity_guid); + if (!$entity instanceof ElggEntity) { + return false; + } + return delete_data("DELETE from {$CONFIG->dbprefix}private_settings - where entity_guid = {$entity_guid}"); + WHERE entity_guid = {$entity_guid}"); } diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php index f19c76e8b..b0cd627fc 100644 --- a/engine/lib/relationships.php +++ b/engine/lib/relationships.php @@ -12,7 +12,8 @@ * * @param stdClass $row Database row from the relationship table * - * @return stdClass or ElggMetadata + * @return ElggRelationship|stdClass + * @access private */ function row_to_elggrelationship($row) { if (!($row instanceof stdClass)) { @@ -27,7 +28,7 @@ function row_to_elggrelationship($row) { * * @param int $id The ID of a relationship * - * @return mixed + * @return ElggRelationship|false */ function get_relationship($id) { global $CONFIG; @@ -108,7 +109,7 @@ function add_entity_relationship($guid_one, $relationship, $guid_two) { * @param string $relationship The type of relationship * @param int $guid_two The GUID of the entity the relationship is with * - * @return object|false Depending on success + * @return ElggRelationship|false Depending on success */ function check_entity_relationship($guid_one, $relationship, $guid_two) { global $CONFIG; @@ -122,7 +123,7 @@ function check_entity_relationship($guid_one, $relationship, $guid_two) { AND relationship='$relationship' AND guid_two=$guid_two limit 1"; - $row = $row = get_data_row($query); + $row = row_to_elggrelationship(get_data_row($query)); if ($row) { return $row; } @@ -157,7 +158,7 @@ function remove_entity_relationship($guid_one, $relationship, $guid_two) { and relationship='$relationship' and guid_two=$guid_two"; - return delete_data($query); + return (bool)delete_data($query); } else { return false; } @@ -219,7 +220,7 @@ function remove_entity_relationships($guid_one, $relationship = "", $inverse = f * @param int $guid The GUID of the relationship owner * @param bool $inverse_relationship Inverse relationship owners? * - * @return mixed + * @return ElggRelationship[] */ function get_entity_relationships($guid, $inverse_relationship = FALSE) { global $CONFIG; @@ -233,9 +234,22 @@ function get_entity_relationships($guid, $inverse_relationship = FALSE) { return get_data($query, "row_to_elggrelationship"); } - /** * Return entities matching a given query joining against a relationship. + * Also accepts all options available to elgg_get_entities() and + * elgg_get_entities_from_metadata(). + * + * To ask for entities that do not have a particulat relationship to an entity, + * use a custom where clause like the following: + * + * $options['wheres'][] = "NOT EXISTS ( + * SELECT 1 FROM {$db_prefix}entity_relationships + * WHERE guid_one = e.guid + * AND relationship = '$relationship' + * )"; + * + * @see elgg_get_entities + * @see elgg_get_entities_from_metadata * * @param array $options Array in format: * @@ -245,7 +259,7 @@ function get_entity_relationships($guid, $inverse_relationship = FALSE) { * * inverse_relationship => BOOL Inverse the relationship * - * @return array + * @return ElggEntity[]|mixed If count, int. If not count, array. false on errors. * @since 1.7.0 */ function elgg_get_entities_from_relationship($options) { @@ -257,7 +271,7 @@ function elgg_get_entities_from_relationship($options) { $options = array_merge($defaults, $options); - $clauses = elgg_get_entity_relationship_where_sql('e', $options['relationship'], + $clauses = elgg_get_entity_relationship_where_sql('e.guid', $options['relationship'], $options['relationship_guid'], $options['inverse_relationship']); if ($clauses) { @@ -278,6 +292,16 @@ function elgg_get_entities_from_relationship($options) { } $options['joins'] = array_merge($options['joins'], $clauses['joins']); + + if (isset($options['selects']) && !is_array($options['selects'])) { + $options['selects'] = array($options['selects']); + } elseif (!isset($options['selects'])) { + $options['selects'] = array(); + } + + $select = array('r.id'); + + $options['selects'] = array_merge($options['selects'], $select); } return elgg_get_entities_from_metadata($options); @@ -288,18 +312,20 @@ function elgg_get_entities_from_relationship($options) { * * @todo add support for multiple relationships and guids. * - * @param string $table Entities table name + * @param string $column Column name the guid should be checked against. + * Provide in table.column format. * @param string $relationship Relationship string * @param int $relationship_guid Entity guid to check - * @param string $inverse_relationship Inverse relationship check? + * @param bool $inverse_relationship Inverse relationship check? * * @return mixed * @since 1.7.0 + * @access private */ -function elgg_get_entity_relationship_where_sql($table, $relationship = NULL, +function elgg_get_entity_relationship_where_sql($column, $relationship = NULL, $relationship_guid = NULL, $inverse_relationship = FALSE) { - if ($relationship == NULL && $entity_guid == NULL) { + if ($relationship == NULL && $relationship_guid == NULL) { return ''; } @@ -309,9 +335,9 @@ $relationship_guid = NULL, $inverse_relationship = FALSE) { $joins = array(); if ($inverse_relationship) { - $joins[] = "JOIN {$CONFIG->dbprefix}entity_relationships r on r.guid_one = e.guid"; + $joins[] = "JOIN {$CONFIG->dbprefix}entity_relationships r on r.guid_one = $column"; } else { - $joins[] = "JOIN {$CONFIG->dbprefix}entity_relationships r on r.guid_two = e.guid"; + $joins[] = "JOIN {$CONFIG->dbprefix}entity_relationships r on r.guid_two = $column"; } if ($relationship) { @@ -335,73 +361,9 @@ $relationship_guid = NULL, $inverse_relationship = FALSE) { } /** - * Return entities from relationships - * - * @deprecated 1.7 Use elgg_get_entities_from_relationship() - * - * @param string $relationship The relationship type - * @param int $relationship_guid The GUID of the relationship owner - * @param bool $inverse_relationship Invert relationship? - * @param string $type Entity type - * @param string $subtype Entity subtype - * @param int $owner_guid Entity owner GUID - * @param string $order_by Order by clause - * @param int $limit Limit - * @param int $offset Offset - * @param bool $count Return a count instead of entities? - * @param int $site_guid Site GUID - * - * @return mixed - */ -function get_entities_from_relationship($relationship, $relationship_guid, -$inverse_relationship = false, $type = "", $subtype = "", $owner_guid = 0, -$order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0) { - - elgg_deprecated_notice('get_entities_from_relationship() was deprecated by elgg_get_entities_from_relationship()!', 1.7); - - $options = array(); - - $options['relationship'] = $relationship; - $options['relationship_guid'] = $relationship_guid; - $options['inverse_relationship'] = $inverse_relationship; - - if ($type) { - $options['types'] = $type; - } - - if ($subtype) { - $options['subtypes'] = $subtype; - } - - if ($owner_guid) { - $options['owner_guid'] = $owner_guid; - } - - $options['limit'] = $limit; - - if ($offset) { - $options['offset'] = $offset; - } - - if ($order_by) { - $options['order_by']; - } - - if ($site_guid) { - $options['site_guid']; - } - - if ($count) { - $options['count'] = $count; - } - - return elgg_get_entities_from_relationship($options); -} - -/** * Returns a viewable list of entities by relationship * - * @param array $options + * @param array $options Options array for retrieval of entities * * @see elgg_list_entities() * @see elgg_get_entities_from_relationship() @@ -413,240 +375,48 @@ function elgg_list_entities_from_relationship(array $options = array()) { } /** - * @deprecated 1.8 Use elgg_list_entities_from_relationship() - */ -function list_entities_from_relationship($relationship, $relationship_guid, -$inverse_relationship = false, $type = ELGG_ENTITIES_ANY_VALUE, -$subtype = ELGG_ENTITIES_ANY_VALUE, $owner_guid = 0, $limit = 10, -$fullview = true, $listtypetoggle = false, $pagination = true) { - - elgg_deprecated_notice("list_entities_from_relationship was deprecated by elgg_list_entities_from_relationship()!", 1.8); - return elgg_list_entities_from_relationship(array( - 'relationship' => $relationship, - 'relationship_guid' => $relationship_guid, - 'inverse_relationship' => $inverse_relationship, - 'types' => $type, - 'subtypes' => $subtype, - 'owner_guid' => $owner_guid, - 'limit' => $limit, - 'full_view' => $fullview, - 'list_type_toggle' => $listtypetoggle, - 'pagination' => $pagination, - )); -} - -/** * Gets the number of entities by a the number of entities related to them in a particular way. * This is a good way to get out the users with the most friends, or the groups with the * most members. * - * @param string $relationship The relationship eg "friends_of" - * @param bool $inverse_relationship Inverse relationship owners - * @param string $type The type of entity (default: all) - * @param string $subtype The entity subtype (default: all) - * @param int $owner_guid The owner of the entities (default: none) - * @param int $limit Limit - * @param int $offset Offset - * @param bool $count Return a count instead of entities - * @param int $site_guid Site GUID - * - * @return array|int|false An array of entities, or the number of entities, or false on failure + * @param array $options An options array compatible with + * elgg_get_entities_from_relationship() + * @return ElggEntity[]|mixed int If count, int. If not count, array. false on errors. + * @since 1.8.0 */ - -function get_entities_by_relationship_count($relationship, $inverse_relationship = true, $type = "", -$subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $count = false, $site_guid = 0) { - - global $CONFIG; - - $relationship = sanitise_string($relationship); - $inverse_relationship = (bool)$inverse_relationship; - $type = sanitise_string($type); - if ($subtype AND !$subtype = get_subtype_id($type, $subtype)) { - return false; - } - $owner_guid = (int)$owner_guid; - $order_by = sanitise_string($order_by); - $limit = (int)$limit; - $offset = (int)$offset; - $site_guid = (int) $site_guid; - if ($site_guid == 0) { - $site_guid = $CONFIG->site_guid; - } - - //$access = get_access_list(); - - $where = array(); - - if ($relationship != "") { - $where[] = "r.relationship='$relationship'"; - } - - if ($inverse_relationship) { - $on = 'e.guid = r.guid_two'; - } else { - $on = 'e.guid = r.guid_one'; - } - if ($type != "") { - $where[] = "e.type='$type'"; - } - - if ($subtype) { - $where[] = "e.subtype=$subtype"; - } - - if ($owner_guid != "") { - $where[] = "e.container_guid='$owner_guid'"; - } - - if ($site_guid > 0) { - $where[] = "e.site_guid = {$site_guid}"; - } - - if ($count) { - $query = "SELECT count(distinct e.guid) as total "; - } else { - $query = "SELECT e.*, count(e.guid) as total "; - } - - $query .= " from {$CONFIG->dbprefix}entity_relationships r - JOIN {$CONFIG->dbprefix}entities e on {$on} where "; - - if (!empty($where)) { - foreach ($where as $w) { - $query .= " $w and "; - } - } - $query .= get_access_sql_suffix("e"); - - if (!$count) { - $query .= " group by e.guid "; - $query .= " order by total desc limit {$offset}, {$limit}"; - return get_data($query, "entity_row_to_elggstar"); - } else { - if ($count = get_data_row($query)) { - return $count->total; - } - } - - return false; +function elgg_get_entities_from_relationship_count(array $options = array()) { + $options['selects'][] = "COUNT(e.guid) as total"; + $options['group_by'] = 'r.guid_two'; + $options['order_by'] = 'total desc'; + return elgg_get_entities_from_relationship($options); } /** - * Displays a human-readable list of entities - * - * @param string $relationship The relationship eg "friends_of" - * @param bool $inverse_relationship Inverse relationship owners - * @param string $type The type of entity (eg 'object') - * @param string $subtype The entity subtype - * @param int $owner_guid The owner (default: all) - * @param int $limit The number of entities to display on a page - * @param bool $fullview Whether or not to display the full view (default: true) - * @param bool $listtypetoggle Whether or not to allow gallery view - * @param bool $pagination Whether to display pagination (default: true) + * Returns a list of entities by relationship count * - * @return string The viewable list of entities + * @see elgg_get_entities_from_relationship_count() + * + * @param array $options Options array + * + * @return string + * @since 1.8.0 */ - -function list_entities_by_relationship_count($relationship, $inverse_relationship = true, -$type = "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, -$listtypetoggle = false, $pagination = true) { - - $limit = (int) $limit; - $offset = (int) get_input('offset'); - $count = get_entities_by_relationship_count($relationship, $inverse_relationship, - $type, $subtype, $owner_guid, 0, 0, true); - $entities = get_entities_by_relationship_count($relationship, $inverse_relationship, - $type, $subtype, $owner_guid, $limit, $offset); - - return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $listtypetoggle, $pagination); -} - -/** - * Gets the number of entities by a the number of entities related to - * them in a particular way also constrained by metadata - * - * @param string $relationship The relationship eg "friends_of" - * @param int $relationship_guid The guid of the entity to use query - * @param bool $inverse_relationship Inverse relationship owner - * @param String $meta_name The metadata name - * @param String $meta_value The metadata value - * @param string $type The type of entity (default: all) - * @param string $subtype The entity subtype (default: all) - * @param int $owner_guid The owner of the entities (default: none) - * @param int $limit Limit - * @param int $offset Offset - * @param bool $count Return a count instead of entities - * @param int $site_guid Site GUID - * - * @return array|int|false An array of entities, or the number of entities, or false on failure - */ -function get_entities_from_relationships_and_meta($relationship, $relationship_guid, -$inverse_relationship = false, $meta_name = "", $meta_value = "", $type = "", -$subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $count = false, $site_guid = 0) { - - elgg_deprecated_notice('get_entities_from_relationship_and_meta() was deprecated by elgg_get_entities_from_relationship()!', 1.7); - - $options = array(); - - $options['relationship'] = $relationship; - $options['relationship_guid'] = $relationship_guid; - $options['inverse_relationship'] = $inverse_relationship; - - if ($meta_value) { - $options['values'] = $meta_value; - } - - if ($entity_type) { - $options['types'] = $entity_type; - } - - if ($type) { - $options['types'] = $type; - } - - if ($subtype) { - $options['subtypes'] = $subtype; - } - - if ($owner_guid) { - $options['owner_guid'] = $owner_guid; - } - - if ($limit) { - $options['limit'] = $limit; - } - - if ($offset) { - $options['offset'] = $offset; - } - - if ($order_by) { - $options['order_by']; - } - - if ($site_guid) { - $options['site_guid']; - } - - if ($count) { - $options['count'] = $count; - } - - return elgg_get_entities_from_relationship($options); +function elgg_list_entities_from_relationship_count($options) { + return elgg_list_entities($options, 'elgg_get_entities_from_relationship_count'); } /** * Sets the URL handler for a particular relationship type * - * @param string $function_name The function to register * @param string $relationship_type The relationship type. + * @param string $function_name The function to register * * @return bool Depending on success */ -function register_relationship_url_handler($function_name, $relationship_type = "all") { +function elgg_register_relationship_url_handler($relationship_type, $function_name) { global $CONFIG; - if (!is_callable($function_name)) { + if (!is_callable($function_name, true)) { return false; } @@ -688,7 +458,7 @@ function get_relationship_url($id) { } if (is_callable($function)) { - $url = $function($relationship); + $url = call_user_func($function, $relationship); } if ($url == "") { @@ -713,7 +483,8 @@ function get_relationship_url($id) { * @param int $guid_two This is the object trying to attach to $guid_one * * @return bool - **/ + * @access private + */ function already_attached($guid_one, $guid_two) { if ($attached = check_entity_relationship($guid_one, "attached", $guid_two)) { return true; @@ -728,14 +499,15 @@ function already_attached($guid_one, $guid_two) { * @param int $guid Entity GUID * @param string $type The type of object to return e.g. 'file', 'friend_of' etc * - * @return an array of objects -**/ + * @return ElggEntity[] + * @access private + */ function get_attachments($guid, $type = "") { $options = array( 'relationship' => 'attached', 'relationship_guid' => $guid, 'inverse_relationship' => false, - 'types' => $type, + 'type' => $type, 'subtypes' => '', 'owner_guid' => 0, 'order_by' => 'time_created desc', @@ -755,7 +527,8 @@ function get_attachments($guid, $type = "") { * @param int $guid_two This is the object to remove from $guid_one * * @return void -**/ + * @access private + */ function remove_attachment($guid_one, $guid_two) { if (already_attached($guid_one, $guid_two)) { remove_entity_relationship($guid_one, "attached", $guid_two); @@ -769,7 +542,8 @@ function remove_attachment($guid_one, $guid_two) { * @param int $guid_two This is the object trying to attach to $guid_one * * @return true|void -**/ + * @access private + */ function make_attachment($guid_one, $guid_two) { if (!(already_attached($guid_one, $guid_two))) { if (add_entity_relationship($guid_one, "attached", $guid_two)) { @@ -787,7 +561,7 @@ function make_attachment($guid_one, $guid_two) { * @param mixed $params Array of params * * @return mixed - * + * @access private */ function import_relationship_plugin_hook($hook, $entity_type, $returnvalue, $params) { $element = $params['element']; @@ -797,9 +571,8 @@ function import_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par if ($element instanceof ODDRelationship) { $tmp = new ElggRelationship(); $tmp->import($element); - - return $tmp; } + return $tmp; } /** @@ -812,10 +585,10 @@ function import_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par * * @elgg_event_handler export all * @return mixed + * @throws InvalidParameterException + * @access private */ function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $params) { - global $CONFIG; - // Sanity check values if ((!is_array($params)) && (!isset($params['guid']))) { throw new InvalidParameterException(elgg_echo('InvalidParameterException:GUIDNotForExport')); @@ -839,38 +612,32 @@ function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par } /** - * An event listener which will notify users based on certain events. + * Notify user that someone has friended them * - * @param string $event Event name - * @param string $object_type Object type - * @param mixed $object Object + * @param string $event Event name + * @param string $type Object type + * @param mixed $object Object * * @return bool + * @access private */ -function relationship_notification_hook($event, $object_type, $object) { - global $CONFIG; +function relationship_notification_hook($event, $type, $object) { + /* @var ElggRelationship $object */ + $user_one = get_entity($object->guid_one); + /* @var ElggUser $user_one */ - if ( - ($object instanceof ElggRelationship) && - ($event == 'create') && - ($object_type == 'friend') - ) { - $user_one = get_entity($object->guid_one); - $user_two = get_entity($object->guid_two); - - // Notify target user - return notify_user($object->guid_two, $object->guid_one, + return notify_user($object->guid_two, + $object->guid_one, elgg_echo('friend:newfriend:subject', array($user_one->name)), elgg_echo("friend:newfriend:body", array($user_one->name, $user_one->getURL())) - ); - } + ); } -/** Register the import hook */ +// Register the import hook elgg_register_plugin_hook_handler("import", "all", "import_relationship_plugin_hook", 3); -/** Register the hook, ensuring entities are serialised first */ +// Register the hook, ensuring entities are serialised first elgg_register_plugin_hook_handler("export", "all", "export_relationship_plugin_hook", 3); -/** Register event to listen to some events **/ +// Register event to listen to some events elgg_register_event_handler('create', 'friend', 'relationship_notification_hook'); diff --git a/engine/lib/river.php b/engine/lib/river.php index ad069d5cf..e92040eb7 100644 --- a/engine/lib/river.php +++ b/engine/lib/river.php @@ -1,7 +1,7 @@ <?php /** - * Elgg river 2.0. - * Functions for listening for and generating the river separately from the system log. + * Elgg river. + * Activity stream functions. * * @package Elgg.Core * @subpackage SocialModel.River @@ -18,12 +18,14 @@ * @param int $posted The UNIX epoch timestamp of the river item (default: now) * @param int $annotation_id The annotation ID associated with this river entry * - * @return bool Depending on success + * @return int/bool River ID or false on failure */ function add_to_river($view, $action_type, $subject_guid, $object_guid, $access_id = "", $posted = 0, $annotation_id = 0) { - // use default viewtype for when called from REST api + global $CONFIG; + + // use default viewtype for when called from web services api if (!elgg_view_exists($view, 'default')) { return false; } @@ -42,705 +44,660 @@ $posted = 0, $annotation_id = 0) { if ($access_id === "") { $access_id = $object->access_id; } - $annotation_id = (int)$annotation_id; $type = $object->getType(); $subtype = $object->getSubtype(); - $action_type = sanitise_string($action_type); - // Load config - global $CONFIG; + $view = sanitise_string($view); + $action_type = sanitise_string($action_type); + $subject_guid = sanitise_int($subject_guid); + $object_guid = sanitise_int($object_guid); + $access_id = sanitise_int($access_id); + $posted = sanitise_int($posted); + $annotation_id = sanitise_int($annotation_id); + + $values = array( + 'type' => $type, + 'subtype' => $subtype, + 'action_type' => $action_type, + 'access_id' => $access_id, + 'view' => $view, + 'subject_guid' => $subject_guid, + 'object_guid' => $object_guid, + 'annotation_id' => $annotation_id, + 'posted' => $posted, + ); + + // return false to stop insert + $values = elgg_trigger_plugin_hook('creating', 'river', null, $values); + if ($values == false) { + // inserting did not fail - it was just prevented + return true; + } + + extract($values); // Attempt to save river item; return success status - $insert_data = insert_data("insert into {$CONFIG->dbprefix}river " . - " set type = '{$type}', " . - " subtype = '{$subtype}', " . - " action_type = '{$action_type}', " . - " access_id = {$access_id}, " . - " view = '{$view}', " . - " subject_guid = {$subject_guid}, " . - " object_guid = {$object_guid}, " . - " annotation_id = {$annotation_id}, " . - " posted = {$posted} "); - - //update the entities which had the action carried out on it - if ($insert_data) { + $id = insert_data("insert into {$CONFIG->dbprefix}river " . + " set type = '$type', " . + " subtype = '$subtype', " . + " action_type = '$action_type', " . + " access_id = $access_id, " . + " view = '$view', " . + " subject_guid = $subject_guid, " . + " object_guid = $object_guid, " . + " annotation_id = $annotation_id, " . + " posted = $posted"); + + // update the entities which had the action carried out on it + // @todo shouldn't this be down elsewhere? Like when an annotation is saved? + if ($id) { update_entity_last_action($object_guid, $posted); - return $insert_data; + + $river_items = elgg_get_river(array('id' => $id)); + if ($river_items) { + elgg_trigger_event('created', 'river', $river_items[0]); + } + return $id; + } else { + return false; } } /** - * Removes all items relating to a particular acting entity from the river + * Delete river items * - * @param int $subject_guid The GUID of the entity + * @warning not checking access (should we?) * - * @return bool Depending on success - */ -function remove_from_river_by_subject($subject_guid) { - // Sanitise - $subject_guid = (int) $subject_guid; - - // Load config - global $CONFIG; - - // Remove - return delete_data("delete from {$CONFIG->dbprefix}river where subject_guid = {$subject_guid}"); -} - -/** - * Removes all items relating to a particular entity being acted upon from the river + * @param array $options Parameters: + * ids => INT|ARR River item id(s) + * subject_guids => INT|ARR Subject guid(s) + * object_guids => INT|ARR Object guid(s) + * annotation_ids => INT|ARR The identifier of the annotation(s) + * action_types => STR|ARR The river action type(s) identifier + * views => STR|ARR River view(s) * - * @param int $object_guid The GUID of the entity + * types => STR|ARR Entity type string(s) + * subtypes => STR|ARR Entity subtype string(s) + * type_subtype_pairs => ARR Array of type => subtype pairs where subtype + * can be an array of subtype strings * - * @return bool Depending on success + * posted_time_lower => INT The lower bound on the time posted + * posted_time_upper => INT The upper bound on the time posted + * + * @return bool + * @since 1.8.0 */ -function remove_from_river_by_object($object_guid) { - // Sanitise - $object_guid = (int) $object_guid; - - // Load config +function elgg_delete_river(array $options = array()) { global $CONFIG; - // Remove - return delete_data("delete from {$CONFIG->dbprefix}river where object_guid = {$object_guid}"); -} + $defaults = array( + 'ids' => ELGG_ENTITIES_ANY_VALUE, -/** - * Removes all items relating to a particular annotation being acted upon from the river - * - * @param int $annotation_id The ID of the annotation - * - * @return bool Depending on success - * @since 1.7.0 - */ -function remove_from_river_by_annotation($annotation_id) { - // Sanitise - $annotation_id = (int) $annotation_id; + 'subject_guids' => ELGG_ENTITIES_ANY_VALUE, + 'object_guids' => ELGG_ENTITIES_ANY_VALUE, + 'annotation_ids' => ELGG_ENTITIES_ANY_VALUE, - // Load config - global $CONFIG; + 'views' => ELGG_ENTITIES_ANY_VALUE, + 'action_types' => ELGG_ENTITIES_ANY_VALUE, - // Remove - return delete_data("delete from {$CONFIG->dbprefix}river where annotation_id = {$annotation_id}"); -} + 'types' => ELGG_ENTITIES_ANY_VALUE, + 'subtypes' => ELGG_ENTITIES_ANY_VALUE, + 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, -/** - * Removes a single river entry - * - * @param int $id The ID of the river entry - * - * @return bool Depending on success - * @since 1.7.2 - */ -function remove_from_river_by_id($id) { - global $CONFIG; + 'posted_time_lower' => ELGG_ENTITIES_ANY_VALUE, + 'posted_time_upper' => ELGG_ENTITIES_ANY_VALUE, - // Sanitise - $id = (int) $id; + 'wheres' => array(), + 'joins' => array(), - return delete_data("delete from {$CONFIG->dbprefix}river where id = {$id}"); -} + ); -/** - * Sets the access ID on river items for a particular object - * - * @param int $object_guid The GUID of the entity - * @param int $access_id The access ID - * - * @return bool Depending on success - */ -function update_river_access_by_object($object_guid, $access_id) { - // Sanitise - $object_guid = (int) $object_guid; - $access_id = (int) $access_id; + $options = array_merge($defaults, $options); - // Load config - global $CONFIG; + $singulars = array('id', 'subject_guid', 'object_guid', 'annotation_id', 'action_type', 'view', 'type', 'subtype'); + $options = elgg_normalise_plural_options_array($options, $singulars); - // Remove - $query = "update {$CONFIG->dbprefix}river - set access_id = {$access_id} - where object_guid = {$object_guid}"; - return update_data($query); -} + $wheres = $options['wheres']; -/** - * Retrieves items from the river. All parameters are optional. - * - * @param int|array $subject_guid Acting entity to restrict to. Default: all - * @param int|array $object_guid Entity being acted on to restrict to. Default: all - * @param string $subject_relationship If set to a relationship type, this will use - * $subject_guid as the starting point and set the - * subjects to be all users this - * entity has this relationship with (eg 'friend'). - * Default: blank - * @param string $type The type of entity to restrict to. Default: all - * @param string $subtype The subtype of entity to restrict to. Default: all - * @param string $action_type The type of river action to restrict to. Default: all - * @param int $limit The number of items to retrieve. Default: 20 - * @param int $offset The page offset. Default: 0 - * @param int $posted_min The minimum time period to look at. Default: none - * @param int $posted_max The maximum time period to look at. Default: none - * - * @return array|false Depending on success - */ -function get_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '', -$type = '', $subtype = '', $action_type = '', $limit = 20, $offset = 0, $posted_min = 0, -$posted_max = 0) { + $wheres[] = elgg_get_guid_based_where_sql('rv.id', $options['ids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.subject_guid', $options['subject_guids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.object_guid', $options['object_guids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.annotation_id', $options['annotation_ids']); + $wheres[] = elgg_river_get_action_where_sql($options['action_types']); + $wheres[] = elgg_river_get_view_where_sql($options['views']); + $wheres[] = elgg_get_river_type_subtype_where_sql('rv', $options['types'], + $options['subtypes'], $options['type_subtype_pairs']); - // Get config - global $CONFIG; + if ($options['posted_time_lower'] && is_int($options['posted_time_lower'])) { + $wheres[] = "rv.posted >= {$options['posted_time_lower']}"; + } - // Sanitise variables - if (!is_array($subject_guid)) { - $subject_guid = (int) $subject_guid; - } else { - foreach ($subject_guid as $key => $temp) { - $subject_guid[$key] = (int) $temp; - } + if ($options['posted_time_upper'] && is_int($options['posted_time_upper'])) { + $wheres[] = "rv.posted <= {$options['posted_time_upper']}"; } - if (!is_array($object_guid)) { - $object_guid = (int) $object_guid; - } else { - foreach ($object_guid as $key => $temp) { - $object_guid[$key] = (int) $temp; + + // see if any functions failed + // remove empty strings on successful functions + foreach ($wheres as $i => $where) { + if ($where === FALSE) { + return FALSE; + } elseif (empty($where)) { + unset($wheres[$i]); } } - if (!empty($type)) { - $type = sanitise_string($type); - } - if (!empty($subtype)) { - $subtype = sanitise_string($subtype); - } - if (!empty($action_type)) { - $action_type = sanitise_string($action_type); - } - $limit = (int) $limit; - $offset = (int) $offset; - $posted_min = (int) $posted_min; - $posted_max = (int) $posted_max; - // Construct 'where' clauses for the river - $where = array(); - // river table does not have columns expected by get_access_sql_suffix so we modify its output - $where[] = str_replace("and enabled='yes'", '', - str_replace('owner_guid', 'subject_guid', get_access_sql_suffix())); + // remove identical where clauses + $wheres = array_unique($wheres); - if (empty($subject_relationship)) { - if (!empty($subject_guid)) { - if (!is_array($subject_guid)) { - $where[] = " subject_guid = {$subject_guid} "; - } else { - $where[] = " subject_guid in (" . implode(',', $subject_guid) . ") "; - } - } - } else { - if (!is_array($subject_guid)) { - if ($entities = elgg_get_entities_from_relationship(array ( - 'relationship' => $subject_relationship, - 'relationship_guid' => $subject_guid, - 'limit' => 9999)) - ) { - $guids = array(); - foreach ($entities as $entity) { - $guids[] = (int) $entity->guid; - } - $where[] = " subject_guid in (" . implode(',', $guids) . ") "; - } else { - return array(); - } - } - } - if (!empty($object_guid)) { - if (!is_array($object_guid)) { - $where[] = " object_guid = {$object_guid} "; - } else { - $where[] = " object_guid in (" . implode(',', $object_guid) . ") "; - } - } - if (!empty($type)) { - $where[] = " type = '{$type}' "; - } - if (!empty($subtype)) { - $where[] = " subtype = '{$subtype}' "; - } - if (!empty($action_type)) { - $where[] = " action_type = '{$action_type}' "; - } - if (!empty($posted_min)) { - $where[] = " posted > {$posted_min} "; - } - if (!empty($posted_max)) { - $where[] = " posted < {$posted_max} "; + $query = "DELETE rv.* FROM {$CONFIG->dbprefix}river rv "; + + // remove identical join clauses + $joins = array_unique($options['joins']); + + // add joins + foreach ($joins as $j) { + $query .= " $j "; } - $whereclause = implode(' and ', $where); + // add wheres + $query .= ' WHERE '; - // Construct main SQL - $sql = "select id, type, subtype, action_type, access_id, view, - subject_guid, object_guid, annotation_id, posted - from {$CONFIG->dbprefix}river - where {$whereclause} order by posted desc limit {$offset}, {$limit}"; + foreach ($wheres as $w) { + $query .= " $w AND "; + } + $query .= "1=1"; - // Get data - return get_data($sql); + return delete_data($query); } /** - * Retrieves items from the river. All parameters are optional. + * Get river items + * + * @note If using types and subtypes in a query, they are joined with an AND. + * + * @param array $options Parameters: + * ids => INT|ARR River item id(s) + * subject_guids => INT|ARR Subject guid(s) + * object_guids => INT|ARR Object guid(s) + * annotation_ids => INT|ARR The identifier of the annotation(s) + * action_types => STR|ARR The river action type(s) identifier + * posted_time_lower => INT The lower bound on the time posted + * posted_time_upper => INT The upper bound on the time posted + * + * types => STR|ARR Entity type string(s) + * subtypes => STR|ARR Entity subtype string(s) + * type_subtype_pairs => ARR Array of type => subtype pairs where subtype + * can be an array of subtype strings * - * @param int|array $subject_guid Acting entity to restrict to. Default: all - * @param int|array $object_guid Entity being acted on to restrict to. Default: all - * @param string $subject_relationship If set to a relationship type, this will use - * $subject_guid as the starting point and set the - * subjects to be all users this entity has this - * relationship with (eg 'friend'). Default: blank - * @param string $type The type of entity to restrict to. Default: all - * @param string $subtype The subtype of entity to restrict to. Default: all - * @param string $action_type The type of river action to restrict to. Default: all - * @param int $limit The number of items to retrieve. Default: 20 - * @param int $offset The page offset. Default: 0 - * @param int $posted_min The minimum time period to look at. Default: none - * @param int $posted_max The maximum time period to look at. Default: none + * relationship => STR Relationship identifier + * relationship_guid => INT|ARR Entity guid(s) + * inverse_relationship => BOOL Subject or object of the relationship (false) * - * @return array|false Depending on success + * limit => INT Number to show per page (20) + * offset => INT Offset in list (0) + * count => BOOL Count the river items? (false) + * order_by => STR Order by clause (rv.posted desc) + * group_by => STR Group by clause + * + * @return array|int + * @since 1.8.0 */ -function elgg_get_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '', -$type = '', $subtype = '', $action_type = '', $limit = 10, $offset = 0, $posted_min = 0, -$posted_max = 0) { - - // Get config +function elgg_get_river(array $options = array()) { global $CONFIG; - // Sanitise variables - if (!is_array($subject_guid)) { - $subject_guid = (int) $subject_guid; - } else { - foreach ($subject_guid as $key => $temp) { - $subject_guid[$key] = (int) $temp; - } - } - if (!is_array($object_guid)) { - $object_guid = (int) $object_guid; - } else { - foreach ($object_guid as $key => $temp) { - $object_guid[$key] = (int) $temp; - } - } - if (!empty($type)) { - $type = sanitise_string($type); - } - if (!empty($subtype)) { - $subtype = sanitise_string($subtype); + $defaults = array( + 'ids' => ELGG_ENTITIES_ANY_VALUE, + + 'subject_guids' => ELGG_ENTITIES_ANY_VALUE, + 'object_guids' => ELGG_ENTITIES_ANY_VALUE, + 'annotation_ids' => ELGG_ENTITIES_ANY_VALUE, + 'action_types' => ELGG_ENTITIES_ANY_VALUE, + + 'relationship' => NULL, + 'relationship_guid' => NULL, + 'inverse_relationship' => FALSE, + + 'types' => ELGG_ENTITIES_ANY_VALUE, + 'subtypes' => ELGG_ENTITIES_ANY_VALUE, + 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, + + 'posted_time_lower' => ELGG_ENTITIES_ANY_VALUE, + 'posted_time_upper' => ELGG_ENTITIES_ANY_VALUE, + + 'limit' => 20, + 'offset' => 0, + 'count' => FALSE, + + 'order_by' => 'rv.posted desc', + 'group_by' => ELGG_ENTITIES_ANY_VALUE, + + 'wheres' => array(), + 'joins' => array(), + ); + + $options = array_merge($defaults, $options); + + $singulars = array('id', 'subject_guid', 'object_guid', 'annotation_id', 'action_type', 'type', 'subtype'); + $options = elgg_normalise_plural_options_array($options, $singulars); + + $wheres = $options['wheres']; + + $wheres[] = elgg_get_guid_based_where_sql('rv.id', $options['ids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.subject_guid', $options['subject_guids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.object_guid', $options['object_guids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.annotation_id', $options['annotation_ids']); + $wheres[] = elgg_river_get_action_where_sql($options['action_types']); + $wheres[] = elgg_get_river_type_subtype_where_sql('rv', $options['types'], + $options['subtypes'], $options['type_subtype_pairs']); + + if ($options['posted_time_lower'] && is_int($options['posted_time_lower'])) { + $wheres[] = "rv.posted >= {$options['posted_time_lower']}"; } - if (!empty($action_type)) { - $action_type = sanitise_string($action_type); + + if ($options['posted_time_upper'] && is_int($options['posted_time_upper'])) { + $wheres[] = "rv.posted <= {$options['posted_time_upper']}"; } - $limit = (int) $limit; - $offset = (int) $offset; - $posted_min = (int) $posted_min; - $posted_max = (int) $posted_max; - // Construct 'where' clauses for the river - $where = array(); - // river table does not have columns expected by get_access_sql_suffix so we modify its output - $where[] = str_replace("and enabled='yes'", '', - str_replace('owner_guid', 'subject_guid', get_access_sql_suffix_new('er', 'e'))); + $joins = $options['joins']; - if (empty($subject_relationship)) { - if (!empty($subject_guid)) { - if (!is_array($subject_guid)) { - $where[] = " subject_guid = {$subject_guid} "; - } else { - $where[] = " subject_guid in (" . implode(',', $subject_guid) . ") "; - } - } - } else { - if (!is_array($subject_guid)) { - $entities = elgg_get_entities_from_relationship(array( - 'relationship' => $subject_relationship, - 'relationship_guid' => $subject_guid, - 'limit' => 9999, - )); - if (is_array($entities) && !empty($entities)) { - $guids = array(); - foreach ($entities as $entity) { - $guids[] = (int) $entity->guid; - } - // $guids[] = $subject_guid; - $where[] = " subject_guid in (" . implode(',', $guids) . ") "; - } else { - return array(); - } + if ($options['relationship_guid']) { + $clauses = elgg_get_entity_relationship_where_sql( + 'rv.subject_guid', + $options['relationship'], + $options['relationship_guid'], + $options['inverse_relationship']); + if ($clauses) { + $wheres = array_merge($wheres, $clauses['wheres']); + $joins = array_merge($joins, $clauses['joins']); } } - if (!empty($object_guid)) { - if (!is_array($object_guid)) { - $where[] = " object_guid = {$object_guid} "; - } else { - $where[] = " object_guid in (" . implode(',', $object_guid) . ") "; + + // see if any functions failed + // remove empty strings on successful functions + foreach ($wheres as $i => $where) { + if ($where === FALSE) { + return FALSE; + } elseif (empty($where)) { + unset($wheres[$i]); } } - if (!empty($type)) { - $where[] = " er.type = '{$type}' "; - } - if (!empty($subtype)) { - $where[] = " er.subtype = '{$subtype}' "; - } - if (!empty($action_type)) { - $where[] = " action_type = '{$action_type}' "; + + // remove identical where clauses + $wheres = array_unique($wheres); + + if (!$options['count']) { + $query = "SELECT DISTINCT rv.* FROM {$CONFIG->dbprefix}river rv "; + } else { + $query = "SELECT count(DISTINCT rv.id) as total FROM {$CONFIG->dbprefix}river rv "; } - if (!empty($posted_min)) { - $where[] = " posted > {$posted_min} "; + + // add joins + foreach ($joins as $j) { + $query .= " $j "; } - if (!empty($posted_max)) { - $where[] = " posted < {$posted_max} "; + + // add wheres + $query .= ' WHERE '; + + foreach ($wheres as $w) { + $query .= " $w AND "; } - $whereclause = implode(' and ', $where); + $query .= elgg_river_get_access_sql(); - // Construct main SQL - $sql = "select er.*" . - " from {$CONFIG->dbprefix}river er, {$CONFIG->dbprefix}entities e " . - " where {$whereclause} AND er.object_guid = e.guid GROUP BY object_guid " . - " ORDER BY e.last_action desc LIMIT {$offset}, {$limit}"; + if (!$options['count']) { + $options['group_by'] = sanitise_string($options['group_by']); + if ($options['group_by']) { + $query .= " GROUP BY {$options['group_by']}"; + } + + $options['order_by'] = sanitise_string($options['order_by']); + $query .= " ORDER BY {$options['order_by']}"; + + if ($options['limit']) { + $limit = sanitise_int($options['limit']); + $offset = sanitise_int($options['offset'], false); + $query .= " LIMIT $offset, $limit"; + } - // Get data - return get_data($sql); + $river_items = get_data($query, 'elgg_row_to_elgg_river_item'); + _elgg_prefetch_river_entities($river_items); + + return $river_items; + } else { + $total = get_data_row($query); + return (int)$total->total; + } } /** - * Returns a human-readable representation of a river item - * - * @see get_river_items + * Prefetch entities that will be displayed in the river. * - * @param stdClass $item A river item object as returned from get_river_items - * - * @return string|false Depending on success + * @param ElggRiverItem[] $river_items + * @access private */ -function elgg_view_river_item($item) { - if (isset($item->view)) { - $object = get_entity($item->object_guid); - $subject = get_entity($item->subject_guid); - if (!$object || !$subject) { - // probably means an entity is disabled - return false; - } else { - if (elgg_view_exists($item->view)) { - $body = elgg_view($item->view, array( - 'item' => $item - )); - } +function _elgg_prefetch_river_entities(array $river_items) { + // prefetch objects and subjects + $guids = array(); + foreach ($river_items as $item) { + if ($item->subject_guid && !_elgg_retrieve_cached_entity($item->subject_guid)) { + $guids[$item->subject_guid] = true; + } + if ($item->object_guid && !_elgg_retrieve_cached_entity($item->object_guid)) { + $guids[$item->object_guid] = true; + } + } + if ($guids) { + // avoid creating oversized query + // @todo how to better handle this? + $guids = array_slice($guids, 0, 300, true); + // return value unneeded, just priming cache + elgg_get_entities(array( + 'guids' => array_keys($guids), + 'limit' => 0, + )); + } + + // prefetch object containers + $guids = array(); + foreach ($river_items as $item) { + $object = $item->getObjectEntity(); + if ($object->container_guid && !_elgg_retrieve_cached_entity($object->container_guid)) { + $guids[$object->container_guid] = true; } - return elgg_view('river/item/wrapper', array( - 'item' => $item, - 'body' => $body + } + if ($guids) { + $guids = array_slice($guids, 0, 300, true); + elgg_get_entities(array( + 'guids' => array_keys($guids), + 'limit' => 0, )); } - return false; } /** - * Returns a human-readable version of the river. + * List river items * - * @param int|array $subject_guid Acting entity to restrict to. Default: all - * @param int|array $object_guid Entity being acted on to restrict to. Default: all - * @param string $subject_relationship If set to a relationship type, this will use - * $subject_guid as the starting point and set - * the subjects to be all users this entity has this - * relationship with (eg 'friend'). Default: blank - * @param string $type The type of entity to restrict to. Default: all - * @param string $subtype The subtype of entity to restrict to. Default: all - * @param string $action_type The type of river action to restrict to. Default: all - * @param int $limit The number of items to retrieve. Default: 20 - * @param int $posted_min The minimum time period to look at. Default: none - * @param int $posted_max The maximum time period to look at. Default: none - * @param bool $pagination Show pagination? - * @param $bool $chronological Show in chronological order? + * @param array $options Any options from elgg_get_river() plus: + * pagination => BOOL Display pagination links (true) * - * @return string Human-readable river. + * @return string + * @since 1.8.0 */ -function elgg_view_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '', -$type = '', $subtype = '', $action_type = '', $limit = 20, $posted_min = 0, -$posted_max = 0, $pagination = true, $chronological = false) { - - // Get input from outside world and sanitise it - $offset = (int) get_input('offset', 0); +function elgg_list_river(array $options = array()) { + global $autofeed; + $autofeed = true; + + $defaults = array( + 'offset' => (int) max(get_input('offset', 0), 0), + 'limit' => (int) max(get_input('limit', 20), 0), + 'pagination' => TRUE, + 'list_class' => 'elgg-list-river elgg-river', // @todo remove elgg-river in Elgg 1.9 + ); + + $options = array_merge($defaults, $options); + + if (!$options["limit"] && !$options["offset"]) {
+ // no need for pagination if listing is unlimited
+ $options["pagination"] = false;
+ } + + $options['count'] = TRUE; + $count = elgg_get_river($options); + + $options['count'] = FALSE; + $items = elgg_get_river($options); + + $options['count'] = $count; + $options['items'] = $items; + + return elgg_view('page/components/list', $options); +} - // Get the correct function - if ($chronological == true) { - $riveritems = get_river_items($subject_guid, $object_guid, $subject_relationship, $type, - $subtype, $action_type, ($limit + 1), $offset, $posted_min, $posted_max); - } else { - $riveritems = elgg_get_river_items($subject_guid, $object_guid, $subject_relationship, $type, - $subtype, $action_type, ($limit + 1), $offset, $posted_min, $posted_max); +/** + * Convert a database row to a new ElggRiverItem + * + * @param stdClass $row Database row from the river table + * + * @return ElggRiverItem + * @since 1.8.0 + * @access private + */ +function elgg_row_to_elgg_river_item($row) { + if (!($row instanceof stdClass)) { + return NULL; } - // Get river items, if they exist - if ($riveritems) { - - return elgg_view('river/item/list', array( - 'limit' => $limit, - 'offset' => $offset, - 'items' => $riveritems, - 'pagination' => $pagination - )); - - } + return new ElggRiverItem($row); +} - return ''; +/** + * Get the river's access where clause + * + * @return string + * @since 1.8.0 + * @access private + */ +function elgg_river_get_access_sql() { + // rewrite default access where clause to work with river table + return str_replace("and enabled='yes'", '', + str_replace('owner_guid', 'rv.subject_guid', + str_replace('access_id', 'rv.access_id', get_access_sql_suffix()))); } /** - * This function has been added here until we decide if it is going to roll into core or not - * Add access restriction sql code to a given query. - * Note that if this code is executed in privileged mode it will return blank. + * Returns SQL where clause for type and subtype on river table * - * @TODO: DELETE once Query classes are fully integrated + * @internal This is a simplified version of elgg_get_entity_type_subtype_where_sql() + * which could be used for all queries once the subtypes have been denormalized. * - * @param string $table_prefix_one Optional table. prefix for the access code. - * @param string $table_prefix_two Another optiona table prefix? - * @param int $owner Owner GUID + * @param string $table 'rv' + * @param NULL|array $types Array of types or NULL if none. + * @param NULL|array $subtypes Array of subtypes or NULL if none + * @param NULL|array $pairs Array of pairs of types and subtypes * * @return string + * @since 1.8.0 + * @access private */ -function get_access_sql_suffix_new($table_prefix_one = '', $table_prefix_two = '', $owner = null) { - global $ENTITY_SHOW_HIDDEN_OVERRIDE, $CONFIG; - - $sql = ""; - $friends_bit = ""; - $enemies_bit = ""; - - if ($table_prefix_one) { - $table_prefix_one = sanitise_string($table_prefix_one) . "."; +function elgg_get_river_type_subtype_where_sql($table, $types, $subtypes, $pairs) { + // short circuit if nothing is requested + if (!$types && !$subtypes && !$pairs) { + return ''; } - if ($table_prefix_two) { - $table_prefix_two = sanitise_string($table_prefix_two) . "."; - } + $wheres = array(); + $types_wheres = array(); + $subtypes_wheres = array(); - if (!isset($owner)) { - $owner = get_loggedin_userid(); - } - - if (!$owner) { - $owner = -1; - } + // if no pairs, use types and subtypes + if (!is_array($pairs)) { + if ($types) { + if (!is_array($types)) { + $types = array($types); + } + foreach ($types as $type) { + $type = sanitise_string($type); + $types_wheres[] = "({$table}.type = '$type')"; + } + } - $ignore_access = elgg_check_access_overrides($owner); - $access = get_access_list($owner); + if ($subtypes) { + if (!is_array($subtypes)) { + $subtypes = array($subtypes); + } + foreach ($subtypes as $subtype) { + $subtype = sanitise_string($subtype); + $subtypes_wheres[] = "({$table}.subtype = '$subtype')"; + } + } - if ($ignore_access) { - $sql = " (1 = 1) "; - } else if ($owner != -1) { - $friends_bit = "{$table_prefix_one}access_id = " . ACCESS_FRIENDS . " - AND {$table_prefix_one}owner_guid IN ( - SELECT guid_one FROM {$CONFIG->dbprefix}entity_relationships - WHERE relationship='friend' AND guid_two=$owner - )"; + if (is_array($types_wheres) && count($types_wheres)) { + $types_wheres = array(implode(' OR ', $types_wheres)); + } - $friends_bit = '(' . $friends_bit . ') OR '; + if (is_array($subtypes_wheres) && count($subtypes_wheres)) { + $subtypes_wheres = array('(' . implode(' OR ', $subtypes_wheres) . ')'); + } - if ((isset($CONFIG->user_block_and_filter_enabled)) && ($CONFIG->user_block_and_filter_enabled)) { - // check to see if the user is in the entity owner's block list - // or if the entity owner is in the user's filter list - // if so, disallow access - $enemies_bit = get_annotation_sql('elgg_block_list', "{$table_prefix_one}owner_guid", - $owner, false); + $wheres = array(implode(' AND ', array_merge($types_wheres, $subtypes_wheres))); - $enemies_bit = '(' - . $enemies_bit - . ' AND ' . get_annotation_sql('elgg_filter_list', $owner, "{$table_prefix_one}owner_guid", - false) - . ')'; + } else { + // using type/subtype pairs + foreach ($pairs as $paired_type => $paired_subtypes) { + $paired_type = sanitise_string($paired_type); + if (is_array($paired_subtypes)) { + $paired_subtypes = array_map('sanitise_string', $paired_subtypes); + $paired_subtype_str = implode("','", $paired_subtypes); + if ($paired_subtype_str) { + $wheres[] = "({$table}.type = '$paired_type'" + . " AND {$table}.subtype IN ('$paired_subtype_str'))"; + } + } else { + $paired_subtype = sanitise_string($paired_subtypes); + $wheres[] = "({$table}.type = '$paired_type'" + . " AND {$table}.subtype = '$paired_subtype')"; + } } } - if (empty($sql)) { - $sql = " $friends_bit ({$table_prefix_one}access_id IN {$access} - OR ({$table_prefix_one}owner_guid = {$owner}) - OR ( - {$table_prefix_one}access_id = " . ACCESS_PRIVATE . " - AND {$table_prefix_one}owner_guid = $owner - ) - )"; + if (is_array($wheres) && count($wheres)) { + $where = implode(' OR ', $wheres); + return "($where)"; + } + + return ''; +} + +/** + * Get the where clause based on river action type strings + * + * @param array $types Array of action type strings + * + * @return string + * @since 1.8.0 + * @access private + */ +function elgg_river_get_action_where_sql($types) { + if (!$types) { + return ''; } - if ($enemies_bit) { - $sql = "$enemies_bit AND ($sql)"; + if (!is_array($types)) { + $types = sanitise_string($types); + return "(rv.action_type = '$types')"; } - if (!$ENTITY_SHOW_HIDDEN_OVERRIDE) { - $sql .= " and {$table_prefix_two}enabled='yes'"; + // sanitize types array + $types_sanitized = array(); + foreach ($types as $type) { + $types_sanitized[] = sanitise_string($type); } - return '(' . $sql . ')'; + $type_str = implode("','", $types_sanitized); + return "(rv.action_type IN ('$type_str'))"; } /** - * Construct and execute the query required for the activity stream. - * - * @deprecated 1.8 + * Get the where clause based on river view strings * - * @param int $limit Limit the query. - * @param int $offset Execute from the given object - * @param mixed $type A type, or array of types to look for. - * Note: This is how they appear in the SYSTEM LOG. - * @param mixed $subtype A subtype, or array of types to look for. - * Note: This is how they appear in the SYSTEM LOG. - * @param mixed $owner_guid The guid or a collection of GUIDs - * @param string $owner_relationship If defined, the relationship between $owner_guid and - * the entity owner_guid - so "is $owner_guid $owner_relationship - * with $entity->owner_guid" + * @param array $views Array of view strings * - * @return array An array of system log entries. + * @return string + * @since 1.8.0 + * @access private */ -function get_activity_stream_data($limit = 10, $offset = 0, $type = "", $subtype = "", -$owner_guid = "", $owner_relationship = "") { - - global $CONFIG; - - $limit = (int)$limit; - $offset = (int)$offset; - - if ($type) { - if (!is_array($type)) { - $type = array(sanitise_string($type)); - } else { - foreach ($type as $k => $v) { - $type[$k] = sanitise_string($v); - } - } +function elgg_river_get_view_where_sql($views) { + if (!$views) { + return ''; } - if ($subtype) { - if (!is_array($subtype)) { - $subtype = array(sanitise_string($subtype)); - } else { - foreach ($subtype as $k => $v) { - $subtype[$k] = sanitise_string($v); - } - } + if (!is_array($views)) { + $views = sanitise_string($views); + return "(rv.view = '$views')"; } - if ($owner_guid) { - if (is_array($owner_guid)) { - foreach ($owner_guid as $k => $v) { - $owner_guid[$k] = (int)$v; - } - } else { - $owner_guid = array((int)$owner_guid); - } + // sanitize views array + $views_sanitized = array(); + foreach ($views as $view) { + $views_sanitized[] = sanitise_string($view); } - $owner_relationship = sanitise_string($owner_relationship); - - // Get a list of possible views - $activity_events = array(); - $activity_views = array_merge(elgg_view_tree('activity', 'default'), - elgg_view_tree('river', 'default')); - - $done = array(); + $view_str = implode("','", $views_sanitized); + return "(rv.view IN ('$view_str'))"; +} - foreach ($activity_views as $view) { - $fragments = explode('/', $view); - $tmp = explode('/', $view, 2); - $tmp = $tmp[1]; +/** + * Sets the access ID on river items for a particular object + * + * @param int $object_guid The GUID of the entity + * @param int $access_id The access ID + * + * @return bool Depending on success + */ +function update_river_access_by_object($object_guid, $access_id) { + // Sanitise + $object_guid = (int) $object_guid; + $access_id = (int) $access_id; - if ((isset($fragments[0])) && (($fragments[0] == 'river') || ($fragments[0] == 'activity')) - && (!in_array($tmp, $done))) { + // Load config + global $CONFIG; - if (isset($fragments[1])) { - $f = array(); - for ($n = 1; $n < count($fragments); $n++) { - $val = sanitise_string($fragments[$n]); - switch($n) { - case 1: $key = 'type'; break; - case 2: $key = 'subtype'; break; - case 3: $key = 'event'; break; - } - $f[$key] = $val; - } + // Remove + $query = "update {$CONFIG->dbprefix}river + set access_id = {$access_id} + where object_guid = {$object_guid}"; + return update_data($query); +} - // Filter result based on parameters - $add = true; - if ($type) { - if (!in_array($f['type'], $type)) { - $add = false; - } - } - if (($add) && ($subtype)) { - if (!in_array($f['subtype'], $subtype)) { - $add = false; - } - } - if (($add) && ($event)) { - if (!in_array($f['event'], $event)) { - $add = false; - } - } +/** + * Page handler for activity + * + * @param array $page + * @return bool + * @access private + */ +function elgg_river_page_handler($page) { + global $CONFIG; - if ($add) { - $activity_events[] = $f; - } - } + elgg_set_page_owner_guid(elgg_get_logged_in_user_guid()); - $done[] = $tmp; - } + // make a URL segment available in page handler script + $page_type = elgg_extract(0, $page, 'all'); + $page_type = preg_replace('[\W]', '', $page_type); + if ($page_type == 'owner') { + $page_type = 'mine'; } + set_input('page_type', $page_type); - $n = 0; - foreach ($activity_events as $details) { - // Get what we're talking about - if ($details['subtype'] == 'default') { - $details['subtype'] = ''; - } - - if (($details['type']) && ($details['event'])) { - if ($n > 0) { - $obj_query .= " or "; - } - - $access = ""; - if ($details['type'] != 'relationship') { - $access = " and " . get_access_sql_suffix('sl'); - } - - $obj_query .= "( sl.object_type='{$details['type']}' - AND sl.object_subtype='{$details['subtype']}' - AND sl.event='{$details['event']}' $access )"; + require_once("{$CONFIG->path}pages/river.php"); + return true; +} - $n++; - } - } +/** + * Register river unit tests + * @access private + */ +function elgg_river_test($hook, $type, $value) { + global $CONFIG; + $value[] = $CONFIG->path . 'engine/tests/api/river.php'; + return $value; +} - // User - if ((count($owner_guid)) && ($owner_guid[0] != 0)) { - $user = " and sl.performed_by_guid in (" . implode(',', $owner_guid) . ")"; - - if ($owner_relationship) { - $friendsarray = ""; - if ($friends = elgg_get_entities_from_relationship(array( - 'relationship' => $owner_relationship, - 'relationship_guid' => $owner_guid[0], - 'inverse_relationship' => FALSE, - 'types' => 'user', - 'subtypes' => $subtype, - 'limit' => 9999)) - ) { - - $friendsarray = array(); - foreach ($friends as $friend) { - $friendsarray[] = $friend->getGUID(); - } +/** + * Initialize river library + * @access private + */ +function elgg_river_init() { + elgg_register_page_handler('activity', 'elgg_river_page_handler'); + $item = new ElggMenuItem('activity', elgg_echo('activity'), 'activity'); + elgg_register_menu_item('site', $item); + + elgg_register_widget_type('river_widget', elgg_echo('river:widget:title'), elgg_echo('river:widget:description')); - $user = " and sl.performed_by_guid in (" . implode(',', $friendsarray) . ")"; - } - } - } + elgg_register_action('river/delete', '', 'admin'); - $query = "SELECT sl.* FROM {$CONFIG->dbprefix}system_log sl - WHERE 1 $user AND ($obj_query) - ORDER BY sl.time_created desc limit $offset, $limit"; - return get_data($query); + elgg_register_plugin_hook_handler('unit_test', 'system', 'elgg_river_test'); } + +elgg_register_event_handler('init', 'system', 'elgg_river_init'); diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php index 4cdc9bcce..e3d5ce9cd 100644 --- a/engine/lib/sessions.php +++ b/engine/lib/sessions.php @@ -18,9 +18,9 @@ global $SESSION; * hook - 'session:get' 'user' to give plugin authors another * way to provide user details to the ACL system without touching the session. * - * @return ElggUser|NULL + * @return ElggUser */ -function get_loggedin_user() { +function elgg_get_logged_in_user_entity() { global $SESSION; if (isset($SESSION)) { @@ -33,11 +33,11 @@ function get_loggedin_user() { /** * Return the current logged in user by id. * - * @see get_loggedin_user() + * @see elgg_get_logged_in_user_entity() * @return int */ -function get_loggedin_userid() { - $user = get_loggedin_user(); +function elgg_get_logged_in_user_guid() { + $user = elgg_get_logged_in_user_entity(); if ($user) { return $user->guid; } @@ -50,9 +50,8 @@ function get_loggedin_userid() { * * @return bool */ -function isloggedin() { - - $user = get_loggedin_user(); +function elgg_is_logged_in() { + $user = elgg_get_logged_in_user_entity(); if ((isset($user)) && ($user instanceof ElggUser) && ($user->guid > 0)) { return true; @@ -64,14 +63,12 @@ function isloggedin() { /** * Returns whether or not the user is currently logged in and that they are an admin user. * - * @uses isloggedin() * @return bool */ -function isadminloggedin() { - - $user = get_loggedin_user(); +function elgg_is_admin_logged_in() { + $user = elgg_get_logged_in_user_entity(); - if ((isloggedin()) && $user->isAdmin()) { + if ((elgg_is_logged_in()) && $user->isAdmin()) { return TRUE; } @@ -90,6 +87,9 @@ function isadminloggedin() { */ function elgg_is_admin_user($user_guid) { global $CONFIG; + + $user_guid = (int)$user_guid; + // cannot use magic metadata here because of recursion // must support the old way of getting admin from metadata @@ -130,12 +130,17 @@ function elgg_is_admin_user($user_guid) { /** * Perform user authentication with a given username and password. * + * @warning This returns an error message on failure. Use the identical operator to check + * for access: if (true === elgg_authenticate()) { ... }. + * + * * @see login * * @param string $username The username * @param string $password The password * * @return true|string True or an error message on failure + * @access private */ function elgg_authenticate($username, $password) { $pam = new ElggPAM('user'); @@ -148,28 +153,6 @@ function elgg_authenticate($username, $password) { } /** - * Perform standard authentication with a given username and password. - * Returns an ElggUser object for use with login. - * - * @see login - * - * @param string $username The username, optionally (for standard logins) - * @param string $password The password, optionally (for standard logins) - * - * @return ElggUser|false The authenticated user object, or false on failure. - */ -function authenticate($username, $password) { - elgg_deprecated_notice('authenticate() has been deprecated for elgg_authenticate()', 1.8); - $pam = new ElggPAM('user'); - $credentials = array('username' => $username, 'password' => $password); - $result = $pam->authenticate($credentials); - if ($result) { - return get_user_by_username($username); - } - return false; -} - -/** * Hook into the PAM system which accepts a username and password and attempts to authenticate * it against a known user. * @@ -179,10 +162,11 @@ function authenticate($username, $password) { * * @return bool * @throws LoginException + * @access private */ -function pam_auth_userpass($credentials = NULL) { +function pam_auth_userpass(array $credentials = array()) { - if (!is_array($credentials) && (!$credentials['username']) && (!$credentials['password'])) { + if (!isset($credentials['username']) || !isset($credentials['password'])) { return false; } @@ -198,7 +182,7 @@ function pam_auth_userpass($credentials = NULL) { if ($user->password !== generate_user_password($user, $credentials['password'])) { log_login_failure($user->guid); throw new LoginException(elgg_echo('LoginException:PasswordFailure')); - } + } return true; } @@ -208,7 +192,7 @@ function pam_auth_userpass($credentials = NULL) { * * @param int $user_guid User GUID * - * @return bool on success + * @return bool */ function log_login_failure($user_guid) { $user_guid = (int)$user_guid; @@ -305,8 +289,6 @@ function check_rate_limit_exceeded($user_guid) { * @throws LoginException */ function login(ElggUser $user, $persistent = false) { - global $CONFIG; - // User is banned, return false. if ($user->isBanned()) { throw new LoginException(elgg_echo('LoginException:BannedUser')); @@ -344,6 +326,12 @@ function login(ElggUser $user, $persistent = false) { set_last_login($_SESSION['guid']); reset_login_failure_count($user->guid); // Reset any previous failed login attempts + // if memcache is enabled, invalidate the user in memcache @see https://github.com/Elgg/Elgg/issues/3143 + if (is_memcache_available()) { + // this needs to happen with a shutdown function because of the timing with set_last_login() + register_shutdown_function("_elgg_invalidate_memcache_for_entity", $_SESSION['guid']); + } + return true; } @@ -353,8 +341,6 @@ function login(ElggUser $user, $persistent = false) { * @return bool */ function logout() { - global $CONFIG; - if (isset($_SESSION['user'])) { if (!elgg_trigger_event('logout', 'user', $_SESSION['user'])) { return false; @@ -369,7 +355,7 @@ function logout() { unset($_SESSION['guid']); unset($_SESSION['id']); unset($_SESSION['user']); - + setcookie("elggperm", "", (time() - (86400 * 30)), "/"); // pass along any messages @@ -378,7 +364,7 @@ function logout() { session_destroy(); // starting a default session to store any post-logout messages. - session_init(NULL, NULL, NULL); + _elgg_session_boot(NULL, NULL, NULL); $_SESSION['msg'] = $old_msg; return TRUE; @@ -395,13 +381,10 @@ function logout() { * * @uses $_SESSION * - * @param string $event Event name - * @param string $object_type Object type - * @param mixed $object Object - * * @return bool + * @access private */ -function session_init($event, $object_type, $object) { +function _elgg_session_boot() { global $DB_PREFIX, $CONFIG; // Use database for sessions @@ -466,8 +449,8 @@ function session_init($event, $object_type, $object) { set_last_action($_SESSION['guid']); } - elgg_register_action("login", '', 'public'); - elgg_register_action("logout"); + elgg_register_action('login', '', 'public'); + elgg_register_action('logout'); // Register a default PAM handler register_pam_handler('pam_auth_userpass'); @@ -482,9 +465,6 @@ function session_init($event, $object_type, $object) { return false; } - // Since we have loaded a new user, this user may have different language preferences - register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/"); - return true; } @@ -494,7 +474,7 @@ function session_init($event, $object_type, $object) { * @return void */ function gatekeeper() { - if (!isloggedin()) { + if (!elgg_is_logged_in()) { $_SESSION['last_forward_from'] = current_page_url(); register_error(elgg_echo('loggedinrequired')); forward('', 'login'); @@ -509,7 +489,7 @@ function gatekeeper() { function admin_gatekeeper() { gatekeeper(); - if (!isadminloggedin()) { + if (!elgg_is_admin_logged_in()) { $_SESSION['last_forward_from'] = current_page_url(); register_error(elgg_echo('adminrequired')); forward('', 'admin'); @@ -524,6 +504,7 @@ function admin_gatekeeper() { * * @return true * @todo Document + * @access private */ function _elgg_session_open($save_path, $session_name) { global $sess_save_path; @@ -539,6 +520,7 @@ function _elgg_session_open($save_path, $session_name) { * @todo document * * @return true + * @access private */ function _elgg_session_close() { return true; @@ -550,6 +532,7 @@ function _elgg_session_close() { * @param string $id The session ID * * @return string + * @access private */ function _elgg_session_read($id) { global $DB_PREFIX; @@ -583,6 +566,7 @@ function _elgg_session_read($id) { * @param mixed $sess_data Session data * * @return bool + * @access private */ function _elgg_session_write($id, $sess_data) { global $DB_PREFIX; @@ -622,6 +606,7 @@ function _elgg_session_write($id, $sess_data) { * @param string $id Session ID * * @return bool + * @access private */ function _elgg_session_destroy($id) { global $DB_PREFIX; @@ -636,10 +621,8 @@ function _elgg_session_destroy($id) { global $sess_save_path; $sess_file = "$sess_save_path/sess_$id"; - return(@unlink($sess_file)); + return @unlink($sess_file); } - - return false; } /** @@ -648,6 +631,7 @@ function _elgg_session_destroy($id) { * @param int $maxlifetime Max age of a session * * @return bool + * @access private */ function _elgg_session_gc($maxlifetime) { global $DB_PREFIX; @@ -670,5 +654,3 @@ function _elgg_session_gc($maxlifetime) { return true; } - -elgg_register_event_handler("boot", "system", "session_init", 20); diff --git a/engine/lib/sites.php b/engine/lib/sites.php index 97398d7f2..3de0eccc2 100644 --- a/engine/lib/sites.php +++ b/engine/lib/sites.php @@ -18,11 +18,19 @@ function elgg_get_site_entity($site_guid = 0) { global $CONFIG; + $result = false; + if ($site_guid == 0) { - return $CONFIG->site; + $site = $CONFIG->site; + } else { + $site = get_entity($site_guid); + } + + if ($site instanceof ElggSite) { + $result = $site; } - return get_entity($site_guid); + return $result; } /** @@ -31,6 +39,7 @@ function elgg_get_site_entity($site_guid = 0) { * @param int $guid The site GUID * * @return mixed + * @access private */ function get_site_entity_as_row($guid) { global $CONFIG; @@ -40,7 +49,7 @@ function get_site_entity_as_row($guid) { } /** - * Create or update the extras table for a given site. + * Create or update the entities table for a given site. * Call create_entity first. * * @param int $guid Site GUID @@ -49,6 +58,7 @@ function get_site_entity_as_row($guid) { * @param string $url URL of the site * * @return bool + * @access private */ function create_site_entity($guid, $name, $description, $url) { global $CONFIG; @@ -100,15 +110,6 @@ function create_site_entity($guid, $name, $description, $url) { } /** - * @deprecated 1.6 - * @return 1 - */ -function delete_site_entity($guid) { - elgg_deprecated_notice("delete_site_entity has been deprecated", 1.6); - return 1; // Always return that we have deleted one row in order to not break existing code. -} - -/** * Add a user to a site. * * @param int $site_guid Site guid @@ -117,8 +118,6 @@ function delete_site_entity($guid) { * @return bool */ function add_site_user($site_guid, $user_guid) { - global $CONFIG; - $site_guid = (int)$site_guid; $user_guid = (int)$user_guid; @@ -141,55 +140,6 @@ function remove_site_user($site_guid, $user_guid) { } /** - * Get the members of a site. - * - * @param int $site_guid Site GUID - * @param int $limit User GUID - * @param int $offset Offset - * - * @return mixed - * @deprecated 1.8 Use ElggSite::getMembers() - */ -function get_site_members($site_guid, $limit = 10, $offset = 0) { - elgg_deprecated_notice("get_site_members() deprecated. - Use ElggSite::getMembers()", 1.8); - - $site = get_entity($site_guid); - if ($site) { - return $site->getMembers($limit, $offset); - } - - return false; -} - -/** - * Display a list of site members - * - * @param int $site_guid The GUID of the site - * @param int $limit The number of members to display on a page - * @param bool $fullview Whether or not to display the full view (default: true) - * - * @return string A displayable list of members - * @deprecated 1.8 Use ElggSite::listMembers() - */ -function list_site_members($site_guid, $limit = 10, $fullview = true) { - elgg_deprecated_notice("list_site_members() deprecated. - Use ElggSite::listMembers()", 1.8); - - $options = array( - 'limit' => $limit, - 'full_view' => $full_view, - ); - - $site = get_entity($site_guid); - if ($site) { - return $site->listMembers($options); - } - - return ''; -} - -/** * Add an object to a site. * * @param int $site_guid Site GUID @@ -198,8 +148,6 @@ function list_site_members($site_guid, $limit = 10, $fullview = true) { * @return mixed */ function add_site_object($site_guid, $object_guid) { - global $CONFIG; - $site_guid = (int)$site_guid; $object_guid = (int)$object_guid; @@ -240,74 +188,8 @@ function get_site_objects($site_guid, $subtype = "", $limit = 10, $offset = 0) { 'relationship' => 'member_of_site', 'relationship_guid' => $site_guid, 'inverse_relationship' => TRUE, - 'types' => 'object', - 'subtypes' => $subtype, - 'limit' => $limit, - 'offset' => $offset - )); -} - -/** - * Add a collection to a site. - * - * @param int $site_guid Site GUID - * @param int $collection_guid Collection GUID - * - * @return mixed - * @deprecated 1.8 - */ -function add_site_collection($site_guid, $collection_guid) { - elgg_deprecated_notice("add_site_collection has been deprecated", 1.8); - global $CONFIG; - - $site_guid = (int)$site_guid; - $collection_guid = (int)$collection_guid; - - return add_entity_relationship($collection_guid, "member_of_site", $site_guid); -} - -/** - * Remove a collection from a site. - * - * @param int $site_guid Site GUID - * @param int $collection_guid Collection GUID - * - * @return mixed - * @deprecated 1.8 - */ -function remove_site_collection($site_guid, $collection_guid) { - elgg_deprecated_notice("remove_site_collection has been deprecated", 1.8); - $site_guid = (int)$site_guid; - $collection_guid = (int)$collection_guid; - - return remove_entity_relationship($collection_guid, "member_of_site", $site_guid); -} - -/** - * Get the collections belonging to a site. - * - * @param int $site_guid Site GUID - * @param string $subtype Subtype - * @param int $limit Limit - * @param int $offset Offset - * - * @return mixed - * @deprecated 1.8 - */ -function get_site_collections($site_guid, $subtype = "", $limit = 10, $offset = 0) { - elgg_deprecated_notice("get_site_collections has been deprecated", 1.8); - $site_guid = (int)$site_guid; - $subtype = sanitise_string($subtype); - $limit = (int)$limit; - $offset = (int)$offset; - - // collection isn't a valid type. This won't work. - return elgg_get_entities_from_relationship(array( - 'relationship' => 'member_of_site', - 'relationship_guid' => $site_guid, - 'inverse_relationship' => TRUE, - 'types' => 'collection', - 'subtypes' => $subtype, + 'type' => 'object', + 'subtype' => $subtype, 'limit' => $limit, 'offset' => $offset )); @@ -328,65 +210,13 @@ function get_site_by_url($url) { $row = get_data_row("SELECT * from {$CONFIG->dbprefix}sites_entity where url='$url'"); if ($row) { - return new ElggSite($row); + return get_entity($row->guid); } return false; } /** - * Searches for a site based on a complete or partial name - * or description or url using full text searching. - * - * IMPORTANT NOTE: With MySQL's default setup: - * 1) $criteria must be 4 or more characters long - * 2) If $criteria matches greater than 50% of results NO RESULTS ARE RETURNED! - * - * @param string $criteria The partial or full name or username. - * @param int $limit Limit of the search. - * @param int $offset Offset. - * @param string $order_by The order. - * @param boolean $count Whether to return the count of results or just the results. - * - * @return mixed - * @deprecated 1.7 - */ -function search_for_site($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) { - elgg_deprecated_notice('search_for_site() was deprecated by new search plugin.', 1.7); - global $CONFIG; - - $criteria = sanitise_string($criteria); - $limit = (int)$limit; - $offset = (int)$offset; - $order_by = sanitise_string($order_by); - - $access = get_access_sql_suffix("e"); - - if ($order_by == "") { - $order_by = "e.time_created desc"; - } - - if ($count) { - $query = "SELECT count(e.guid) as total "; - } else { - $query = "SELECT e.* "; - } - $query .= "from {$CONFIG->dbprefix}entities e - join {$CONFIG->dbprefix}sites_entity s on e.guid=s.guid - where match(s.name, s.description, s.url) against ('$criteria') and $access"; - - if (!$count) { - $query .= " order by $order_by limit $offset, $limit"; // Add order and limit - return get_data($query, "entity_row_to_elggstar"); - } else { - if ($count = get_data_row($query)) { - return $count->total; - } - } - return false; -} - -/** * Retrieve a site and return the domain portion of its url. * * @param int $guid ElggSite GUID @@ -406,53 +236,21 @@ function get_site_domain($guid) { } /** - * Initialise site handling - * - * Called at the beginning of system running, to set the ID of the current site. - * This is 0 by default, but plugins may alter this behaviour by attaching functions - * to the sites init event and changing $CONFIG->site_id. - * - * @uses $CONFIG - * - * @param string $event Event API required parameter - * @param string $object_type Event API required parameter - * @param null $object Event API required parameter - * - * @return true - */ -function sites_boot($event, $object_type, $object) { - global $CONFIG; - - $site = elgg_trigger_plugin_hook("siteid", "system"); - if ($site === null || $site === false) { - $CONFIG->site_id = (int) datalist_get('default_site'); - } else { - $CONFIG->site_id = $site; - } - $CONFIG->site_guid = $CONFIG->site_id; - $CONFIG->site = get_entity($CONFIG->site_guid); - - return true; -} - -// Register event handlers -elgg_register_event_handler('boot', 'system', 'sites_boot', 2); - -// Register with unit test -elgg_register_plugin_hook_handler('unit_test', 'system', 'sites_test'); - -/** * Unit tests for sites * - * @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 sites_test($hook, $type, $value, $params) { global $CONFIG; $value[] = "{$CONFIG->path}engine/tests/objects/sites.php"; return $value; } + +// Register with unit test +elgg_register_plugin_hook_handler('unit_test', 'system', 'sites_test'); diff --git a/engine/lib/statistics.php b/engine/lib/statistics.php index 62f4ca508..4cb0bb0b8 100644 --- a/engine/lib/statistics.php +++ b/engine/lib/statistics.php @@ -95,28 +95,32 @@ function get_number_users($show_deactivated = false) { * @return string */ function get_online_users() { - $offset = get_input('offset', 0); - $count = count(find_active_users(600, 9999)); - $objects = find_active_users(600, 10, $offset); + $limit = max(0, (int) get_input("limit", 10)); + $offset = max(0, (int) get_input("offset", 0)); + + $count = find_active_users(600, $limit, $offset, true); + $objects = find_active_users(600, $limit, $offset); if ($objects) { - return elgg_view_entity_list($objects, $count, $offset, 10, false); + return elgg_view_entity_list($objects, array( + 'count' => $count, + 'limit' => $limit, + 'offset' => $offset + )); } + return ''; } /** * Initialise the statistics admin page. * * @return void + * @access private */ function statistics_init() { - extend_elgg_admin_page('admin/statistics_opt/basic', 'admin/statistics'); - extend_elgg_admin_page('admin/statistics_opt/numentities', 'admin/statistics'); - extend_elgg_admin_page('admin/statistics_opt/online', 'admin/statistics'); - - extend_elgg_settings_page('usersettings/statistics_opt/online', 'usersettings/statistics'); - extend_elgg_settings_page('usersettings/statistics_opt/numentities', 'usersettings/statistics'); + elgg_extend_view('core/settings/statistics', 'core/settings/statistics/online'); + elgg_extend_view('core/settings/statistics', 'core/settings/statistics/numentities'); } /// Register init function -elgg_register_event_handler('init', 'system', 'statistics_init');
\ No newline at end of file +elgg_register_event_handler('init', 'system', 'statistics_init'); diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php index 5e4a145bb..84302632e 100644 --- a/engine/lib/system_log.php +++ b/engine/lib/system_log.php @@ -10,7 +10,10 @@ /** * Retrieve the system log based on a number of parameters. * + * @todo too many args, and the first arg is too confusing + * * @param int|array $by_user The guid(s) of the user(s) who initiated the event. + * Use 0 for unowned entries. Anything else falsey means anyone. * @param string $event The event you are searching on. * @param string $class The class of object it effects. * @param string $type The type @@ -21,11 +24,12 @@ * @param int $timebefore Lower time limit * @param int $timeafter Upper time limit * @param int $object_id GUID of an object - * + * @param string $ip_address The IP address. * @return mixed */ -function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "", -$limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0) { +function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "", $limit = 10, + $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0, + $ip_address = "") { global $CONFIG; @@ -37,16 +41,18 @@ $limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $obje } else { $by_user = (int)$by_user; } + $event = sanitise_string($event); $class = sanitise_string($class); $type = sanitise_string($type); $subtype = sanitise_string($subtype); + $ip_address = sanitise_string($ip_address); $limit = (int)$limit; $offset = (int)$offset; $where = array(); - if ($by_user_orig !== "") { + if ($by_user_orig !== "" && $by_user_orig !== false && $by_user_orig !== null) { if (is_int($by_user)) { $where[] = "performed_by_guid=$by_user"; } else if (is_array($by_user)) { @@ -75,6 +81,9 @@ $limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $obje if ($object_id) { $where[] = "object_id = " . ((int) $object_id); } + if ($ip_address) { + $where[] = "ip_address = '$ip_address'"; + } $select = "*"; if ($count) { @@ -91,7 +100,8 @@ $limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $obje } if ($count) { - if ($numrows = get_data_row($query)) { + $numrows = get_data_row($query); + if ($numrows) { return $numrows->count; } } else { @@ -128,9 +138,12 @@ function get_object_from_log_entry($entry_id) { if ($entry) { $class = $entry->object_class; - $tmp = new $class(); - $object = $tmp->getObjectFromID($entry->object_id); - + // surround with try/catch because object could be disabled + try { + $object = new $class($entry->object_id); + } catch (Exception $e) { + + } if ($object) { return $object; } @@ -145,17 +158,26 @@ function get_object_from_log_entry($entry_id) { * This is called by the event system and should not be called directly. * * @param object $object The object you're talking about. - * @param string $event String The event being logged - * - * @return mixed + * @param string $event The event being logged + * @return void */ function system_log($object, $event) { global $CONFIG; - static $logcache; + static $log_cache; + static $cache_size = 0; if ($object instanceof Loggable) { - if (!is_array($logcache)) { - $logcache = array(); + + /* @var ElggEntity|ElggExtender $object */ + if (datalist_get('version') < 2012012000) { + // this is a site that doesn't have the ip_address column yet + return; + } + + // reset cache if it has grown too large + if (!is_array($log_cache) || $cache_size > 500) { + $log_cache = array(); + $cache_size = 0; } // Has loggable interface, extract the necessary information and store @@ -165,7 +187,17 @@ function system_log($object, $event) { $object_subtype = $object->getSubtype(); $event = sanitise_string($event); $time = time(); - $performed_by = get_loggedin_userid(); + + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])); + } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) { + $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_REAL_IP'])); + } else { + $ip_address = $_SERVER['REMOTE_ADDR']; + } + $ip_address = sanitise_string($ip_address); + + $performed_by = elgg_get_logged_in_user_guid(); if (isset($object->access_id)) { $access_id = $object->access_id; @@ -185,20 +217,19 @@ function system_log($object, $event) { } // Create log if we haven't already created it - if (!isset($logcache[$time][$object_id][$event])) { + if (!isset($log_cache[$time][$object_id][$event])) { $query = "INSERT DELAYED into {$CONFIG->dbprefix}system_log (object_id, object_class, object_type, object_subtype, event, - performed_by_guid, owner_guid, access_id, enabled, time_created) + performed_by_guid, owner_guid, access_id, enabled, time_created, ip_address) VALUES ('$object_id','$object_class','$object_type', '$object_subtype', '$event', - $performed_by, $owner_guid, $access_id, '$enabled', '$time')"; + $performed_by, $owner_guid, $access_id, '$enabled', '$time', '$ip_address')"; insert_data($query); - $logcache[$time][$object_id][$event] = true; + $log_cache[$time][$object_id][$event] = true; + $cache_size += 1; } - - return true; } } @@ -263,6 +294,7 @@ function system_log_default_logger($event, $object_type, $object) { * @param Loggable $object Object to log * * @return true + * @access private */ function system_log_listener($event, $object_type, $object) { if (($object_type != 'systemlog') && ($event != 'log')) { @@ -276,4 +308,4 @@ function system_log_listener($event, $object_type, $object) { elgg_register_event_handler('all', 'all', 'system_log_listener', 400); /** Register a default system log handler */ -elgg_register_event_handler('log', 'systemlog', 'system_log_default_logger', 999);
\ No newline at end of file +elgg_register_event_handler('log', 'systemlog', 'system_log_default_logger', 999); diff --git a/engine/lib/tags.php b/engine/lib/tags.php index 9e81f4601..586a9b9e4 100644 --- a/engine/lib/tags.php +++ b/engine/lib/tags.php @@ -17,6 +17,7 @@ * @param int $buckets The number of buckets * * @return int + * @access private */ function calculate_tag_size($min, $max, $number_of_tags, $buckets = 6) { $delta = (($max - $min) / $buckets); @@ -47,7 +48,8 @@ function calculate_tag_size($min, $max, $number_of_tags, $buckets = 6) { * @param array $tags The array of tags. * @param int $buckets The number of buckets * - * @return An associated array of tags with a weighting, this can then be mapped to a display class. + * @return array An associated array of tags with a weighting, this can then be mapped to a display class. + * @access private */ function generate_tag_cloud(array $tags, $buckets = 6) { $cloud = array(); @@ -112,8 +114,8 @@ function generate_tag_cloud(array $tags, $buckets = 6) { * * joins => array() Additional joins * - * @return false/array - if no tags or error, false - * otherwise, array of objects with ->tag and ->total values + * @return object[]|false If no tags or error, false + * otherwise, array of objects with ->tag and ->total values * @since 1.7.1 */ function elgg_get_tags(array $options = array()) { @@ -170,6 +172,7 @@ function elgg_get_tags(array $options = array()) { // catch for tags that were spaces $wheres[] = "msv.string != ''"; + $sanitised_tags = array(); foreach ($options['tag_names'] as $tag) { $sanitised_tags[] = '"' . sanitise_string($tag) . '"'; } @@ -178,15 +181,12 @@ function elgg_get_tags(array $options = array()) { $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'], $options['subtypes'], $options['type_subtype_pairs']); - $wheres[] = elgg_get_entity_site_where_sql('e', $options['site_guids']); - $wheres[] = elgg_get_entity_owner_where_sql('e', $options['owner_guids']); - $wheres[] = elgg_get_entity_container_where_sql('e', $options['container_guids']); + $wheres[] = elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']); + $wheres[] = elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']); + $wheres[] = elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']); $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) { @@ -197,6 +197,8 @@ function elgg_get_tags(array $options = array()) { } } + // remove identical where clauses + $wheres = array_unique($wheres); $joins = $options['joins']; @@ -245,75 +247,6 @@ function elgg_get_tags(array $options = array()) { } /** - * Get an array of tags with weights for use with the output/tagcloud view. - * - * @deprecated 1.8 Use elgg_get_tags(). - * - * @param int $threshold Get the threshold of minimum number of each tags to - * bother with (ie only show tags where there are more - * than $threshold occurances) - * @param int $limit Number of tags to return - * @param string $metadata_name Optionally, the name of the field you want to grab for - * @param string $entity_type Optionally, the entity type ('object' etc) - * @param string $entity_subtype The entity subtype, optionally - * @param int $owner_guid The GUID of the tags owner, optionally - * @param int $site_guid Optionally, the site to restrict to (default is the current site) - * @param int $start_ts Optionally specify a start timestamp for tags used to - * generate cloud. - * @param int $end_ts Optionally specify an end timestamp for tags used to generate cloud - * - * @return array|false Array of objects with ->tag and ->total values, or false on failure - */ -function get_tags($threshold = 1, $limit = 10, $metadata_name = "", $entity_type = "object", -$entity_subtype = "", $owner_guid = "", $site_guid = -1, $start_ts = "", $end_ts = "") { - - elgg_deprecated_notice('get_tags() has been replaced by elgg_get_tags()', 1.8); - - if (is_array($metadata_name)) { - return false; - } - - $options = array(); - if ($metadata_name === '') { - $options['tag_names'] = array(); - } else { - $options['tag_names'] = array($metadata_name); - } - - $options['threshold'] = $threshold; - $options['limit'] = $limit; - - // rewrite owner_guid to container_guid to emulate old functionality - $container_guid = $owner_guid; - if ($container_guid) { - $options['container_guids'] = $container_guid; - } - - if ($entity_type) { - $options['type'] = $entity_type; - } - - if ($entity_subtype) { - $options['subtype'] = $entity_subtype; - } - - if ($site_guid != -1) { - $options['site_guids'] = $site_guid; - } - - if ($end_ts) { - $options['created_time_upper'] = $end_ts; - } - - if ($start_ts) { - $options['created_time_lower'] = $start_ts; - } - - $r = elgg_get_tags($options); - return $r; -} - -/** * Returns viewable tagcloud * * @see elgg_get_tags @@ -338,45 +271,10 @@ function elgg_view_tagcloud(array $options = array()) { } $tag_data = elgg_get_tags($options); - return elgg_view("output/tagcloud", array('value' => $tag_data, - 'type' => $type, - 'subtype' => $subtype)); - -} - -/** - * Loads and displays a tagcloud given particular criteria. - * - * @deprecated 1.8 use elgg_view_tagcloud() - * - * @param int $threshold Get the threshold of minimum number of each tags - * to bother with (ie only show tags where there are - * more than $threshold occurances) - * @param int $limit Number of tags to return - * @param string $metadata_name Optionally, the name of the field you want to grab for - * @param string $entity_type Optionally, the entity type ('object' etc) - * @param string $entity_subtype The entity subtype, optionally - * @param int $owner_guid The GUID of the tags owner, optionally - * @param int $site_guid Optionally, the site to restrict to (default is the current site) - * @param int $start_ts Optionally specify a start timestamp for tags used to - * generate cloud. - * @param int $end_ts Optionally specify an end timestamp for tags used to generate - * cloud. - * - * @return string The HTML (or other, depending on view type) of the tagcloud. - */ -function display_tagcloud($threshold = 1, $limit = 10, $metadata_name = "", $entity_type = "object", -$entity_subtype = "", $owner_guid = "", $site_guid = -1, $start_ts = "", $end_ts = "") { - - elgg_deprecated_notice('display_tagcloud() was deprecated by elgg_view_tagcloud()!', 1.8); - - $tags = get_tags($threshold, $limit, $metadata_name, $entity_type, - $entity_subtype, $owner_guid, $site_guid, $start_ts, $end_ts); - - return elgg_view('output/tagcloud', array( - 'value' => $tags, - 'type' => $entity_type, - 'subtype' => $entity_subtype, + return elgg_view("output/tagcloud", array( + 'value' => $tag_data, + 'type' => $type, + 'subtype' => $subtype, )); } @@ -419,32 +317,38 @@ function elgg_get_registered_tag_metadata_names() { return $names; } -// register the standard tags metadata name -elgg_register_tag_metadata_name('tags'); - -register_page_handler('tags', 'elgg_tagcloud_page_handler'); - /** * Page hander for tags * * @param array $page Page array * - * @return void + * @return bool + * @access private */ function elgg_tagcloud_page_handler($page) { - switch ($page[0]) { - default: - $title = elgg_view_title(elgg_echo('tags:site_cloud')); - $options = array( - 'threshold' => 0, - 'limit' => 100, - 'tag_name' => 'tags', - ); - $tags = elgg_view_tagcloud($options); - $content = $title . $tags; - $body = elgg_view_layout('one_column_with_sidebar', array('content' => $content)); - - echo elgg_view_page(elgg_echo('tags:site_cloud'), $body); - break; - } + + $title = elgg_view_title(elgg_echo('tags:site_cloud')); + $options = array( + 'threshold' => 0, + 'limit' => 100, + 'tag_name' => 'tags', + ); + $tags = elgg_view_tagcloud($options); + $content = $title . $tags; + $body = elgg_view_layout('one_sidebar', array('content' => $content)); + + echo elgg_view_page(elgg_echo('tags:site_cloud'), $body); + return true; +} + +/** + * @access private + */ +function elgg_tags_init() { + // register the standard tags metadata name + elgg_register_tag_metadata_name('tags'); + + elgg_register_page_handler('tags', 'elgg_tagcloud_page_handler'); } + +elgg_register_event_handler('init', 'system', 'elgg_tags_init');
\ No newline at end of file diff --git a/engine/lib/upgrade.php b/engine/lib/upgrade.php new file mode 100644 index 000000000..158ec9ec1 --- /dev/null +++ b/engine/lib/upgrade.php @@ -0,0 +1,365 @@ +<?php +/** + * Elgg upgrade library. + * Contains code for handling versioning and upgrades. + * + * @package Elgg.Core + * @subpackage Upgrade + */ + +/** + * Run any php upgrade scripts which are required + * + * @param int $version Version upgrading from. + * @param bool $quiet Suppress errors. Don't use this. + * + * @return bool + * @access private + */ +function upgrade_code($version, $quiet = FALSE) { + // do not remove - upgrade scripts depend on this + global $CONFIG; + + $version = (int) $version; + $upgrade_path = elgg_get_config('path') . 'engine/lib/upgrades/'; + $processed_upgrades = elgg_get_processed_upgrades(); + + // upgrading from 1.7 to 1.8. Need to bootstrap. + if (!$processed_upgrades) { + elgg_upgrade_bootstrap_17_to_18(); + + // grab accurate processed upgrades + $processed_upgrades = elgg_get_processed_upgrades(); + } + + $upgrade_files = elgg_get_upgrade_files($upgrade_path); + + if ($upgrade_files === false) { + return false; + } + + $upgrades = elgg_get_unprocessed_upgrades($upgrade_files, $processed_upgrades); + + // Sort and execute + sort($upgrades); + + foreach ($upgrades as $upgrade) { + $upgrade_version = elgg_get_upgrade_file_version($upgrade); + $success = true; + + // hide all errors. + if ($quiet) { + // hide include errors as well as any exceptions that might happen + try { + if (!@include("$upgrade_path/$upgrade")) { + $success = false; + error_log("Could not include $upgrade_path/$upgrade"); + } + } catch (Exception $e) { + $success = false; + error_log($e->getmessage()); + } + } else { + if (!include("$upgrade_path/$upgrade")) { + $success = false; + error_log("Could not include $upgrade_path/$upgrade"); + } + } + + if ($success) { + // incrementally set upgrade so we know where to start if something fails. + $processed_upgrades[] = $upgrade; + + // don't set the version to a lower number in instances where an upgrade + // has been merged from a lower version of Elgg + if ($upgrade_version > $version) { + datalist_set('version', $upgrade_version); + } + + elgg_set_processed_upgrades($processed_upgrades); + } else { + return false; + } + } + + return true; +} + +/** + * Saves the processed upgrades to a dataset. + * + * @param array $processed_upgrades An array of processed upgrade filenames + * (not the path, just the file) + * @return bool + * @access private + */ +function elgg_set_processed_upgrades(array $processed_upgrades) { + $processed_upgrades = array_unique($processed_upgrades); + return datalist_set('processed_upgrades', serialize($processed_upgrades)); +} + +/** + * Gets a list of processes upgrades + * + * @return mixed Array of processed upgrade filenames or false + * @access private + */ +function elgg_get_processed_upgrades() { + $upgrades = datalist_get('processed_upgrades'); + $unserialized = unserialize($upgrades); + return $unserialized; +} + +/** + * Returns the version of the upgrade filename. + * + * @param string $filename The upgrade filename. No full path. + * @return int|false + * @since 1.8.0 + * @access private + */ +function elgg_get_upgrade_file_version($filename) { + preg_match('/^([0-9]{10})([\.a-z0-9-_]+)?\.(php)$/i', $filename, $matches); + + if (isset($matches[1])) { + return (int) $matches[1]; + } + + return false; +} + +/** + * Returns a list of upgrade files relative to the $upgrade_path dir. + * + * @param string $upgrade_path The up + * @return array|false + * @access private + */ +function elgg_get_upgrade_files($upgrade_path = null) { + if (!$upgrade_path) { + $upgrade_path = elgg_get_config('path') . 'engine/lib/upgrades/'; + } + $upgrade_path = sanitise_filepath($upgrade_path); + $handle = opendir($upgrade_path); + + if (!$handle) { + return false; + } + + $upgrade_files = array(); + + while ($upgrade_file = readdir($handle)) { + // make sure this is a wellformed upgrade. + if (is_dir($upgrade_path . '$upgrade_file')) { + continue; + } + $upgrade_version = elgg_get_upgrade_file_version($upgrade_file); + if (!$upgrade_version) { + continue; + } + $upgrade_files[] = $upgrade_file; + } + + sort($upgrade_files); + + return $upgrade_files; +} + +/** + * Get the current Elgg version information + * + * @param bool $humanreadable Whether to return a human readable version (default: false) + * + * @return string|false Depending on success + */ +function get_version($humanreadable = false) { + global $CONFIG; + + static $version, $release; + + if (isset($CONFIG->path)) { + if (!isset($version) || !isset($release)) { + if (!include($CONFIG->path . "version.php")) { + return false; + } + } + return (!$humanreadable) ? $version : $release; + } + + return false; +} + +/** + * Checks if any upgrades need to be run. + * + * @param null|array $upgrade_files Optional upgrade files + * @param null|array $processed_upgrades Optional processed upgrades + * + * @return array + * @access private + */ +function elgg_get_unprocessed_upgrades($upgrade_files = null, $processed_upgrades = null) { + if ($upgrade_files === null) { + $upgrade_files = elgg_get_upgrade_files(); + } + + if ($processed_upgrades === null) { + $processed_upgrades = unserialize(datalist_get('processed_upgrades')); + if (!is_array($processed_upgrades)) { + $processed_upgrades = array(); + } + } + + $unprocessed = array_diff($upgrade_files, $processed_upgrades); + return $unprocessed; +} + +/** + * Determines whether or not the database needs to be upgraded. + * + * @return bool Depending on whether or not the db version matches the code version + * @access private + */ +function version_upgrade_check() { + $dbversion = (int) datalist_get('version'); + $version = get_version(); + + if ($version > $dbversion) { + return TRUE; + } + + return FALSE; +} + +/** + * Upgrades Elgg Database and code + * + * @return bool + * @access private + */ +function version_upgrade() { + // It's possible large upgrades could exceed the max execution time. + set_time_limit(0); + + $dbversion = (int) datalist_get('version'); + + // No version number? Oh snap...this is an upgrade from a clean installation < 1.7. + // Run all upgrades without error reporting and hope for the best. + // See https://github.com/elgg/elgg/issues/1432 for more. + $quiet = !$dbversion; + + // Note: Database upgrades are deprecated as of 1.8. Use code upgrades. See #1433 + if (db_upgrade($dbversion, '', $quiet)) { + system_message(elgg_echo('upgrade:db')); + } + + if (upgrade_code($dbversion, $quiet)) { + system_message(elgg_echo('upgrade:core')); + + // Now we trigger an event to give the option for plugins to do something + $upgrade_details = new stdClass; + $upgrade_details->from = $dbversion; + $upgrade_details->to = get_version(); + + elgg_trigger_event('upgrade', 'upgrade', $upgrade_details); + + return true; + } + + return false; +} + +/** + * Boot straps into 1.8 upgrade system from 1.7 + * + * This runs all the 1.7 upgrades, then sets the processed_upgrades to all existing 1.7 upgrades. + * Control is then passed back to the main upgrade function which detects and runs the + * 1.8 upgrades, regardless of filename convention. + * + * @return bool + * @access private + */ +function elgg_upgrade_bootstrap_17_to_18() { + $db_version = (int) datalist_get('version'); + + // the 1.8 upgrades before the upgrade system change that are interspersed with 1.7 upgrades. + $upgrades_18 = array( + '2010111501.php', + '2010121601.php', + '2010121602.php', + '2010121701.php', + '2010123101.php', + '2011010101.php', + ); + + $upgrade_files = elgg_get_upgrade_files(); + $processed_upgrades = array(); + + foreach ($upgrade_files as $upgrade_file) { + // ignore if not in 1.7 format or if it's a 1.8 upgrade + if (in_array($upgrade_file, $upgrades_18) || !preg_match("/[0-9]{10}\.php/", $upgrade_file)) { + continue; + } + + $upgrade_version = elgg_get_upgrade_file_version($upgrade_file); + + // this has already been run in a previous 1.7.X -> 1.7.X upgrade + if ($upgrade_version < $db_version) { + $processed_upgrades[] = $upgrade_file; + } + } + + return elgg_set_processed_upgrades($processed_upgrades); +} + +/** + * Creates a table {prefix}upgrade_lock that is used as a mutex for upgrades. + * + * @see _elgg_upgrade_lock() + * + * @return bool + * @access private + */ +function _elgg_upgrade_lock() { + global $CONFIG; + + if (!_elgg_upgrade_is_locked()) { + // lock it + insert_data("create table {$CONFIG->dbprefix}upgrade_lock (id INT)"); + elgg_log('Locked for upgrade.', 'NOTICE'); + return true; + } + + elgg_log('Cannot lock for upgrade: already locked.', 'WARNING'); + return false; +} + +/** + * Unlocks upgrade. + * + * @see _elgg_upgrade_lock() + * + * @access private + */ +function _elgg_upgrade_unlock() { + global $CONFIG; + delete_data("drop table {$CONFIG->dbprefix}upgrade_lock"); + elgg_log('Upgrade unlocked.', 'NOTICE'); +} + +/** + * Checks if upgrade is locked + * + * @return bool + * @access private + */ +function _elgg_upgrade_is_locked() { + global $CONFIG; + + $is_locked = count(get_data("show tables like '{$CONFIG->dbprefix}upgrade_lock'")); + + // @todo why? + _elgg_invalidate_query_cache(); + + return $is_locked; +} diff --git a/engine/lib/upgrades/2008100701.php b/engine/lib/upgrades/2008100701.php index 394a90046..b8d4dfdbc 100644 --- a/engine/lib/upgrades/2008100701.php +++ b/engine/lib/upgrades/2008100701.php @@ -1,7 +1,7 @@ <?php - /// Activate mail plugin - /** - * Because Elgg now has a plugable account activation process we need to activate - * the email account activation plugin for existing installs. - */ - enable_plugin('uservalidationbyemail', $CONFIG->site->guid);
\ No newline at end of file + +/** + * Because Elgg now has a plugable account activation process we need to activate + * the email account activation plugin for existing installs. + */ +enable_plugin('uservalidationbyemail', $CONFIG->site->guid); diff --git a/engine/lib/upgrades/2009022701.php b/engine/lib/upgrades/2009022701.php index 2e83b56b3..54083a34d 100644 --- a/engine/lib/upgrades/2009022701.php +++ b/engine/lib/upgrades/2009022701.php @@ -1,7 +1,7 @@ <?php - global $CONFIG; +global $CONFIG; - /** - * Disable update client since this has now been removed. - */ - disable_plugin('updateclient', $CONFIG->site->guid);
\ No newline at end of file +/** + * Disable update client since this has now been removed. + */ +disable_plugin('updateclient', $CONFIG->site->guid); diff --git a/engine/lib/upgrades/2009041701.php b/engine/lib/upgrades/2009041701.php index acc8fc0bd..7b31a3bc9 100644 --- a/engine/lib/upgrades/2009041701.php +++ b/engine/lib/upgrades/2009041701.php @@ -1,9 +1,8 @@ <?php - global $CONFIG; +global $CONFIG; - /// Activate kses - /** - * Elgg now has kses tag filtering built as a plugin. This needs to be enabled. - */ - enable_plugin('kses', $CONFIG->site->guid);
\ No newline at end of file +/** + * Elgg now has kses tag filtering built as a plugin. This needs to be enabled. + */ +enable_plugin('kses', $CONFIG->site->guid); diff --git a/engine/lib/upgrades/2009070101.php b/engine/lib/upgrades/2009070101.php index 3bf89e9b7..d0eae9b91 100644 --- a/engine/lib/upgrades/2009070101.php +++ b/engine/lib/upgrades/2009070101.php @@ -1,10 +1,9 @@ <?php - global $CONFIG; +global $CONFIG; - /// Deprecate kses and activate htmlawed - /** - * Kses appears to be a dead project so we are deprecating it in favour of htmlawed. - */ - disable_plugin('kses', $CONFIG->site->guid); - enable_plugin('htmlawed', $CONFIG->site->guid);
\ No newline at end of file +/** + * Kses appears to be a dead project so we are deprecating it in favour of htmlawed. + */ +disable_plugin('kses', $CONFIG->site->guid); +enable_plugin('htmlawed', $CONFIG->site->guid); diff --git a/engine/lib/upgrades/2009102801.php b/engine/lib/upgrades/2009102801.php index 8885dbb09..3ad113fb2 100644 --- a/engine/lib/upgrades/2009102801.php +++ b/engine/lib/upgrades/2009102801.php @@ -1,7 +1,8 @@ <?php -// disable timeout for large sites. -set_time_limit(0); +/** + * Move user's data directories from using username to registration date + */ /** * Generates a file matrix like Elgg 1.0 did @@ -202,14 +203,15 @@ function user_file_matrix($guid) { return "$time_created/$user->guid/"; } -global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE; +global $ENTITY_CACHE, $CONFIG; /** - Upgrade file locations + * Upgrade file locations */ $users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity WHERE username != ''"); while ($user = mysql_fetch_object($users)) { - $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array(); + $ENTITY_CACHE = array(); + _elgg_invalidate_query_cache(); $to = $CONFIG->dataroot . user_file_matrix($user->guid); foreach (array('1_0', '1_1', '1_6') as $version) { diff --git a/engine/lib/upgrades/2010033101.php b/engine/lib/upgrades/2010033101.php index 5c8ee036b..4779295fd 100644 --- a/engine/lib/upgrades/2010033101.php +++ b/engine/lib/upgrades/2010033101.php @@ -1,6 +1,7 @@ <?php -/* - * Conditional upgrade for UTF8 as described in http://trac.elgg.org/ticket/1928 + +/** + * Conditional upgrade for UTF8 as described in https://github.com/elgg/elgg/issues/1928 */ // get_version() returns the code version. @@ -66,4 +67,4 @@ if ($dbversion < 2009100701) { } } } -}
\ No newline at end of file +} diff --git a/engine/lib/upgrades/2010040201.php b/engine/lib/upgrades/2010040201.php index 22eee15f8..789bf5dfc 100644 --- a/engine/lib/upgrades/2010040201.php +++ b/engine/lib/upgrades/2010040201.php @@ -1,4 +1,5 @@ <?php + /** * Pull admin metadata setting into users_entity table column */ @@ -37,4 +38,4 @@ $qs[] = "DELETE FROM {$CONFIG->dbprefix}metadata foreach ($qs as $q) { update_data($q); -}
\ No newline at end of file +} diff --git a/engine/lib/upgrades/2010050701.php b/engine/lib/upgrades/2010050701.php deleted file mode 100644 index 4a02a77c6..000000000 --- a/engine/lib/upgrades/2010050701.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Removes the Walled Garden plugin in favor of new system settings - */ - -$access = elgg_set_ignore_access(TRUE); - -if (is_plugin_enabled('walledgarden')) { - disable_plugin('walledgarden'); - set_config('allow_registration', FALSE); - set_config('walled_garden', TRUE); -} else { - set_config('allow_registration', TRUE); - set_config('walled_garden', FALSE); -} - -elgg_set_ignore_access($access); diff --git a/engine/lib/upgrades/2010052601.php b/engine/lib/upgrades/2010052601.php index 5b477910f..a9cca6dc5 100644 --- a/engine/lib/upgrades/2010052601.php +++ b/engine/lib/upgrades/2010052601.php @@ -9,14 +9,14 @@ $params = array('type' => 'group', $groups = elgg_get_entities($params); if ($groups) { foreach ($groups as $group) { - $group->name = html_entity_decode($group->name, ENT_COMPAT, 'UTF-8'); - $group->description = html_entity_decode($group->description, ENT_COMPAT, 'UTF-8'); - $group->briefdescription = html_entity_decode($group->briefdescription, ENT_COMPAT, 'UTF-8'); - $group->website = html_entity_decode($group->website, ENT_COMPAT, 'UTF-8'); + $group->name = _elgg_html_decode($group->name); + $group->description = _elgg_html_decode($group->description); + $group->briefdescription = _elgg_html_decode($group->briefdescription); + $group->website = _elgg_html_decode($group->website); if ($group->interests) { $tags = $group->interests; - foreach ($tags as $index=>$tag) { - $tags[$index] = html_entity_decode($tag, ENT_COMPAT, 'UTF-8'); + foreach ($tags as $index => $tag) { + $tags[$index] = _elgg_html_decode($tag); } $group->interests = $tags; } diff --git a/engine/lib/upgrades/2010060101.php b/engine/lib/upgrades/2010060101.php index 7772c42eb..bb7f7c1a6 100644 --- a/engine/lib/upgrades/2010060101.php +++ b/engine/lib/upgrades/2010060101.php @@ -10,7 +10,7 @@ delete_data($query); if ($CONFIG->simplecache_enabled) { datalist_set('simplecache_enabled', 1); - elgg_view_regenerate_simplecache(); + elgg_regenerate_simplecache(); } else { datalist_set('simplecache_enabled', 0); } diff --git a/engine/lib/upgrades/2010061501.php b/engine/lib/upgrades/2010061501.php index d230236fc..744c28fd5 100644 --- a/engine/lib/upgrades/2010061501.php +++ b/engine/lib/upgrades/2010061501.php @@ -1,13 +1,12 @@ <?php /** - * utf8 conversion and file merging for usernames with multibyte chars + * utf8 database conversion and file merging for usernames with multibyte chars * */ // check that we need to do the utf8 conversion // C&P logic from 2010033101 -set_time_limit(0); $dbversion = (int) datalist_get('version'); if ($dbversion < 2009100701) { @@ -46,7 +45,7 @@ if ($dbversion < 2009100701) { } } - global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE; + global $ENTITY_CACHE; /** Upgrade file locations @@ -61,7 +60,9 @@ if ($dbversion < 2009100701) { $users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity WHERE username != ''", $link); while ($user = mysql_fetch_object($users)) { - $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array(); + $ENTITY_CACHE = array(); + _elgg_invalidate_query_cache(); + $to = $CONFIG->dataroot . user_file_matrix($user->guid); foreach (array('1_0', '1_1', '1_6') as $version) { @@ -71,4 +72,4 @@ if ($dbversion < 2009100701) { } } } -}
\ No newline at end of file +} diff --git a/engine/lib/upgrades/2010071001.php b/engine/lib/upgrades/2010071001.php index 1b5d379d8..5594493a8 100644 --- a/engine/lib/upgrades/2010071001.php +++ b/engine/lib/upgrades/2010071001.php @@ -30,11 +30,12 @@ function user_file_matrix_2010071001($guid) { $sizes = array('large', 'medium', 'small', 'tiny', 'master', 'topbar'); -global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG; +global $ENTITY_CACHE, $CONFIG; $users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity WHERE username != ''"); while ($user = mysql_fetch_object($users)) { - $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array(); + $ENTITY_CACHE = array(); + _elgg_invalidate_query_cache(); $user_directory = user_file_matrix_2010071001($user->guid); if (!$user_directory) { diff --git a/engine/lib/upgrades/2010071002.php b/engine/lib/upgrades/2010071002.php index 30bd6538c..52aa15ef5 100644 --- a/engine/lib/upgrades/2010071002.php +++ b/engine/lib/upgrades/2010071002.php @@ -4,12 +4,13 @@ */ // loop through all users checking collections and notifications -global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG; +global $ENTITY_CACHE, $CONFIG; global $NOTIFICATION_HANDLERS; $users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity WHERE username != ''"); while ($user = mysql_fetch_object($users)) { - $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array(); + $ENTITY_CACHE = array(); + _elgg_invalidate_query_cache(); $user = get_entity($user->guid); foreach ($NOTIFICATION_HANDLERS as $method => $foo) { diff --git a/engine/lib/upgrades/2010100500.php b/engine/lib/upgrades/2010100500.php deleted file mode 100644 index 1f9587196..000000000 --- a/engine/lib/upgrades/2010100500.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php -/** - * Upgrades the oAuth Library plugin name - */ - -if (is_plugin_enabled('oauth')) { - disable_plugin('oauth'); - enable_plugin('oauth_lib'); -} diff --git a/engine/lib/upgrades/2010121601.php b/engine/lib/upgrades/2010121601.php new file mode 100644 index 000000000..ad7d26adb --- /dev/null +++ b/engine/lib/upgrades/2010121601.php @@ -0,0 +1,9 @@ +<?php +/** + * Create friends river view has been changed + */ + +$query = "UPDATE {$CONFIG->dbprefix}river + SET view='river/relationship/friend/create', action_type='create' + WHERE view='friends/river/create' AND action_type='friend'"; +update_data($query); diff --git a/engine/lib/upgrades/2010121602.php b/engine/lib/upgrades/2010121602.php new file mode 100644 index 000000000..5b0996b5e --- /dev/null +++ b/engine/lib/upgrades/2010121602.php @@ -0,0 +1,10 @@ +<?php +/** + * Create comment river view has been changed + */ + +$query = "UPDATE {$CONFIG->dbprefix}river + SET view='river/annotation/generic_comment/create' + WHERE view='annotation/annotate' AND action_type='comment'"; +update_data($query); + diff --git a/engine/lib/upgrades/2010121701.php b/engine/lib/upgrades/2010121701.php new file mode 100644 index 000000000..375654bac --- /dev/null +++ b/engine/lib/upgrades/2010121701.php @@ -0,0 +1,10 @@ +<?php +/** + * Create group forum topic river view has been changed + */ + +$query = "UPDATE {$CONFIG->dbprefix}river + SET view='river/object/groupforumtopic/create' + WHERE view='river/forum/topic/create' AND action_type='create'"; +update_data($query); + diff --git a/engine/lib/upgrades/2010123101.php b/engine/lib/upgrades/2010123101.php new file mode 100644 index 000000000..f4befd1a8 --- /dev/null +++ b/engine/lib/upgrades/2010123101.php @@ -0,0 +1,9 @@ +<?php +/** + * Set default access for older sites + */ + +$access = elgg_get_config('default_access'); +if ($access == false) { + elgg_save_config('default_access', ACCESS_LOGGED_IN); +} diff --git a/engine/lib/upgrades/2011010101.php b/engine/lib/upgrades/2011010101.php new file mode 100644 index 000000000..f4411ee20 --- /dev/null +++ b/engine/lib/upgrades/2011010101.php @@ -0,0 +1,98 @@ +<?php +/** + * Migrate plugins to the new system using ElggPlugin and private settings + */ + +$old_ia = elgg_set_ignore_access(true); + +$site = get_config('site'); +$old_plugin_order = unserialize($site->pluginorder); +$old_enabled_plugins = $site->enabled_plugins; + +$db_prefix = get_config('dbprefix'); +$plugin_subtype_id = get_subtype_id('object', 'plugin'); + +// easy one first: make sure the the site owns all plugin entities. +$q = "UPDATE {$db_prefix}entities e + SET owner_guid = $site->guid, container_guid = $site->guid + WHERE e.type = 'object' AND e.subtype = $plugin_subtype_id"; + +$r = update_data($q); + +// rewrite all plugin:setting:* to ELGG_PLUGIN_USER_SETTING_PREFIX . * +$q = "UPDATE {$db_prefix}private_settings + SET name = replace(name, 'plugin:settings:', '" . ELGG_PLUGIN_USER_SETTING_PREFIX . "') + WHERE name LIKE 'plugin:settings:%'"; + +$r = update_data($q); + +// grab current plugin GUIDs to add a temp priority +$q = "SELECT * FROM {$db_prefix}entities e + JOIN {$db_prefix}objects_entity oe ON e.guid = oe.guid + WHERE e.type = 'object' AND e.subtype = $plugin_subtype_id"; + +$plugins = get_data($q); + +foreach ($plugins as $plugin) { + $priority = elgg_namespace_plugin_private_setting('internal', 'priority'); + set_private_setting($plugin->guid, $priority, 0); +} + +// force regenerating plugin entities +elgg_generate_plugin_entities(); + +// set the priorities for all plugins +// this function rewrites it to a normal index so use the current one. +elgg_set_plugin_priorities($old_plugin_order); + +// add relationships for enabled plugins +if ($old_enabled_plugins) { + // they might only have one plugin enabled. + if (!is_array($old_enabled_plugins)) { + $old_enabled_plugins = array($old_enabled_plugins); + } + + // sometimes there were problems and you'd get 1000s of enabled plugins. + $old_enabled_plugins = array_unique($old_enabled_plugins); + + foreach ($old_enabled_plugins as $plugin_id) { + $plugin = elgg_get_plugin_from_id($plugin_id); + + if ($plugin) { + $plugin->activate(); + } + } +} + +// invalidate caches +elgg_invalidate_simplecache(); +elgg_reset_system_cache(); + +// clean up. +remove_metadata($site->guid, 'pluginorder'); +remove_metadata($site->guid, 'enabled_plugins'); + +elgg_set_ignore_access($old_id); + +/** + * @hack + * + * We stop the upgrade at this point because plugins weren't given the chance to + * load due to the new plugin code introduced with Elgg 1.8. Instead, we manually + * set the version and start the upgrade process again. + * + * The variables from upgrade_code() are available because this script was included + */ +if ($upgrade_version > $version) { + datalist_set('version', $upgrade_version); +} + +// add ourselves to the processed_upgrades. +$processed_upgrades[] = '2011010101.php'; + +$processed_upgrades = array_unique($processed_upgrades); +elgg_set_processed_upgrades($processed_upgrades); + +_elgg_upgrade_unlock(); + +forward('upgrade.php'); diff --git a/engine/lib/upgrades/2011021800-1.8_svn-goodbye_walled_garden-083121a656d06894.php b/engine/lib/upgrades/2011021800-1.8_svn-goodbye_walled_garden-083121a656d06894.php new file mode 100644 index 000000000..40b2c71d5 --- /dev/null +++ b/engine/lib/upgrades/2011021800-1.8_svn-goodbye_walled_garden-083121a656d06894.php @@ -0,0 +1,34 @@ +<?php +/** + * Elgg 1.8-svn upgrade 2011021800 + * goodbye_walled_garden + * + * Removes the Walled Garden plugin in favor of new system settings + */ + +global $CONFIG; + +$access = elgg_set_ignore_access(TRUE); + +if (elgg_is_active_plugin('walledgarden')) { + disable_plugin('walledgarden'); + set_config('allow_registration', FALSE); + set_config('walled_garden', TRUE); +} else { + set_config('allow_registration', TRUE); + set_config('walled_garden', FALSE); +} + +// this was for people who manually set the config option +$disable_registration = elgg_get_config('disable_registration'); +if ($disable_registration !== null) { + $allow_registration = !$disable_registration; + elgg_save_config('allow_registration', $allow_registration); + + $site = elgg_get_site_entity(); + $query = "DELETE FROM {$CONFIG->dbprefix}config + WHERE name = 'disable_registration' AND site_guid = $site->guid"; + delete_data($query); +} + +elgg_set_ignore_access($access); diff --git a/engine/lib/upgrades/2011022000-1.8_svn-custom_profile_fields-390ac967b0bb5665.php b/engine/lib/upgrades/2011022000-1.8_svn-custom_profile_fields-390ac967b0bb5665.php new file mode 100644 index 000000000..7561b84ba --- /dev/null +++ b/engine/lib/upgrades/2011022000-1.8_svn-custom_profile_fields-390ac967b0bb5665.php @@ -0,0 +1,59 @@ +<?php +/** + * Elgg 2011010401 upgrade 00 + * custom_profile_fields + * + * Migrate 1.7 style custom profile fields to 1.8 + */ + +$plugin = elgg_get_plugin_from_id('profile'); + +// plugin not installed +if (!$plugin) { + return true; +} + +$settings = $plugin->getAllSettings(); +// no fields to migrate +if (!$settings['user_defined_fields']) { + return true; +} + +$order = array(); +$remove_settings = array(); + +// make sure we have a name and type +foreach ($settings as $k => $v) { + if (!preg_match('/admin_defined_profile_([0-9]+)/i', $k, $matches)) { + continue; + } + + $i = $matches[1]; + $type_name = "admin_defined_profile_type_$i"; + $type = elgg_extract($type_name, $settings, null); + + if ($type) { + // field name + elgg_save_config($k, $v); + // field value + elgg_save_config($type_name, $type); + + $order[] = $i; + $remove_settings[] = $k; + $remove_settings[] = $type_name; + } +} + +if ($order) { + // these will always need to be in order, but there might be gaps + ksort($order); + + $order_str = implode(',', $order); + elgg_save_config('profile_custom_fields', $order_str); + + foreach ($remove_settings as $name) { + $plugin->unsetSetting($name); + } + + $plugin->unsetSetting('user_defined_fields'); +}
\ No newline at end of file diff --git a/engine/lib/upgrades/2011030700-1.8_svn-blog_status_metadata-4645225d7b440876.php b/engine/lib/upgrades/2011030700-1.8_svn-blog_status_metadata-4645225d7b440876.php new file mode 100644 index 000000000..fe2af9928 --- /dev/null +++ b/engine/lib/upgrades/2011030700-1.8_svn-blog_status_metadata-4645225d7b440876.php @@ -0,0 +1,24 @@ +<?php +/** + * Elgg 1.8-svn upgrade 2011030700 + * blog_status_metadata + * + * Add a "status" metadata entry to every blog entity because in 1.8 you can have status = draft or + * status = published + */ +$ia = elgg_set_ignore_access(true); +$options = array( + 'type' => 'object', + 'subtype' => 'blog', + 'limit' => 0, +); +$batch = new ElggBatch('elgg_get_entities', $options); + +foreach ($batch as $entity) { + if (!$entity->status) { + // create metadata owned by the original owner + create_metadata($entity->getGUID(), 'status', 'published', '', $entity->owner_guid, + $entity->access_id); + } +} +elgg_set_ignore_access($ia);
\ No newline at end of file diff --git a/engine/lib/upgrades/2011031300-1.8_svn-twitter_api-12b832a5a7a3e1bd.php b/engine/lib/upgrades/2011031300-1.8_svn-twitter_api-12b832a5a7a3e1bd.php new file mode 100644 index 000000000..df60892a6 --- /dev/null +++ b/engine/lib/upgrades/2011031300-1.8_svn-twitter_api-12b832a5a7a3e1bd.php @@ -0,0 +1,54 @@ +<?php +/** + * Elgg 1.8-svn upgrade 2011031300 + * twitter_api + * + * Updates the database for twitterservice to twitter_api changes. + */ + + +$ia = elgg_set_ignore_access(true); + +// make sure we have updated plugins +elgg_generate_plugin_entities(); + +$show_hidden = access_get_show_hidden_status(); +access_show_hidden_entities(true); + +$db_prefix = elgg_get_config('dbprefix'); +$site_guid = elgg_get_site_entity()->getGUID(); +$old = elgg_get_plugin_from_id('twitterservice'); +$new = elgg_get_plugin_from_id('twitter_api'); +$has_settings = false; + +// if not loaded, don't bother. +if (!$old || !$new) { + return true; +} + +$settings = array('consumer_key', 'consumer_secret', 'sign_on', 'new_users'); + +foreach ($settings as $setting) { + $value = $old->getSetting($setting); + if ($value) { + $has_settings = true; + $new->setSetting($setting, $value); + } +} + +// update the user settings +$q = "UPDATE {$db_prefix}private_settings + SET name = replace(name, 'twitterservice', 'twitter_api') + WHERE name like '%twitterservice%'"; + +update_data($q); + +// if there were settings, emit a notice to re-enable twitter_api +if ($has_settings) { + elgg_add_admin_notice('twitter_api:disabled', elgg_echo('update:twitter_api:deactivated')); +} + +$old->delete(); + +access_show_hidden_entities($show_hidden); +elgg_set_ignore_access($ia);
\ No newline at end of file diff --git a/engine/lib/upgrades/2011031600-1.8_svn-datalist_grows_up-0b8aec5a55cc1e1c.php b/engine/lib/upgrades/2011031600-1.8_svn-datalist_grows_up-0b8aec5a55cc1e1c.php new file mode 100644 index 000000000..379244b36 --- /dev/null +++ b/engine/lib/upgrades/2011031600-1.8_svn-datalist_grows_up-0b8aec5a55cc1e1c.php @@ -0,0 +1,18 @@ +<?php +/** + * Elgg 1.8-svn upgrade 2011031600 + * datalist_grows_up + * + * Ups the varchar to 256 for the datalist and config table. + * + * Keeping it as a varchar because of the trailing whitespace trimming it apparently does: + * http://dev.mysql.com/doc/refman/5.0/en/char.html + */ + +$db_prefix = elgg_get_config('dbprefix'); + +$q = "ALTER TABLE {$db_prefix}datalists CHANGE name name VARCHAR(255)"; +update_data($q); + +$q = "ALTER TABLE {$db_prefix}config CHANGE name name VARCHAR(255)"; +update_data($q); diff --git a/engine/lib/upgrades/2011032000-1.8_svn-widgets_arent_plugins-61836261fa280a5c.php b/engine/lib/upgrades/2011032000-1.8_svn-widgets_arent_plugins-61836261fa280a5c.php new file mode 100644 index 000000000..a20970d79 --- /dev/null +++ b/engine/lib/upgrades/2011032000-1.8_svn-widgets_arent_plugins-61836261fa280a5c.php @@ -0,0 +1,10 @@ +<?php +/** + * Elgg 1.8-svn upgrade 2011031800 + * widgets_arent_plugins + * + * At some point in Elgg's history subtype widget was registered with class ElggPlugin. + * Fix that. + */ + +update_subtype('object', 'widget', 'ElggWidget'); diff --git a/engine/lib/upgrades/2011032200-1.8_svn-admins_like_widgets-7f19d2783c1680d3.php b/engine/lib/upgrades/2011032200-1.8_svn-admins_like_widgets-7f19d2783c1680d3.php new file mode 100644 index 000000000..592adb403 --- /dev/null +++ b/engine/lib/upgrades/2011032200-1.8_svn-admins_like_widgets-7f19d2783c1680d3.php @@ -0,0 +1,13 @@ +<?php +/** + * Elgg 1.8-svn upgrade 2011032200 + * admins_like_widgets + * + * Give current admins widgets for those pre-1.8 + */ + +$admins = elgg_get_admins(array('limit' => 0)); +foreach ($admins as $admin) { + // call the admin handler for the make_admin event + elgg_add_admin_widgets('make_admin', 'user', $admin); +} diff --git a/engine/lib/upgrades/2011052801.php b/engine/lib/upgrades/2011052801.php new file mode 100644 index 000000000..b5a8e1018 --- /dev/null +++ b/engine/lib/upgrades/2011052801.php @@ -0,0 +1,46 @@ +<?php +/** + * Make sure all users have the relationship member_of_site + */ +global $ENTITY_CACHE; +$db_prefix = get_config('dbprefix'); + +$limit = 100; + +$q = "SELECT e.* FROM {$db_prefix}entities e + WHERE e.type = 'user' AND e.guid NOT IN ( + SELECT guid_one FROM {$db_prefix}entity_relationships + WHERE guid_two = 1 AND relationship = 'member_of_site' + ) + LIMIT $limit"; + +$users = get_data($q); + +while ($users) { + $ENTITY_CACHE = array(); + _elgg_invalidate_query_cache(); + + // do manually to not trigger any events because these aren't new users. + foreach ($users as $user) { + $rel_q = "INSERT INTO {$db_prefix}entity_relationships VALUES ( + '', + '$user->guid', + 'member_of_site', + '$user->site_guid', + '$user->time_created' + )"; + + insert_data($rel_q); + } + + // every time we run this query we've just reduced the rows it returns by $limit + // so don't pass an offset. + $q = "SELECT e.* FROM {$db_prefix}entities e + WHERE e.type = 'user' AND e.guid NOT IN ( + SELECT guid_one FROM {$db_prefix}entity_relationships + WHERE guid_two = 1 AND relationship = 'member_of_site' + ) + LIMIT $limit"; + + $users = get_data($q); +}
\ No newline at end of file diff --git a/engine/lib/upgrades/2011061200-1.8b1-sites_need_a_site_guid-6d9dcbf46c0826cc.php b/engine/lib/upgrades/2011061200-1.8b1-sites_need_a_site_guid-6d9dcbf46c0826cc.php new file mode 100644 index 000000000..41ab29998 --- /dev/null +++ b/engine/lib/upgrades/2011061200-1.8b1-sites_need_a_site_guid-6d9dcbf46c0826cc.php @@ -0,0 +1,31 @@ +<?php +/** + * Elgg 1.8b1 upgrade 2011061200 + * sites_need_a_site_guid + * + * Sites did not have a site guid. This causes problems with getting + * metadata on site objects since we default to the current site. + */ + +global $CONFIG; + +$ia = elgg_set_ignore_access(true); +$access_status = access_get_show_hidden_status(); +access_show_hidden_entities(true); + +$options = array( + 'type' => 'site', + 'site_guid' => 0, + 'limit' => 0, +); +$batch = new ElggBatch('elgg_get_entities', $options); + +foreach ($batch as $entity) { + if (!$entity->site_guid) { + update_data("UPDATE {$CONFIG->dbprefix}entities SET site_guid=$entity->guid + WHERE guid=$entity->guid"); + } +} + +access_show_hidden_entities($access_status); +elgg_set_ignore_access($ia); diff --git a/engine/lib/upgrades/2011092500-1.8.0.1-forum_reply_river_view-5758ce8d86ac56ce.php b/engine/lib/upgrades/2011092500-1.8.0.1-forum_reply_river_view-5758ce8d86ac56ce.php new file mode 100644 index 000000000..3a9200b51 --- /dev/null +++ b/engine/lib/upgrades/2011092500-1.8.0.1-forum_reply_river_view-5758ce8d86ac56ce.php @@ -0,0 +1,12 @@ +<?php +/** + * Elgg 1.8.0.1 upgrade 2011092500 + * forum_reply_river_view + * + * The forum reply river view is in a new location in Elgg 1.8 + */ + +$query = "UPDATE {$CONFIG->dbprefix}river SET view='river/annotation/group_topic_post/reply', + action_type='reply' + WHERE view='river/forum/create' AND action_type='create'"; +update_data($query); diff --git a/engine/lib/upgrades/2011123100-1.8.2-fix_friend_river-b17e7ff8345c2269.php b/engine/lib/upgrades/2011123100-1.8.2-fix_friend_river-b17e7ff8345c2269.php new file mode 100644 index 000000000..4dc43cd32 --- /dev/null +++ b/engine/lib/upgrades/2011123100-1.8.2-fix_friend_river-b17e7ff8345c2269.php @@ -0,0 +1,12 @@ +<?php +/** + * Elgg 1.8.2 upgrade 2011123100 + * fix_friend_river + * + * Action type was incorrect due to previoud friends river upgrade + */ + +$query = "UPDATE {$CONFIG->dbprefix}river + SET action_type='friend' + WHERE view='river/relationship/friend/create' AND action_type='create'"; +update_data($query); diff --git a/engine/lib/upgrades/2011123101-1.8.2-fix_blog_status-b14c2a0e7b9e7d55.php b/engine/lib/upgrades/2011123101-1.8.2-fix_blog_status-b14c2a0e7b9e7d55.php new file mode 100644 index 000000000..e351c6ac9 --- /dev/null +++ b/engine/lib/upgrades/2011123101-1.8.2-fix_blog_status-b14c2a0e7b9e7d55.php @@ -0,0 +1,25 @@ +<?php +/** + * Elgg 1.8.2 upgrade 2011123101 + * fix_blog_status + * + * Most blog posts did not have their status properly set with 1.8 upgrade so we run + * the blog status upgrade again + */ + +$ia = elgg_set_ignore_access(true); +$options = array( + 'type' => 'object', + 'subtype' => 'blog', + 'limit' => 0, +); +$batch = new ElggBatch('elgg_get_entities', $options); + +foreach ($batch as $entity) { + if (!$entity->status) { + // create metadata owned by the original owner + create_metadata($entity->getGUID(), 'status', 'published', '', $entity->owner_guid, + $entity->access_id); + } +} +elgg_set_ignore_access($ia);
\ No newline at end of file diff --git a/engine/lib/upgrades/2012012000-1.8.3-ip_in_syslog-87fe0f068cf62428.php b/engine/lib/upgrades/2012012000-1.8.3-ip_in_syslog-87fe0f068cf62428.php new file mode 100644 index 000000000..b9514e156 --- /dev/null +++ b/engine/lib/upgrades/2012012000-1.8.3-ip_in_syslog-87fe0f068cf62428.php @@ -0,0 +1,12 @@ +<?php +/** + * Elgg 1.8.3 upgrade 2012012000 + * ip_in_syslog + * + * Adds a field for an IP address in the system log table + */ + +$db_prefix = elgg_get_config('dbprefix'); +$q = "ALTER TABLE {$db_prefix}system_log ADD ip_address VARCHAR(15) NOT NULL AFTER time_created"; + +update_data($q);
\ No newline at end of file diff --git a/engine/lib/upgrades/2012012100-1.8.3-system_cache-93100e7d55a24a11.php b/engine/lib/upgrades/2012012100-1.8.3-system_cache-93100e7d55a24a11.php new file mode 100644 index 000000000..3a9aae2a1 --- /dev/null +++ b/engine/lib/upgrades/2012012100-1.8.3-system_cache-93100e7d55a24a11.php @@ -0,0 +1,13 @@ +<?php +/** + * Elgg 1.8.3 upgrade 2012012100 + * system_cache + * + * Convert viewpath cache to system cache + */ + +$value = datalist_get('viewpath_cache_enabled'); +datalist_set('system_cache_enabled', $value); + +$query = "DELETE FROM {$CONFIG->dbprefix}datalists WHERE name='viewpath_cache_enabled'"; +delete_data($query); diff --git a/engine/lib/upgrades/2012041800-1.8.3-dont_filter_passwords-c0ca4a18b38ae2bc.php b/engine/lib/upgrades/2012041800-1.8.3-dont_filter_passwords-c0ca4a18b38ae2bc.php new file mode 100644 index 000000000..b82ffbebf --- /dev/null +++ b/engine/lib/upgrades/2012041800-1.8.3-dont_filter_passwords-c0ca4a18b38ae2bc.php @@ -0,0 +1,11 @@ +<?php +/** + * Elgg 1.8.3 upgrade 2012041800 + * dont_filter_passwords + * + * Add admin notice that password handling has changed and if + * users can't login to have them reset their passwords. + */ +elgg_add_admin_notice('dont_filter_passwords', 'Password handling has been updated to be more secure and flexible. ' + . 'This change may prevent a small number of users from logging in with their existing passwords. ' + . 'If a user is unable to log in, please advise him or her to reset their password, or reset it as an admin user.'); diff --git a/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php b/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php new file mode 100644 index 000000000..780038c32 --- /dev/null +++ b/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php @@ -0,0 +1,13 @@ +<?php +/** + * Elgg 1.8.3 upgrade 2012041801 + * multiple_user_tokens + * + * Fixes https://github.com/elgg/elgg/issues/4291 + * Removes the unique index on users_apisessions for user_guid and site_guid + */ + +$db_prefix = elgg_get_config('dbprefix'); +$q = "ALTER TABLE {$db_prefix}users_apisessions DROP INDEX user_guid, + ADD INDEX user_guid (user_guid, site_guid)"; +update_data($q);
\ No newline at end of file diff --git a/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php b/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php new file mode 100644 index 000000000..8eccf05e2 --- /dev/null +++ b/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php @@ -0,0 +1,24 @@ +<?php +/** + * Elgg 1.8.14 upgrade 2013030600 + * update_user_location + * + * Before Elgg 1.8, a location like "London, England" would be stored as an array. + * This script turns that back into a string. + */ + +$ia = elgg_set_ignore_access(true); +$options = array( + 'type' => 'user', + 'limit' => 0, +); +$batch = new ElggBatch('elgg_get_entities', $options); + +foreach ($batch as $entity) { + _elgg_invalidate_query_cache(); + + if (is_array($entity->location)) { + $entity->location = implode(', ', $entity->location); + } +} +elgg_set_ignore_access($ia); diff --git a/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php b/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php new file mode 100644 index 000000000..ee99bdbc8 --- /dev/null +++ b/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php @@ -0,0 +1,28 @@ +<?php +/** + * Elgg 1.8.15 upgrade 2013051700 + * add_missing_group_index + * + * Some Elgg sites are missing the groups_entity full text index on name and + * description. This checks if it exists and adds it if it does not. + */ + +$db_prefix = elgg_get_config('dbprefix'); + +$full_text_index_exists = false; +$results = get_data("SHOW INDEX FROM {$db_prefix}groups_entity"); +if ($results) { + foreach ($results as $result) { + if ($result->Index_type === 'FULLTEXT') { + $full_text_index_exists = true; + } + } +} + +if ($full_text_index_exists == false) { + $query = "ALTER TABLE {$db_prefix}groups_entity + ADD FULLTEXT name_2 (name, description)"; + if (!update_data($query)) { + elgg_log("Failed to add full text index to groups_entity table", 'ERROR'); + } +} diff --git a/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php b/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php new file mode 100644 index 000000000..d333a6cd2 --- /dev/null +++ b/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php @@ -0,0 +1,12 @@ +<?php +/** + * Elgg 1.8.15 upgrade 2013052900 + * ipv6_in_syslog + * + * Upgrade the ip column in system_log to be able to store ipv6 addresses + */ + +$db_prefix = elgg_get_config('dbprefix'); +$q = "ALTER TABLE {$db_prefix}system_log MODIFY COLUMN ip_address varchar(46) NOT NULL"; + +update_data($q);
\ No newline at end of file diff --git a/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php new file mode 100644 index 000000000..538d74dd6 --- /dev/null +++ b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php @@ -0,0 +1,16 @@ +<?php +/** + * Elgg 1.8.15 upgrade 2013060900 + * site_secret + * + * Description + */ + +$strength = _elgg_get_site_secret_strength(); + +if ($strength !== 'strong') { + // a new key is needed immediately + register_translations(elgg_get_root_path() . 'languages/'); + + elgg_add_admin_notice('weak_site_key', elgg_echo("upgrade:site_secret_warning:$strength")); +} diff --git a/engine/lib/upgrades/create_upgrade.php b/engine/lib/upgrades/create_upgrade.php new file mode 100644 index 000000000..b34f31b7e --- /dev/null +++ b/engine/lib/upgrades/create_upgrade.php @@ -0,0 +1,152 @@ +<?php +/** + * Creates an upgrade file for Elgg. + * + * Run this from the command line: + * php create_upgrade.php upgrade_name + */ + +error_reporting(E_NOTICE); + +// only allow from the command line. +if (php_sapi_name() != 'cli') { + die('Upgrades can only be created from the command line.'); +} + +if (count($argv) < 2) { + elgg_create_upgrade_show_usage('No upgrade name.'); +} + +$name = $argv[1]; + +if (strlen($name) > 24) { + elgg_create_upgrade_show_usage('Upgrade names cannot be longer than 24 characters.'); +} + +require_once '../../../version.php'; +require_once '../elgglib.php'; +$upgrade_path = dirname(__FILE__); + +$upgrade_name = strtolower($name); +$upgrade_name = str_replace(array(' ', '-'), '_', $upgrade_name); +$upgrade_release = str_replace(array(' ', '-'), '_', $release); +$time = time(); +$upgrade_rnd = substr(md5($time), 0, 16); +$upgrade_date = date('Ymd', $time); + +// determine the inc count +$upgrade_inc = 0; +$files = elgg_get_file_list($upgrade_path); +sort($files); + +foreach ($files as $filename) { + $filename = basename($filename); + $date = (int)substr($filename, 0, 8); + $inc = (int)substr($filename, 8, 2); + + if ($upgrade_date == $date) { + if ($inc >= $upgrade_inc) { + $upgrade_inc = $inc + 1; + } + } +} + +// zero-pad +// if there are more than 10 upgrades in a day, someone needs talking to. +if ($upgrade_inc < 10) { + $upgrade_inc = "0$upgrade_inc"; +} + +$upgrade_version = $upgrade_date . $upgrade_inc; + +// make filename +if (substr($release, 0, 3) == '1.7') { + // 1.7 upgrades are YYYYMMDDXX + $upgrade_name = $upgrade_version . '.php'; +} else { + // 1.8+ upgrades are YYYYMMDDXX-release-friendly_name-rnd + $upgrade_name = $upgrade_version . "-$upgrade_release-$name-$upgrade_rnd.php"; +} + +$upgrade_file = $upgrade_path . '/' . $upgrade_name; + +if (is_file($upgrade_file)) { + elgg_create_upgrade_show_usage("Upgrade file $upgrade_file already exists. This script has failed you."); +} + +$upgrade_code = <<<___UPGRADE +<?php +/** + * Elgg $release upgrade $upgrade_version + * $name + * + * Description + */ + +// upgrade code here. + +___UPGRADE; + +$h = fopen($upgrade_file, 'wb'); + +if (!$h) { + die("Could not open file $upgrade_file"); +} + +if (!fwrite($h, $upgrade_code)) { + die("Could not write to $upgrade_file"); +} else { + elgg_set_version_dot_php_version($upgrade_version); + echo <<<___MSG + +Created upgrade file and updated version.php. + +Upgrade file: $upgrade_name +Version: $upgrade_version + +___MSG; +} + +fclose($h); + + +function elgg_set_version_dot_php_version($version) { + $file = '../../../version.php'; + $h = fopen($file, 'r+b'); + + if (!$h) { + return false; + } + + $out = ''; + + while (($line = fgets($h)) !== false) { + $find = "/\\\$version[ ]?=[ ]?[0-9]{10};/"; + $replace = "\$version = $version;"; + $out .= preg_replace($find, $replace, $line); + } + + rewind($h); + + fwrite($h, $out); + fclose($h); + return true; +} + +/** + * Shows the usage for the create_upgrade script and dies(). + * + * @param string $msg Optional message to display + * @return void + */ +function elgg_create_upgrade_show_usage($msg = '') { + $text = <<<___MSG +$msg + +Example: + php create_upgrade.php my_upgrade + +___MSG; + + die($text); +} diff --git a/engine/lib/user_settings.php b/engine/lib/user_settings.php new file mode 100644 index 000000000..0e36dc46d --- /dev/null +++ b/engine/lib/user_settings.php @@ -0,0 +1,360 @@ +<?php +/** + * Elgg user settings functions. + * Functions for adding and manipulating options on the user settings panel. + * + * @package Elgg.Core + * @subpackage Settings.User + */ + +/** + * Saves user settings. + * + * @todo this assumes settings are coming in on a GET/POST request + * + * @note This is a handler for the 'usersettings:save', 'user' plugin hook + * + * @return void + * @access private + */ +function users_settings_save() { + elgg_set_user_language(); + elgg_set_user_password(); + elgg_set_user_default_access(); + elgg_set_user_name(); + elgg_set_user_email(); +} + +/** + * Set a user's password + * + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_set_user_password() { + $current_password = get_input('current_password', null, false); + $password = get_input('password', null, false); + $password2 = get_input('password2', null, false); + $user_guid = get_input('guid'); + + if (!$user_guid) { + $user = elgg_get_logged_in_user_entity(); + } else { + $user = get_entity($user_guid); + } + + if ($user && $password) { + // let admin user change anyone's password without knowing it except his own. + if (!elgg_is_admin_logged_in() || elgg_is_admin_logged_in() && $user->guid == elgg_get_logged_in_user_guid()) { + $credentials = array( + 'username' => $user->username, + 'password' => $current_password + ); + + try { + pam_auth_userpass($credentials); + } catch (LoginException $e) { + register_error(elgg_echo('LoginException:ChangePasswordFailure')); + return false; + } + } + + try { + $result = validate_password($password); + } catch (RegistrationException $e) { + register_error($e->getMessage()); + return false; + } + + if ($result) { + if ($password == $password2) { + $user->salt = generate_random_cleartext_password(); // Reset the salt + $user->password = generate_user_password($user, $password); + if ($user->save()) { + system_message(elgg_echo('user:password:success')); + return true; + } else { + register_error(elgg_echo('user:password:fail')); + } + } else { + register_error(elgg_echo('user:password:fail:notsame')); + } + } else { + register_error(elgg_echo('user:password:fail:tooshort')); + } + } else { + // no change + return null; + } + + return false; +} + +/** + * Set a user's display name + * + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_set_user_name() { + $name = strip_tags(get_input('name')); + $user_id = get_input('guid'); + + if (!$user_id) { + $user = elgg_get_logged_in_user_entity(); + } else { + $user = get_entity($user_id); + } + + if (elgg_strlen($name) > 50) { + register_error(elgg_echo('user:name:fail')); + return false; + } + + if (($user) && ($user->canEdit()) && ($name)) { + if ($name != $user->name) { + $user->name = $name; + if ($user->save()) { + system_message(elgg_echo('user:name:success')); + return true; + } else { + register_error(elgg_echo('user:name:fail')); + } + } else { + // no change + return null; + } + } else { + register_error(elgg_echo('user:name:fail')); + } + return false; +} + +/** + * Set a user's language + * + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_set_user_language() { + $language = get_input('language'); + $user_id = get_input('guid'); + + if (!$user_id) { + $user = elgg_get_logged_in_user_entity(); + } else { + $user = get_entity($user_id); + } + + if (($user) && ($language)) { + if (strcmp($language, $user->language) != 0) { + $user->language = $language; + if ($user->save()) { + system_message(elgg_echo('user:language:success')); + return true; + } else { + register_error(elgg_echo('user:language:fail')); + } + } else { + // no change + return null; + } + } else { + register_error(elgg_echo('user:language:fail')); + } + return false; +} + +/** + * Set a user's email address + * + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_set_user_email() { + $email = get_input('email'); + $user_id = get_input('guid'); + + if (!$user_id) { + $user = elgg_get_logged_in_user_entity(); + } else { + $user = get_entity($user_id); + } + + if (!is_email_address($email)) { + register_error(elgg_echo('email:save:fail')); + return false; + } + + if ($user) { + if (strcmp($email, $user->email) != 0) { + if (!get_user_by_email($email)) { + if ($user->email != $email) { + + $user->email = $email; + if ($user->save()) { + system_message(elgg_echo('email:save:success')); + return true; + } else { + register_error(elgg_echo('email:save:fail')); + } + } + } else { + register_error(elgg_echo('registration:dupeemail')); + } + } else { + // no change + return null; + } + } else { + register_error(elgg_echo('email:save:fail')); + } + return false; +} + +/** + * Set a user's default access level + * + * @return bool + * @since 1.8.0 + * @access private + */ +function elgg_set_user_default_access() { + + if (!elgg_get_config('allow_user_default_access')) { + return false; + } + + $default_access = get_input('default_access'); + $user_id = get_input('guid'); + + if (!$user_id) { + $user = elgg_get_logged_in_user_entity(); + } else { + $user = get_entity($user_id); + } + + if ($user) { + $current_default_access = $user->getPrivateSetting('elgg_default_access'); + if ($default_access !== $current_default_access) { + if ($user->setPrivateSetting('elgg_default_access', $default_access)) { + system_message(elgg_echo('user:default_access:success')); + return true; + } else { + register_error(elgg_echo('user:default_access:fail')); + } + } else { + // no change + return null; + } + } else { + register_error(elgg_echo('user:default_access:fail')); + } + + return false; +} + +/** + * Set up the menu for user settings + * + * @return void + * @access private + */ +function usersettings_pagesetup() { + $user = elgg_get_page_owner_entity(); + + if ($user && elgg_get_context() == "settings") { + $params = array( + 'name' => '1_account', + 'text' => elgg_echo('usersettings:user:opt:linktext'), + 'href' => "settings/user/{$user->username}", + ); + elgg_register_menu_item('page', $params); + $params = array( + 'name' => '1_plugins', + 'text' => elgg_echo('usersettings:plugins:opt:linktext'), + 'href' => "settings/plugins/{$user->username}", + ); + elgg_register_menu_item('page', $params); + $params = array( + 'name' => '1_statistics', + 'text' => elgg_echo('usersettings:statistics:opt:linktext'), + 'href' => "settings/statistics/{$user->username}", + ); + elgg_register_menu_item('page', $params); + } +} + +/** + * Page handler for user settings + * + * @param array $page Pages array + * + * @return bool + * @access private + */ +function usersettings_page_handler($page) { + global $CONFIG; + + if (!isset($page[0])) { + $page[0] = 'user'; + } + + if (isset($page[1])) { + $user = get_user_by_username($page[1]); + elgg_set_page_owner_guid($user->guid); + } else { + $user = elgg_get_logged_in_user_entity(); + elgg_set_page_owner_guid($user->guid); + } + + elgg_push_breadcrumb(elgg_echo('settings'), "settings/user/$user->username"); + + switch ($page[0]) { + case 'statistics': + elgg_push_breadcrumb(elgg_echo('usersettings:statistics:opt:linktext')); + $path = $CONFIG->path . "pages/settings/statistics.php"; + break; + case 'plugins': + elgg_push_breadcrumb(elgg_echo('usersettings:plugins:opt:linktext')); + $path = $CONFIG->path . "pages/settings/tools.php"; + break; + case 'user': + $path = $CONFIG->path . "pages/settings/account.php"; + break; + } + + if (isset($path)) { + require $path; + return true; + } + return false; +} + +/** + * Initialize the user settings library + * + * @return void + * @access private + */ +function usersettings_init() { + elgg_register_page_handler('settings', 'usersettings_page_handler'); + + elgg_register_plugin_hook_handler('usersettings:save', 'user', 'users_settings_save'); + + elgg_register_action("usersettings/save"); + + // extend the account settings form + elgg_extend_view('forms/account/settings', 'core/settings/account/name', 100); + elgg_extend_view('forms/account/settings', 'core/settings/account/password', 100); + elgg_extend_view('forms/account/settings', 'core/settings/account/email', 100); + elgg_extend_view('forms/account/settings', 'core/settings/account/language', 100); + elgg_extend_view('forms/account/settings', 'core/settings/account/default_access', 100); +} + +elgg_register_event_handler('init', 'system', 'usersettings_init'); +elgg_register_event_handler('pagesetup', 'system', 'usersettings_pagesetup'); diff --git a/engine/lib/users.php b/engine/lib/users.php index 1d7359806..a8fb9121c 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -8,9 +8,11 @@ */ /// Map a username to a cached GUID +global $USERNAME_TO_GUID_MAP_CACHE; $USERNAME_TO_GUID_MAP_CACHE = array(); /// Map a user code to a cached GUID +global $CODE_TO_GUID_MAP_CACHE; $CODE_TO_GUID_MAP_CACHE = array(); /** @@ -19,6 +21,7 @@ $CODE_TO_GUID_MAP_CACHE = array(); * @param int $guid The ElggUser guid * * @return mixed + * @access private */ function get_user_entity_as_row($guid) { global $CONFIG; @@ -28,7 +31,7 @@ function get_user_entity_as_row($guid) { } /** - * Create or update the extras table for a given user. + * Create or update the entities table for a given user. * Call create_entity first. * * @param int $guid The user's GUID @@ -41,6 +44,7 @@ function get_user_entity_as_row($guid) { * @param string $code A code * * @return bool + * @access private */ function create_user_entity($guid, $name, $username, $password, $salt, $email, $language, $code) { global $CONFIG; @@ -57,13 +61,12 @@ function create_user_entity($guid, $name, $username, $password, $salt, $email, $ $row = get_entity_as_row($guid); if ($row) { // Exists and you have access to it - $query = "SELECT guid from {$CONFIG->dbprefix}users_entity where guid = {$guid}"; if ($exists = get_data_row($query)) { $query = "UPDATE {$CONFIG->dbprefix}users_entity - set name='$name', username='$username', password='$password', salt='$salt', - email='$email', language='$language', code='$code', last_action = " - . time() . " where guid = {$guid}"; + SET name='$name', username='$username', password='$password', salt='$salt', + email='$email', language='$language', code='$code' + WHERE guid = $guid"; $result = update_data($query); if ($result != false) { @@ -76,7 +79,7 @@ function create_user_entity($guid, $name, $username, $password, $salt, $email, $ } } } else { - // Update failed, attempt an insert. + // Exists query failed, attempt an insert. $query = "INSERT into {$CONFIG->dbprefix}users_entity (guid, name, username, password, salt, email, language, code) values ($guid, '$name', '$username', '$password', '$salt', '$email', '$language', '$code')"; @@ -87,7 +90,7 @@ function create_user_entity($guid, $name, $username, $password, $salt, $email, $ if (elgg_trigger_event('create', $entity->type, $entity)) { return $guid; } else { - $entity->delete(); //delete_entity($guid); + $entity->delete(); } } } @@ -134,7 +137,6 @@ function ban_user($user_guid, $reason = "") { global $CONFIG; $user_guid = (int)$user_guid; - $reason = sanitise_string($reason); $user = get_entity($user_guid); @@ -235,7 +237,7 @@ function make_user_admin($user_guid) { } $r = update_data("UPDATE {$CONFIG->dbprefix}users_entity set admin='yes' where guid=$user_guid"); - invalidate_cache_for_entity($user_guid); + _elgg_invalidate_cache_for_entity($user_guid); return $r; } @@ -271,7 +273,7 @@ function remove_user_admin($user_guid) { } $r = update_data("UPDATE {$CONFIG->dbprefix}users_entity set admin='no' where guid=$user_guid"); - invalidate_cache_for_entity($user_guid); + _elgg_invalidate_cache_for_entity($user_guid); return $r; } @@ -282,30 +284,13 @@ function remove_user_admin($user_guid) { } /** - * THIS FUNCTION IS DEPRECATED. - * - * Delete a user's extra data. - * - * @todo remove - * - * @param int $guid User GUID - * - * @return 1 - */ -function delete_user_entity($guid) { - system_message(elgg_echo('deprecatedfunction', array('delete_user_entity'))); - - return 1; // Always return that we have deleted one row in order to not break existing code. -} - -/** * Get the sites this user is part of * * @param int $user_guid The user's GUID * @param int $limit Number of results to return * @param int $offset Any indexing offset * - * @return false|array On success, an array of ElggSites + * @return ElggSite[]|false On success, an array of ElggSites */ function get_user_sites($user_guid, $limit = 10, $offset = 0) { $user_guid = (int)$user_guid; @@ -313,13 +298,14 @@ function get_user_sites($user_guid, $limit = 10, $offset = 0) { $offset = (int)$offset; return elgg_get_entities_from_relationship(array( + 'site_guids' => ELGG_ENTITIES_ANY_VALUE, 'relationship' => 'member_of_site', 'relationship_guid' => $user_guid, 'inverse_relationship' => FALSE, - 'types' => 'site', + 'type' => 'site', 'limit' => $limit, - 'offset' => $offset) - ); + 'offset' => $offset, + )); } /** @@ -357,8 +343,6 @@ function user_add_friend($user_guid, $friend_guid) { * @return bool Depending on success */ function user_remove_friend($user_guid, $friend_guid) { - global $CONFIG; - $user_guid = (int) $user_guid; $friend_guid = (int) $friend_guid; @@ -382,7 +366,7 @@ function user_remove_friend($user_guid, $friend_guid) { * @return bool */ function user_is_friend($user_guid, $friend_guid) { - return check_entity_relationship($user_guid, "friend", $friend_guid); + return check_entity_relationship($user_guid, "friend", $friend_guid) !== false; } /** @@ -393,7 +377,7 @@ function user_is_friend($user_guid, $friend_guid) { * @param int $limit Number of results to return (default 10) * @param int $offset Indexing offset, if any * - * @return false|array Either an array of ElggUsers or false, depending on success + * @return ElggUser[]|false Either an array of ElggUsers or false, depending on success */ function get_user_friends($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $offset = 0) { @@ -401,8 +385,8 @@ $offset = 0) { return elgg_get_entities_from_relationship(array( 'relationship' => 'friend', 'relationship_guid' => $user_guid, - 'types' => 'user', - 'subtypes' => $subtype, + 'type' => 'user', + 'subtype' => $subtype, 'limit' => $limit, 'offset' => $offset )); @@ -416,7 +400,7 @@ $offset = 0) { * @param int $limit Number of results to return (default 10) * @param int $offset Indexing offset, if any * - * @return false|array Either an array of ElggUsers or false, depending on success + * @return ElggUser[]|false Either an array of ElggUsers or false, depending on success */ function get_user_friends_of($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $offset = 0) { @@ -425,96 +409,11 @@ $offset = 0) { 'relationship' => 'friend', 'relationship_guid' => $user_guid, 'inverse_relationship' => TRUE, - 'types' => 'user', - 'subtypes' => $subtype, - 'limit' => $limit, - 'offset' => $offset - )); -} - -/** - * Obtains a list of objects owned by a user - * - * @param int $user_guid The GUID of the owning user - * @param string $subtype Optionally, the subtype of objects - * @param int $limit The number of results to return (default 10) - * @param int $offset Indexing offset, if any - * @param int $timelower The earliest time the entity can have been created. Default: all - * @param int $timeupper The latest time the entity can have been created. Default: all - * - * @return false|array An array of ElggObjects or false, depending on success - * @deprecated 1.8 Use elgg_get_entities() instead - */ -function get_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, -$offset = 0, $timelower = 0, $timeupper = 0) { - elgg_deprecated_notice("get_user_objects() was deprecated in favor of elgg_get_entities()", 1.8); - $ntt = elgg_get_entities(array( - 'type' => 'object', + 'type' => 'user', 'subtype' => $subtype, - 'owner_guid' => $user_guid, 'limit' => $limit, - 'offset' => $offset, - 'container_guid' => $user_guid, - 'created_time_lower' => $timelower, - 'created_time_upper' => $timeupper - )); - return $ntt; -} - -/** - * Counts the objects (optionally of a particular subtype) owned by a user - * - * @param int $user_guid The GUID of the owning user - * @param string $subtype Optionally, the subtype of objects - * @param int $timelower The earliest time the entity can have been created. Default: all - * @param int $timeupper The latest time the entity can have been created. Default: all - * - * @return int The number of objects the user owns (of this subtype) - * @deprecated 1.8 Use elgg_get_entities() instead - */ -function count_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $timelower = 0, -$timeupper = 0) { - elgg_deprecated_notice("count_user_objects() was deprecated in favor of elgg_get_entities()", 1.8); - $total = elgg_get_entities(array( - 'type' => 'object', - 'subtype' => $subtype, - 'owner_guid' => $user_guid, - 'count' => TRUE, - 'container_guid' => $user_guid, - 'created_time_lower' => $timelower, - 'created_time_upper' => $timeupper + 'offset' => $offset )); - return $total; -} - -/** - * Displays a list of user objects of a particular subtype, with navigation. - * - * @see elgg_view_entity_list - * - * @param int $user_guid The GUID of the user - * @param string $subtype The object subtype - * @param int $limit The number of entities to display on a page - * @param bool $fullview Whether or not to display the full view (default: true) - * @param bool $listtypetoggle Whether or not to allow gallery view (default: true) - * @param bool $pagination Whether to display pagination (default: true) - * @param int $timelower The earliest time the entity can have been created. Default: all - * @param int $timeupper The latest time the entity can have been created. Default: all - * - * @return string The list in a form suitable to display - * @deprecated 1.8 Use elgg_list_entities() instead - */ -function list_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, -$fullview = true, $listtypetoggle = true, $pagination = true, $timelower = 0, $timeupper = 0) { - elgg_deprecated_notice("list_user_objects() was deprecated in favor of elgg_list_entities()", 1.8); - - $offset = (int) get_input('offset'); - $limit = (int) $limit; - $count = (int) count_user_objects($user_guid, $subtype, $timelower, $timeupper); - $entities = get_user_objects($user_guid, $subtype, $limit, $offset, $timelower, $timeupper); - - return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $listtypetoggle, - $pagination); } /** @@ -527,7 +426,7 @@ $fullview = true, $listtypetoggle = true, $pagination = true, $timelower = 0, $t * @param int $timelower The earliest time the entity can have been created. Default: all * @param int $timeupper The latest time the entity can have been created. Default: all * - * @return false|array An array of ElggObjects or false, depending on success + * @return ElggObject[]|false An array of ElggObjects or false, depending on success */ function get_user_friends_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $offset = 0, $timelower = 0, $timeupper = 0) { @@ -590,45 +489,32 @@ $timelower = 0, $timeupper = 0) { * @param int $user_guid The GUID of the user * @param string $subtype The object subtype * @param int $limit The number of entities to display on a page - * @param bool $fullview Whether or not to display the full view (default: true) + * @param bool $full_view Whether or not to display the full view (default: true) * @param bool $listtypetoggle Whether or not to allow you to flip to gallery mode (default: true) * @param bool $pagination Whether to display pagination (default: true) * @param int $timelower The earliest time the entity can have been created. Default: all * @param int $timeupper The latest time the entity can have been created. Default: all * - * @return string The list in a form suitable to display + * @return string */ -function list_user_friends_objects($user_guid, $subtype = "", $limit = 10, $fullview = true, +function list_user_friends_objects($user_guid, $subtype = "", $limit = 10, $full_view = true, $listtypetoggle = true, $pagination = true, $timelower = 0, $timeupper = 0) { - $offset = (int) get_input('offset'); - $limit = (int) $limit; - $count = (int) count_user_friends_objects($user_guid, $subtype, $timelower, $timeupper); + $offset = (int)get_input('offset'); + $limit = (int)$limit; + $count = (int)count_user_friends_objects($user_guid, $subtype, $timelower, $timeupper); $entities = get_user_friends_objects($user_guid, $subtype, $limit, $offset, $timelower, $timeupper); - return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, - $listtypetoggle, $pagination); -} - -/** - * Get user objects by an array of metadata - * - * @param int $user_guid The GUID of the owning user - * @param string $subtype Optionally, the subtype of objects - * @param array $metadata An array of metadata - * @param int $limit The number of results to return (default 10) - * @param int $offset Indexing offset, if any - * - * @return false|array An array of ElggObjects or false, depending on success - * @deprecated 1.8 Use elgg_get_entities_from_metadata() instead - */ -function get_user_objects_by_metadata($user_guid, $subtype = "", $metadata = array(), -$limit = 0, $offset = 0) { - elgg_deprecated_notice("get_user_objects_by_metadata() was deprecated in favor of elgg_get_entities_from_metadata()", 1.8); - return get_entities_from_metadata_multi($metadata, "object", $subtype, $user_guid, - $limit, $offset); + return elgg_view_entity_list($entities, array( + 'count' => $count, + 'offset' => $offset, + 'limit' => $limit, + 'full_view' => $full_view, + 'list_type_toggle' => $listtypetoggle, + 'pagination' => $pagination, + )); } /** @@ -667,26 +553,32 @@ function get_user($guid) { function get_user_by_username($username) { global $CONFIG, $USERNAME_TO_GUID_MAP_CACHE; + // Fixes #6052. Username is frequently sniffed from the path info, which, + // unlike $_GET, is not URL decoded. If the username was not URL encoded, + // this is harmless. + $username = rawurldecode($username); + $username = sanitise_string($username); $access = get_access_sql_suffix('e'); // Caching if ((isset($USERNAME_TO_GUID_MAP_CACHE[$username])) - && (retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]))) { - return retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]); + && (_elgg_retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]))) { + return _elgg_retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]); } $query = "SELECT e.* from {$CONFIG->dbprefix}users_entity u join {$CONFIG->dbprefix}entities e on e.guid=u.guid where u.username='$username' and $access "; - $row = get_data_row($query); - if ($row) { - $USERNAME_TO_GUID_MAP_CACHE[$username] = $row->guid; - return new ElggUser($row); + $entity = get_data_row($query, 'entity_row_to_elggstar'); + if ($entity) { + $USERNAME_TO_GUID_MAP_CACHE[$username] = $entity->guid; + } else { + $entity = false; } - return false; + return $entity; } /** @@ -705,30 +597,29 @@ function get_user_by_code($code) { // Caching if ((isset($CODE_TO_GUID_MAP_CACHE[$code])) - && (retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]))) { + && (_elgg_retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]))) { - return retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]); + return _elgg_retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]); } $query = "SELECT e.* from {$CONFIG->dbprefix}users_entity u join {$CONFIG->dbprefix}entities e on e.guid=u.guid where u.code='$code' and $access"; - $row = get_data_row($query); - if ($row) { - $CODE_TO_GUID_MAP_CACHE[$code] = $row->guid; - return new ElggUser($row); + $entity = get_data_row($query, 'entity_row_to_elggstar'); + if ($entity) { + $CODE_TO_GUID_MAP_CACHE[$code] = $entity->guid; } - return false; + return $entity; } /** - * Get an array of users from their + * Get an array of users from an email address * * @param string $email Email address. * - * @return Array of users + * @return array */ function get_user_by_email($email) { global $CONFIG; @@ -745,103 +636,38 @@ function get_user_by_email($email) { } /** - * Searches for a user based on a complete or partial name or username. - * - * @param string $criteria The partial or full name or username. - * @param int $limit Limit of the search. - * @param int $offset Offset. - * @param string $order_by The order. - * @param boolean $count Whether to return the count of results or just the results. - * - * @return mixed - * @deprecated 1.7 - */ -function search_for_user($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) { - elgg_deprecated_notice('search_for_user() was deprecated by new search.', 1.7); - global $CONFIG; - - $criteria = sanitise_string($criteria); - $limit = (int)$limit; - $offset = (int)$offset; - $order_by = sanitise_string($order_by); - - $access = get_access_sql_suffix("e"); - - if ($order_by == "") { - $order_by = "e.time_created desc"; - } - - if ($count) { - $query = "SELECT count(e.guid) as total "; - } else { - $query = "SELECT e.* "; - } - $query .= "from {$CONFIG->dbprefix}entities e - join {$CONFIG->dbprefix}users_entity u on e.guid=u.guid where "; - - $query .= "(u.name like \"%{$criteria}%\" or u.username like \"%{$criteria}%\")"; - $query .= " and $access"; - - if (!$count) { - $query .= " order by $order_by limit $offset, $limit"; - return get_data($query, "entity_row_to_elggstar"); - } else { - if ($count = get_data_row($query)) { - return $count->total; - } - } - return false; -} - -/** - * Displays a list of user objects that have been searched for. - * - * @see elgg_view_entity_list - * - * @param string $tag Search criteria - * @param int $limit The number of entities to display on a page - * - * @return string The list in a form suitable to display - * - * @deprecated 1.7 - */ -function list_user_search($tag, $limit = 10) { - elgg_deprecated_notice('list_user_search() deprecated by new search', 1.7); - $offset = (int) get_input('offset'); - $limit = (int) $limit; - $count = (int) search_for_user($tag, 10, 0, '', true); - $entities = search_for_user($tag, $limit, $offset); - - return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, false); -} - -/** * A function that returns a maximum of $limit users who have done something within the last - * $seconds seconds. + * $seconds seconds or the total count of active users. * - * @param int $seconds Number of seconds (default 600 = 10min) - * @param int $limit Limit, default 10. - * @param int $offset Offset, defualt 0. + * @param int $seconds Number of seconds (default 600 = 10min) + * @param int $limit Limit, default 10. + * @param int $offset Offset, default 0. + * @param bool $count Count, default false. * * @return mixed */ -function find_active_users($seconds = 600, $limit = 10, $offset = 0) { - global $CONFIG; - +function find_active_users($seconds = 600, $limit = 10, $offset = 0, $count = false) { $seconds = (int)$seconds; $limit = (int)$limit; $offset = (int)$offset; + $params = array('seconds' => $seconds, 'limit' => $limit, 'offset' => $offset, 'count' => $count); + $data = elgg_trigger_plugin_hook('find_active_users', 'system', $params, NULL); + if (!$data) { + global $CONFIG; - $time = time() - $seconds; - - $access = get_access_sql_suffix("e"); - - $query = "SELECT distinct e.* from {$CONFIG->dbprefix}entities e - join {$CONFIG->dbprefix}users_entity u on e.guid = u.guid - where u.last_action >= {$time} and $access - order by u.last_action desc limit {$offset}, {$limit}"; + $time = time() - $seconds; - return get_data($query, "entity_row_to_elggstar"); + $data = elgg_get_entities(array( + 'type' => 'user', + 'limit' => $limit, + 'offset' => $offset, + 'count' => $count, + 'joins' => array("join {$CONFIG->dbprefix}users_entity u on e.guid = u.guid"), + 'wheres' => array("u.last_action >= {$time}"), + 'order_by' => "u.last_action desc" + )); + } + return $data; } /** @@ -852,25 +678,22 @@ function find_active_users($seconds = 600, $limit = 10, $offset = 0) { * @return bool */ function send_new_password_request($user_guid) { - global $CONFIG; - $user_guid = (int)$user_guid; $user = get_entity($user_guid); - if ($user) { + if ($user instanceof ElggUser) { // generate code $code = generate_random_cleartext_password(); - //create_metadata($user_guid, 'conf_code', $code, '', 0, ACCESS_PRIVATE); - set_private_setting($user_guid, 'passwd_conf_code', $code); + $user->setPrivateSetting('passwd_conf_code', $code); // generate link - $link = $CONFIG->site->url . "pg/resetpassword?u=$user_guid&c=$code"; + $link = elgg_get_site_url() . "resetpassword?u=$user_guid&c=$code"; // generate email $email = elgg_echo('email:resetreq:body', array($user->name, $_SERVER['REMOTE_ADDR'], $link)); - return notify_user($user->guid, $CONFIG->site->guid, - elgg_echo('email:resetreq:subject'), $email, NULL, 'email'); + return notify_user($user->guid, elgg_get_site_entity()->guid, + elgg_echo('email:resetreq:subject'), $email, array(), 'email'); } return false; @@ -887,19 +710,18 @@ function send_new_password_request($user_guid) { * @return bool */ function force_user_password_reset($user_guid, $password) { - global $CONFIG; - $user = get_entity($user_guid); + if ($user instanceof ElggUser) { + $ia = elgg_set_ignore_access(); - if ($user) { - $salt = generate_random_cleartext_password(); // Reset the salt - $user->salt = $salt; + $user->salt = generate_random_cleartext_password(); + $hash = generate_user_password($user, $password); + $user->password = $hash; + $result = (bool)$user->save(); - $hash = generate_user_password($user, $password); + elgg_set_ignore_access($ia); - $query = "UPDATE {$CONFIG->dbprefix}users_entity - set password='$hash', salt='$salt' where guid=$user_guid"; - return update_data($query); + return $result; } return false; @@ -919,18 +741,22 @@ function execute_new_password_request($user_guid, $conf_code) { $user_guid = (int)$user_guid; $user = get_entity($user_guid); - $saved_code = get_private_setting($user_guid, 'passwd_conf_code'); - - if ($user && $saved_code && $saved_code == $conf_code) { - $password = generate_random_cleartext_password(); + if ($user instanceof ElggUser) { + $saved_code = $user->getPrivateSetting('passwd_conf_code'); - if (force_user_password_reset($user_guid, $password)) { - remove_private_setting($user_guid, 'passwd_conf_code'); + if ($saved_code && $saved_code == $conf_code) { + $password = generate_random_cleartext_password(); - $email = elgg_echo('email:resetpassword:body', array($user->name, $password)); + if (force_user_password_reset($user_guid, $password)) { + remove_private_setting($user_guid, 'passwd_conf_code'); + // clean the logins failures + reset_login_failure_count($user_guid); + + $email = elgg_echo('email:resetpassword:body', array($user->name, $password)); - return notify_user($user->guid, $CONFIG->site->guid, - elgg_echo('email:resetpassword:subject'), $email, NULL, 'email'); + return notify_user($user->guid, $CONFIG->site->guid, + elgg_echo('email:resetpassword:subject'), $email, array(), 'email'); + } } } @@ -938,54 +764,6 @@ function execute_new_password_request($user_guid, $conf_code) { } /** - * Handles pages for password reset requests. - * - * @param array $page Pages array - * - * @return void - */ -function elgg_user_resetpassword_page_handler($page) { - global $CONFIG; - - $user_guid = get_input('u'); - $code = get_input('c'); - - $user = get_entity($user_guid); - - // don't check code here to avoid automated attacks - if (!$user instanceof ElggUser) { - register_error(elgg_echo('user:passwordreset:unknown_user')); - forward(); - } - - $form_body = elgg_echo('user:resetpassword:reset_password_confirm') . "<br />"; - - $form_body .= elgg_view('input/hidden', array( - 'internalname' => 'u', - 'value' => $user_guid - )); - - $form_body .= elgg_view('input/hidden', array( - 'internalname' => 'c', - 'value' => $code - )); - - $form_body .= elgg_view('input/submit', array( - 'value' => elgg_echo('resetpassword') - )); - - $form .= elgg_view('input/form', array( - 'body' => $form_body, - 'action' => 'action/user/passwordreset' - )); - - $title = elgg_echo('resetpassword'); - $content = elgg_view_title(elgg_echo('resetpassword')) . $form; - - echo elgg_view_page($title, elgg_view_layout('one_column', array('content' => $content))); -} - -/** * Simple function that will generate a random clear text password * suitable for feeding into generate_user_password(). * @@ -1028,7 +806,14 @@ function validate_username($username) { } if (strlen($username) < $CONFIG->minusername) { - throw new RegistrationException(elgg_echo('registration:usernametooshort')); + $msg = elgg_echo('registration:usernametooshort', array($CONFIG->minusername)); + throw new RegistrationException($msg); + } + + // username in the database has a limit of 128 characters + if (strlen($username) > 128) { + $msg = elgg_echo('registration:usernametoolong', array(128)); + throw new RegistrationException($msg); } // Blacklist for bad characters (partially nicked from mediawiki) @@ -1044,6 +829,7 @@ function validate_username($username) { if ( preg_match($blacklist, $username) ) { + // @todo error message needs work throw new RegistrationException(elgg_echo('registration:invalidchars')); } @@ -1054,6 +840,7 @@ function validate_username($username) { for ($n = 0; $n < strlen($blacklist2); $n++) { if (strpos($username, $blacklist2[$n]) !== false) { $msg = elgg_echo('registration:invalidchars', array($blacklist2[$n], $blacklist2)); + $msg = htmlspecialchars($msg, ENT_QUOTES, 'UTF-8'); throw new RegistrationException($msg); } } @@ -1074,8 +861,13 @@ function validate_username($username) { function validate_password($password) { global $CONFIG; + if (!isset($CONFIG->min_password_length)) { + $CONFIG->min_password_length = 6; + } + if (strlen($password) < $CONFIG->min_password_length) { - throw new RegistrationException(elgg_echo('registration:passwordtooshort')); + $msg = elgg_echo('registration:passwordtooshort', array($CONFIG->min_password_length)); + throw new RegistrationException($msg); } $result = true; @@ -1115,13 +907,11 @@ function validate_email_address($address) { * @param string $invitecode An invite code from a friend * * @return int|false The new user's GUID; false on failure + * @throws RegistrationException */ function register_user($username, $password, $name, $email, $allow_multiple_emails = false, $friend_guid = 0, $invitecode = '') { - // Load the configuration - global $CONFIG; - // no need to trim password. $username = trim($username); $name = trim(strip_tags($name)); @@ -1171,6 +961,7 @@ $allow_multiple_emails = false, $friend_guid = 0, $invitecode = '') { $user->password = generate_user_password($user, $password); $user->owner_guid = 0; // Users aren't owned by anyone, even if they are admin created. $user->container_guid = 0; // Users aren't contained by anyone, even if they are admin created. + $user->language = get_current_language(); $user->save(); // If $friend_guid has been set, make mutual friends @@ -1181,28 +972,12 @@ $allow_multiple_emails = false, $friend_guid = 0, $invitecode = '') { $friend_user->addFriend($user->guid); // @todo Should this be in addFriend? - add_to_river('friends/river/create', 'friend', $user->getGUID(), $friend_guid); - add_to_river('friends/river/create', 'friend', $friend_guid, $user->getGUID()); + add_to_river('river/relationship/friend/create', 'friend', $user->getGUID(), $friend_guid); + add_to_river('river/relationship/friend/create', 'friend', $friend_guid, $user->getGUID()); } } } - // Check to see if we've registered the first admin yet. - // If not, this is the first admin user! - $have_admin = datalist_get('admin_registered'); - - if (!$have_admin) { - // makeAdmin() calls ElggUser::canEdit(). - // right now no one is logged in and so canEdit() returns false. - // instead of making an override for this one instance that is called on every - // canEdit() call, just override the access system to set the first admin user. - // @todo remove this when Cash merges in the new installer - $ia = elgg_set_ignore_access(TRUE); - $user->makeAdmin(); - datalist_set('admin_registered', 1); - elgg_set_ignore_access($ia); - } - // Turn on email notifications by default set_user_notification_setting($user->getGUID(), 'email', true); @@ -1248,12 +1023,15 @@ function elgg_set_user_validation_status($user_guid, $status, $method = '') { * @since 1.8.0 */ function elgg_get_user_validation_status($user_guid) { - $md = get_metadata_byname($user_guid, 'validated'); + $md = elgg_get_metadata(array( + 'guid' => $user_guid, + 'metadata_name' => 'validated' + )); if ($md == false) { - return; + return null; } - if ($md->value) { + if ($md[0]->value) { return true; } @@ -1261,89 +1039,52 @@ function elgg_get_user_validation_status($user_guid) { } /** - * Set the validation status for a user. - * - * @param bool $status Validated (true) or false - * @param string $method Optional method to say how a user was validated - * @return bool - * @deprecated 1.8 - */ -function set_user_validation_status($user_guid, $status, $method = '') { - elgg_deprecated_notice("set_user_validation_status() is deprecated", 1.8); - return elgg_set_user_validation_status($user_guid, $status, $method); -} - -/** - * Trigger an event requesting that a user guid be validated somehow - either by email address or some other way. - * - * This function invalidates any existing validation value. - * - * @param int $user_guid User's GUID - * @deprecated 1.8 - */ -function request_user_validation($user_guid) { - elgg_deprecated_notice("request_user_validation() is deprecated. - Plugins should register for the 'register, user' plugin hook", 1.8); - $user = get_entity($user_guid); - - if (($user) && ($user instanceof ElggUser)) { - // invalidate any existing validations - set_user_validation_status($user_guid, false); - - // request validation - trigger_elgg_event('validate', 'user', $user); - } -} - -/** * Adds collection submenu items * * @return void + * @access private */ function collections_submenu_items() { - global $CONFIG; - $user = get_loggedin_user(); - add_submenu_item(elgg_echo('friends:collections'), - $CONFIG->wwwroot . "pg/collections/" . $user->username); + $user = elgg_get_logged_in_user_entity(); - add_submenu_item(elgg_echo('friends:collections:add'), $CONFIG->wwwroot . "pg/collections/add"); + elgg_register_menu_item('page', array( + 'name' => 'friends:view:collections', + 'text' => elgg_echo('friends:collections'), + 'href' => "collections/$user->username", + )); } /** - * Page handler for friends + * Page handler for friends-related pages * - * @param array $page_elements Page elements + * @param array $segments URL segments + * @param string $handler The first segment in URL used for routing * - * @return void + * @return bool + * @access private */ -function friends_page_handler($page_elements) { - if (isset($page_elements[0]) && $user = get_user_by_username($page_elements[0])) { - set_page_owner($user->getGUID()); +function friends_page_handler($segments, $handler) { + elgg_set_context('friends'); + + if (isset($segments[0]) && $user = get_user_by_username($segments[0])) { + elgg_set_page_owner_guid($user->getGUID()); } - if (get_loggedin_userid() == elgg_get_page_owner_guid()) { - // disabled for now as we no longer use friends collections (replaced by shared access) - // collections_submenu_items(); + if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid()) { + collections_submenu_items(); } - require_once(dirname(dirname(dirname(__FILE__))) . "/pages/friends/index.php"); -} -/** - * Page handler for friends of - * - * @param array $page_elements Page elements - * - * @return void - */ -function friends_of_page_handler($page_elements) { - if (isset($page_elements[0]) && $user = get_user_by_username($page_elements[0])) { - set_page_owner($user->getGUID()); + switch ($handler) { + case 'friends': + require_once(dirname(dirname(dirname(__FILE__))) . "/pages/friends/index.php"); + break; + case 'friendsof': + require_once(dirname(dirname(dirname(__FILE__))) . "/pages/friends/of.php"); + break; + default: + return false; } - if (get_loggedin_userid() == elgg_get_page_owner_guid()) { - // disabled for now as we no longer use friends collections (replaced by shared access) - // collections_submenu_items(); - } - require_once(dirname(dirname(dirname(__FILE__))) . "/pages/friends/of.php"); + return true; } /** @@ -1351,62 +1092,63 @@ function friends_of_page_handler($page_elements) { * * @param array $page_elements Page elements * - * @return void + * @return bool + * @access private */ function collections_page_handler($page_elements) { + gatekeeper(); + elgg_set_context('friends'); + $base = elgg_get_config('path'); if (isset($page_elements[0])) { if ($page_elements[0] == "add") { - set_page_owner(get_loggedin_userid()); + elgg_set_page_owner_guid(elgg_get_logged_in_user_guid()); collections_submenu_items(); - require_once(dirname(dirname(dirname(__FILE__))) . "/pages/friends/add.php"); + require_once "{$base}pages/friends/collections/add.php"; + return true; } else { - if ($user = get_user_by_username($page_elements[0])) { - set_page_owner($user->getGUID()); - if (get_loggedin_userid() == elgg_get_page_owner_guid()) { + $user = get_user_by_username($page_elements[0]); + if ($user) { + elgg_set_page_owner_guid($user->getGUID()); + if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid()) { collections_submenu_items(); } - require_once(dirname(dirname(dirname(__FILE__))) . "/pages/friends/collections.php"); + require_once "{$base}pages/friends/collections/view.php"; + return true; } } } + return false; } /** - * Page handler for dashboard - * - * @param array $page_elements Page elements - * - * @return void - */ -function dashboard_page_handler($page_elements) { - require_once(dirname(dirname(dirname(__FILE__))) . "/pages/dashboard/index.php"); -} - - -/** - * Page handler for registration - * - * @param array $page_elements Page elements - * - * @return void - */ -function registration_page_handler($page_elements) { - require_once(dirname(dirname(dirname(__FILE__))) . "/pages/account/register.php"); -} - -/** - * Display a login box. + * Page handler for account related pages * - * This is a fallback for non-JS users who click on the - * dropdown login link. + * @param array $page_elements Page elements + * @param string $handler The handler string * - * @return void - * @todo finish + * @return bool + * @access private */ -function elgg_user_login_page_handler() { - $login_box = elgg_view('account/login_box'); - $content = elgg_view_layout('one_column', array('content' => $login_box)); - echo elgg_view_page(elgg_echo('login'), $content); +function elgg_user_account_page_handler($page_elements, $handler) { + + $base_dir = elgg_get_root_path() . 'pages/account'; + switch ($handler) { + case 'login': + require_once("$base_dir/login.php"); + break; + case 'forgotpassword': + require_once("$base_dir/forgotten_password.php"); + break; + case 'resetpassword': + require_once("$base_dir/reset_password.php"); + break; + case 'register': + require_once("$base_dir/register.php"); + break; + default: + return false; + } + return true; } /** @@ -1433,7 +1175,7 @@ function set_last_action($user_guid) { * * @param int $user_guid The user GUID * - * @return boid + * @return void */ function set_last_login($user_guid) { $user_guid = (int) $user_guid; @@ -1453,169 +1195,409 @@ function set_last_login($user_guid) { * @param string $object_type user * @param ElggUser $object User object * - * @return bool + * @return void + * @access private */ function user_create_hook_add_site_relationship($event, $object_type, $object) { - global $CONFIG; - - add_entity_relationship($object->getGUID(), 'member_of_site', $CONFIG->site->getGUID()); + add_entity_relationship($object->getGUID(), 'member_of_site', elgg_get_site_entity()->guid); } /** - * Sets up user-related menu items + * Serves the user's avatar * - * @return void + * @param string $hook + * @param string $entity_type + * @param string $returnvalue + * @param array $params + * @return string + * @access private */ -function users_pagesetup() { - // Load config - global $CONFIG; +function user_avatar_hook($hook, $entity_type, $returnvalue, $params) { + $user = $params['entity']; + $size = $params['size']; + + if (isset($user->icontime)) { + return "avatar/view/$user->username/$size/$user->icontime"; + } else { + return "_graphics/icons/user/default{$size}.gif"; + } +} + +/** + * Setup the default user hover menu + * @access private + */ +function elgg_user_hover_menu($hook, $type, $return, $params) { + $user = $params['entity']; + /* @var ElggUser $user */ + + if (elgg_is_logged_in()) { + if (elgg_get_logged_in_user_guid() != $user->guid) { + if ($user->isFriend()) { + $url = "action/friends/remove?friend={$user->guid}"; + $text = elgg_echo('friend:remove'); + $name = 'remove_friend'; + } else { + $url = "action/friends/add?friend={$user->guid}"; + $text = elgg_echo('friend:add'); + $name = 'add_friend'; + } + $url = elgg_add_action_tokens_to_url($url); + $item = new ElggMenuItem($name, $text, $url); + $item->setSection('action'); + $return[] = $item; + } else { + $url = "profile/$user->username/edit"; + $item = new ElggMenuItem('profile:edit', elgg_echo('profile:edit'), $url); + $item->setSection('action'); + $return[] = $item; + + $url = "avatar/edit/$user->username"; + $item = new ElggMenuItem('avatar:edit', elgg_echo('avatar:edit'), $url); + $item->setSection('action'); + $return[] = $item; + } + } - //add submenu options - if (elgg_get_context() == "friends" || elgg_get_context() == "friendsof") { - // || elgg_get_context() == "collections") { - disabled as we no longer use collections + // prevent admins from banning or deleting themselves + if (elgg_get_logged_in_user_guid() == $user->guid) { + return $return; + } - add_submenu_item(elgg_echo('friends'), $CONFIG->wwwroot . "pg/friends/" - . elgg_get_page_owner()->username); + if (elgg_is_admin_logged_in()) { + $actions = array(); + if (!$user->isBanned()) { + $actions[] = 'ban'; + } else { + $actions[] = 'unban'; + } + $actions[] = 'delete'; + $actions[] = 'resetpassword'; + if (!$user->isAdmin()) { + $actions[] = 'makeadmin'; + } else { + $actions[] = 'removeadmin'; + } - add_submenu_item(elgg_echo('friends:of'), $CONFIG->wwwroot . "pg/friendsof/" - . elgg_get_page_owner()->username); + foreach ($actions as $action) { + $url = "action/admin/user/$action?guid={$user->guid}"; + $url = elgg_add_action_tokens_to_url($url); + $item = new ElggMenuItem($action, elgg_echo($action), $url); + $item->setSection('admin'); + $item->setLinkClass('elgg-requires-confirmation'); - if (is_plugin_enabled('members')) { - add_submenu_item(elgg_echo('members:browse'), $CONFIG->wwwroot . "mod/members/index.php"); + $return[] = $item; } + + $url = "profile/$user->username/edit"; + $item = new ElggMenuItem('profile:edit', elgg_echo('profile:edit'), $url); + $item->setSection('admin'); + $return[] = $item; + + $url = "settings/user/$user->username"; + $item = new ElggMenuItem('settings:edit', elgg_echo('settings:edit'), $url); + $item->setSection('admin'); + $return[] = $item; } + + return $return; } /** - * Users initialisation function, which establishes the page handler + * Setup the menu shown with an entity * - * @return void + * @param string $hook + * @param string $type + * @param array $return + * @param array $params + * @return array + * + * @access private */ -function users_init() { - // Load config - global $CONFIG; +function elgg_users_setup_entity_menu($hook, $type, $return, $params) { + if (elgg_in_context('widgets')) { + return $return; + } - // add Friends to tools menu - if profile mod is running - // now added to toolbar - /* - if ( isloggedin() && is_plugin_enabled('profile') ) { - $user = get_loggedin_user(); - add_menu(elgg_echo('friends'), $CONFIG->wwwroot . - "pg/friends/" . $user->username, array(), 'core:friends'); + $entity = $params['entity']; + if (!elgg_instanceof($entity, 'user')) { + return $return; + } + /* @var ElggUser $entity */ + + if ($entity->isBanned()) { + $banned = elgg_echo('banned'); + $options = array( + 'name' => 'banned', + 'text' => "<span>$banned</span>", + 'href' => false, + 'priority' => 0, + ); + $return = array(ElggMenuItem::factory($options)); + } else { + $return = array(); + if (isset($entity->location)) { + $location = htmlspecialchars($entity->location, ENT_QUOTES, 'UTF-8', false); + $options = array( + 'name' => 'location', + 'text' => "<span>$location</span>", + 'href' => false, + 'priority' => 150, + ); + $return[] = ElggMenuItem::factory($options); + } } - */ - register_page_handler('friends', 'friends_page_handler'); - register_page_handler('friendsof', 'friends_of_page_handler'); - register_page_handler('dashboard', 'dashboard_page_handler'); - register_page_handler('register', 'registration_page_handler'); - register_page_handler('resetpassword', 'elgg_user_resetpassword_page_handler'); - register_page_handler('login', 'elgg_user_login_page_handler'); + return $return; +} - elgg_register_action("register", '', 'public'); - elgg_register_action("useradd", '', 'public'); - elgg_register_action("friends/add"); - elgg_register_action("friends/remove"); - //elgg_register_action('friends/addcollection'); - //elgg_register_action('friends/deletecollection'); - //elgg_register_action('friends/editcollection'); - //elgg_register_action("user/spotlight"); +/** + * This function loads a set of default fields into the profile, then triggers a hook letting other plugins to edit + * add and delete fields. + * + * Note: This is a secondary system:init call and is run at a super low priority to guarantee that it is called after all + * other plugins have initialised. + * @access private + */ +function elgg_profile_fields_setup() { + global $CONFIG; - elgg_register_action("usersettings/save"); + $profile_defaults = array ( + 'description' => 'longtext', + 'briefdescription' => 'text', + 'location' => 'location', + 'interests' => 'tags', + 'skills' => 'tags', + 'contactemail' => 'email', + 'phone' => 'text', + 'mobile' => 'text', + 'website' => 'url', + 'twitter' => 'text' + ); - elgg_register_action("user/passwordreset", '', 'public'); - elgg_register_action("user/requestnewpassword", '', 'public'); + $loaded_defaults = array(); + if ($fieldlist = elgg_get_config('profile_custom_fields')) { + if (!empty($fieldlist)) { + $fieldlistarray = explode(',', $fieldlist); + foreach ($fieldlistarray as $listitem) { + if ($translation = elgg_get_config("admin_defined_profile_{$listitem}")) { + $type = elgg_get_config("admin_defined_profile_type_{$listitem}"); + $loaded_defaults["admin_defined_profile_{$listitem}"] = $type; + add_translation(get_current_language(), array("profile:admin_defined_profile_{$listitem}" => $translation)); + } + } + } + } - // User name change - extend_elgg_settings_page('user/settings/name', 'usersettings/user', 1); - //elgg_register_action("user/name"); + if (count($loaded_defaults)) { + $CONFIG->profile_using_custom = true; + $profile_defaults = $loaded_defaults; + } - // User password change - extend_elgg_settings_page('user/settings/password', 'usersettings/user', 1); - //elgg_register_action("user/password"); + $CONFIG->profile_fields = elgg_trigger_plugin_hook('profile:fields', 'profile', NULL, $profile_defaults); - // Add email settings - extend_elgg_settings_page('user/settings/email', 'usersettings/user', 1); - //elgg_register_action("email/save"); + // register any tag metadata names + foreach ($CONFIG->profile_fields as $name => $type) { + if ($type == 'tags' || $type == 'location' || $type == 'tag') { + elgg_register_tag_metadata_name($name); + // register a tag name translation + add_translation(get_current_language(), array("tag_names:$name" => elgg_echo("profile:$name"))); + } + } +} - // Add language settings - extend_elgg_settings_page('user/settings/language', 'usersettings/user', 1); +/** + * Avatar page handler + * + * /avatar/edit/<username> + * /avatar/view/<username>/<size>/<icontime> + * + * @param array $page + * @return bool + * @access private + */ +function elgg_avatar_page_handler($page) { + global $CONFIG; - // Add default access settings - extend_elgg_settings_page('user/settings/default_access', 'usersettings/user', 1); + $user = get_user_by_username($page[1]); + if ($user) { + elgg_set_page_owner_guid($user->getGUID()); + } - //elgg_register_action("user/language"); + if ($page[0] == 'edit') { + require_once("{$CONFIG->path}pages/avatar/edit.php"); + return true; + } else { + set_input('size', $page[2]); + require_once("{$CONFIG->path}pages/avatar/view.php"); + return true; + } + return false; +} - // Register the user type - register_entity_type('user', ''); +/** + * Profile page handler + * + * @param array $page + * @return bool + * @access private + */ +function elgg_profile_page_handler($page) { + global $CONFIG; - elgg_register_plugin_hook_handler('usersettings:save', 'user', 'users_settings_save'); + $user = get_user_by_username($page[0]); + elgg_set_page_owner_guid($user->guid); - elgg_register_event_handler('create', 'user', 'user_create_hook_add_site_relationship'); + if ($page[1] == 'edit') { + require_once("{$CONFIG->path}pages/profile/edit.php"); + return true; + } + return false; } /** - * Returns a formatted list of users suitable for injecting into search. - * - * @deprecated 1.7 - * - * @param string $hook Hook name - * @param string $user User? - * @param mixed $returnvalue Previous hook's return value - * @param mixed $tag Tag to search against + * Sets up user-related menu items * * @return void + * @access private */ -function search_list_users_by_name($hook, $user, $returnvalue, $tag) { - elgg_deprecated_notice('search_list_users_by_name() was deprecated by new search', 1.7); - // Change this to set the number of users that display on the search page - $threshold = 4; +function users_pagesetup() { + + $owner = elgg_get_page_owner_entity(); + $viewer = elgg_get_logged_in_user_entity(); + + if ($owner) { + $params = array( + 'name' => 'friends', + 'text' => elgg_echo('friends'), + 'href' => 'friends/' . $owner->username, + 'contexts' => array('friends') + ); + elgg_register_menu_item('page', $params); + + $params = array( + 'name' => 'friends:of', + 'text' => elgg_echo('friends:of'), + 'href' => 'friendsof/' . $owner->username, + 'contexts' => array('friends') + ); + elgg_register_menu_item('page', $params); + + elgg_register_menu_item('page', array( + 'name' => 'edit_avatar', + 'href' => "avatar/edit/{$owner->username}", + 'text' => elgg_echo('avatar:edit'), + 'contexts' => array('profile_edit'), + )); - $object = get_input('object'); + elgg_register_menu_item('page', array( + 'name' => 'edit_profile', + 'href' => "profile/{$owner->username}/edit", + 'text' => elgg_echo('profile:edit'), + 'contexts' => array('profile_edit'), + )); + } - if (!get_input('offset') && (empty($object) || $object == 'user')) { - if ($users = search_for_user($tag, $threshold)) { - $countusers = search_for_user($tag, 0, 0, "", true); + // topbar + if ($viewer) { + elgg_register_menu_item('topbar', array( + 'name' => 'profile', + 'href' => $viewer->getURL(), + 'text' => elgg_view('output/img', array( + 'src' => $viewer->getIconURL('topbar'), + 'alt' => $viewer->name, + 'title' => elgg_echo('profile'), + 'class' => 'elgg-border-plain elgg-transition', + )), + 'priority' => 100, + 'link_class' => 'elgg-topbar-avatar', + )); - $return = elgg_view('user/search/startblurb', array('count' => $countusers, 'tag' => $tag)); - foreach ($users as $user) { - $return .= elgg_view_entity($user); - } + elgg_register_menu_item('topbar', array( + 'name' => 'friends', + 'href' => "friends/{$viewer->username}", + 'text' => elgg_view_icon('users'), + 'title' => elgg_echo('friends'), + 'priority' => 300, + )); - $vars = array('count' => $countusers, 'threshold' => $threshold, 'tag' => $tag); - $return .= elgg_view('user/search/finishblurb', $vars); - return $return; + elgg_register_menu_item('topbar', array( + 'name' => 'usersettings', + 'href' => "settings/user/{$viewer->username}", + 'text' => elgg_view_icon('settings') . elgg_echo('settings'), + 'priority' => 500, + 'section' => 'alt', + )); - } + elgg_register_menu_item('topbar', array( + 'name' => 'logout', + 'href' => "action/logout", + 'text' => elgg_echo('logout'), + 'is_action' => TRUE, + 'priority' => 1000, + 'section' => 'alt', + )); } } /** - * Saves user settings by directly including actions. - * - * @todo this is dirty. + * Users initialisation function, which establishes the page handler * * @return void + * @access private */ -function users_settings_save() { - global $CONFIG; - //@todo Wha?? - include($CONFIG->path . "actions/user/name.php"); - include($CONFIG->path . "actions/user/password.php"); - include($CONFIG->path . "actions/email/save.php"); - include($CONFIG->path . "actions/user/language.php"); - include($CONFIG->path . "actions/user/default_access.php"); +function users_init() { + + elgg_register_page_handler('friends', 'friends_page_handler'); + elgg_register_page_handler('friendsof', 'friends_page_handler'); + elgg_register_page_handler('register', 'elgg_user_account_page_handler'); + elgg_register_page_handler('forgotpassword', 'elgg_user_account_page_handler'); + elgg_register_page_handler('resetpassword', 'elgg_user_account_page_handler'); + elgg_register_page_handler('login', 'elgg_user_account_page_handler'); + elgg_register_page_handler('avatar', 'elgg_avatar_page_handler'); + elgg_register_page_handler('profile', 'elgg_profile_page_handler'); + elgg_register_page_handler('collections', 'collections_page_handler'); + + elgg_register_plugin_hook_handler('register', 'menu:user_hover', 'elgg_user_hover_menu'); + + elgg_register_action('register', '', 'public'); + elgg_register_action('useradd', '', 'admin'); + elgg_register_action('friends/add'); + elgg_register_action('friends/remove'); + elgg_register_action('avatar/upload'); + elgg_register_action('avatar/crop'); + elgg_register_action('avatar/remove'); + elgg_register_action('profile/edit'); + + elgg_register_action('friends/collections/add'); + elgg_register_action('friends/collections/delete'); + elgg_register_action('friends/collections/edit'); + + elgg_register_plugin_hook_handler('entity:icon:url', 'user', 'user_avatar_hook'); + + elgg_register_action('user/passwordreset', '', 'public'); + elgg_register_action('user/requestnewpassword', '', 'public'); + + elgg_register_widget_type('friends', elgg_echo('friends'), elgg_echo('friends:widget:description')); + + // Register the user type + elgg_register_entity_type('user', ''); + + elgg_register_plugin_hook_handler('register', 'menu:entity', 'elgg_users_setup_entity_menu', 501); + + elgg_register_event_handler('create', 'user', 'user_create_hook_add_site_relationship'); } /** * Runs unit tests for ElggObject * - * @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 users_test($hook, $type, $value, $params) { global $CONFIG; @@ -1624,5 +1606,6 @@ function users_test($hook, $type, $value, $params) { } elgg_register_event_handler('init', 'system', 'users_init', 0); +elgg_register_event_handler('init', 'system', 'elgg_profile_fields_setup', 10000); // Ensure this runs after other plugins elgg_register_event_handler('pagesetup', 'system', 'users_pagesetup', 0); -elgg_register_plugin_hook_handler('unit_test', 'system', 'users_test');
\ No newline at end of file +elgg_register_plugin_hook_handler('unit_test', 'system', 'users_test'); diff --git a/engine/lib/usersettings.php b/engine/lib/usersettings.php deleted file mode 100644 index 1696dd1cd..000000000 --- a/engine/lib/usersettings.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php -/** - * Elgg user settings functions. - * Functions for adding and manipulating options on the user settings panel. - * - * @package Elgg.Core - * @subpackage Settings.User - */ - -/** - * Register a user settings page with the admin panel. - * This function extends the view "usersettings/main" with the provided view. - * This view should provide a description and either a control or a link to. - * - * Usage: - * - To add a control to the main admin panel then extend usersettings/main - * - To add a control to a new page create a page which renders a view - * usersettings/subpage (where subpage is your new page - - * nb. some pages already exist that you can extend), extend the main view - * to point to it, and add controls to your new view. - * - * At the moment this is essentially a wrapper around elgg_extend_view(). - * - * @param string $new_settings_view The view associated with the control you're adding - * @param string $view The view to extend, by default this is 'usersettings/main'. - * @param int $priority Optional priority to govern the appearance in the list. - * - * @return bool - */ -function extend_elgg_settings_page($new_settings_view, $view = 'usersettings/main', -$priority = 500) { - - return elgg_extend_view($view, $new_settings_view, $priority); -} - -/** - * Set up the page for user settings - * - * @return void - */ -function usersettings_pagesetup() { - // Get config - global $CONFIG; - - // Menu options - if (elgg_get_context() == "settings") { - $user = get_loggedin_user(); - add_submenu_item(elgg_echo('usersettings:user:opt:linktext'), - $CONFIG->wwwroot . "pg/settings/user/{$user->username}/"); - - add_submenu_item(elgg_echo('usersettings:plugins:opt:linktext'), - $CONFIG->wwwroot . "pg/settings/plugins/{$user->username}/"); - - add_submenu_item(elgg_echo('usersettings:statistics:opt:linktext'), - $CONFIG->wwwroot . "pg/settings/statistics/{$user->username}/"); - } -} - -/** - * Page handler for user settings - * - * @param array $page Pages array - * - * @return void - */ -function usersettings_page_handler($page) { - global $CONFIG; - - $path = $CONFIG->path . "pages/settings/index.php"; - - if ($page[0]) { - switch ($page[0]) { - case 'user': - $path = $CONFIG->path . "pages/settings/user.php"; - break; - case 'statistics': - $path = $CONFIG->path . "pages/settings/statistics.php"; - break; - case 'plugins': - $path = $CONFIG->path . "pages/settings/plugins.php"; - break; - } - } - - if ($page[1]) { - set_input('username', $page[1]); - } - - require($path); -} - -/** - * Initialise the admin page. - * - * @return void - */ -function usersettings_init() { - // Page handler - register_page_handler('settings', 'usersettings_page_handler'); -} - -/// Register init function -elgg_register_event_handler('init', 'system', 'usersettings_init'); -elgg_register_event_handler('pagesetup', 'system', 'usersettings_pagesetup');
\ No newline at end of file diff --git a/engine/lib/version.php b/engine/lib/version.php deleted file mode 100644 index 4ffe85bc5..000000000 --- a/engine/lib/version.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php -/** - * Elgg version library. - * Contains code for handling versioning and upgrades. - * - * @package Elgg.Core - * @subpackage Version - */ - -/** - * Run any php upgrade scripts which are required - * - * @param int $version Version upgrading from. - * @param bool $quiet Suppress errors. Don't use this. - * - * @return bool - */ -function upgrade_code($version, $quiet = FALSE) { - global $CONFIG; - - $version = (int) $version; - - if ($handle = opendir($CONFIG->path . 'engine/lib/upgrades/')) { - $upgrades = array(); - - while ($updatefile = readdir($handle)) { - // Look for upgrades and add to upgrades list - if (!is_dir($CONFIG->path . 'engine/lib/upgrades/' . $updatefile)) { - if (preg_match('/^([0-9]{10})\.(php)$/', $updatefile, $matches)) { - $core_version = (int) $matches[1]; - if ($core_version > $version) { - $upgrades[] = $updatefile; - } - } - } - } - - // Sort and execute - asort($upgrades); - - if (sizeof($upgrades) > 0) { - foreach ($upgrades as $upgrade) { - // hide all errors. - if ($quiet) { - // hide include errors as well as any exceptions that might happen - try { - if (!@include($CONFIG->path . 'engine/lib/upgrades/' . $upgrade)) { - error_log($e->getmessage()); - } - } catch (Exception $e) { - error_log($e->getmessage()); - } - } else { - include($CONFIG->path . 'engine/lib/upgrades/' . $upgrade); - } - } - } - - return TRUE; - } - - return FALSE; -} - -/** - * Get the current version information - * - * @param bool $humanreadable Whether to return a human readable version (default: false) - * - * @return string|false Depending on success - */ -function get_version($humanreadable = false) { - global $CONFIG; - - if (isset($CONFIG->path)) { - if (include($CONFIG->path . "version.php")) { - return (!$humanreadable) ? $version : $release; - } - } - - return FALSE; -} - -/** - * Determines whether or not the database needs to be upgraded. - * - * @return true|false Depending on whether or not the db version matches the code version - */ -function version_upgrade_check() { - $dbversion = (int) datalist_get('version'); - $version = get_version(); - - if ($version > $dbversion) { - return TRUE; - } - - return FALSE; -} - -/** - * Upgrades Elgg Database and code - * - * @return bool - * - */ -function version_upgrade() { - // It's possible large upgrades could exceed the max execution time. - set_time_limit(0); - - $dbversion = (int) datalist_get('version'); - - // No version number? Oh snap...this is an upgrade from a clean installation < 1.7. - // Run all upgrades without error reporting and hope for the best. - // See http://trac.elgg.org/elgg/ticket/1432 for more. - $quiet = !$dbversion; - - // Upgrade database - if (db_upgrade($dbversion, '', $quiet)) { - system_message(elgg_echo('upgrade:db')); - } - - // Upgrade core - if (upgrade_code($dbversion, $quiet)) { - system_message(elgg_echo('upgrade:core')); - } - - // Now we trigger an event to give the option for plugins to do something - $upgrade_details = new stdClass; - $upgrade_details->from = $dbversion; - $upgrade_details->to = get_version(); - - elgg_trigger_event('upgrade', 'upgrade', $upgrade_details); - - // Update the version - datalist_set('version', get_version()); -} diff --git a/engine/lib/views.php b/engine/lib/views.php index 26d48be2f..1142461fe 100644 --- a/engine/lib/views.php +++ b/engine/lib/views.php @@ -40,7 +40,7 @@ * are called, so the init order doesn't affect views. * * @internal The file that determines the output of the view is the last - * registered by {@link set_view_location()}. + * registered by {@link elgg_set_view_location()}. * * @package Elgg.Core * @subpackage Views @@ -53,6 +53,7 @@ * @global string $CURRENT_SYSTEM_VIEWTYPE * @see elgg_set_viewtype() */ +global $CURRENT_SYSTEM_VIEWTYPE; $CURRENT_SYSTEM_VIEWTYPE = ""; /** @@ -100,12 +101,15 @@ function elgg_get_viewtype() { return $CURRENT_SYSTEM_VIEWTYPE; } - $viewtype = get_input('view', NULL); - if ($viewtype) { - return $viewtype; + $viewtype = get_input('view', '', false); + if (is_string($viewtype) && $viewtype !== '') { + // only word characters allowed. + if (!preg_match('/\W/', $viewtype)) { + return $viewtype; + } } - if (isset($CONFIG->view) && !empty($CONFIG->view)) { + if (!empty($CONFIG->view)) { return $CONFIG->view; } @@ -113,12 +117,33 @@ function elgg_get_viewtype() { } /** + * Register a view type as valid. + * + * @param string $view_type The view type to register + * @return bool + */ +function elgg_register_viewtype($view_type) { + global $CONFIG; + + if (!isset($CONFIG->view_types) || !is_array($CONFIG->view_types)) { + $CONFIG->view_types = array(); + } + + if (!in_array($view_type, $CONFIG->view_types)) { + $CONFIG->view_types[] = $view_type; + } + + return true; +} + +/** * Checks if $view_type is valid on this installation. * * @param string $view_type View type * * @return bool * @since 1.7.2 + * @access private */ function elgg_is_valid_view_type($view_type) { global $CONFIG; @@ -174,6 +199,37 @@ function elgg_does_viewtype_fallback($viewtype) { return FALSE; } +/** + * Register a view to be available for ajax calls + * + * @param string $view The view name + * @return void + * @since 1.8.3 + */ +function elgg_register_ajax_view($view) { + global $CONFIG; + + if (!isset($CONFIG->allowed_ajax_views)) { + $CONFIG->allowed_ajax_views = array(); + } + + $CONFIG->allowed_ajax_views[$view] = true; +} + +/** + * Unregister a view for ajax calls + * + * @param string $view The view name + * @return void + * @since 1.8.3 + */ +function elgg_unregister_ajax_view($view) { + global $CONFIG; + + if (isset($CONFIG->allowed_ajax_views[$view])) { + unset($CONFIG->allowed_ajax_views[$view]); + } +} /** * Returns the file location for a view. @@ -202,8 +258,6 @@ function elgg_get_view_location($view, $viewtype = '') { } else { return $CONFIG->views->locations[$viewtype][$view]; } - - return false; } /** @@ -222,7 +276,7 @@ function elgg_get_view_location($view, $viewtype = '') { * * @return void */ -function set_view_location($view, $location, $viewtype = '') { +function elgg_set_view_location($view, $location, $viewtype = '') { global $CONFIG; if (empty($viewtype)) { @@ -247,7 +301,7 @@ function set_view_location($view, $location, $viewtype = '') { /** * Returns whether the specified view exists * - * @note If $recurse is strue, also checks if a view exists only as an extension. + * @note If $recurse is true, also checks if a view exists only as an extension. * * @param string $view The view name * @param string $viewtype If set, forces the viewtype @@ -273,7 +327,7 @@ function elgg_view_exists($view, $viewtype = '', $recurse = true) { $location = $CONFIG->views->locations[$viewtype][$view]; } - if (file_exists($location . "{$viewtype}/{$view}.php")) { + if (file_exists("{$location}{$viewtype}/{$view}.php")) { return true; } @@ -288,6 +342,11 @@ function elgg_view_exists($view, $viewtype = '', $recurse = true) { } } + // Now check if the default view exists if the view is registered as a fallback + if ($viewtype != 'default' && elgg_does_viewtype_fallback($viewtype)) { + return elgg_view_exists($view, 'default'); + } + return false; } @@ -297,11 +356,12 @@ function elgg_view_exists($view, $viewtype = '', $recurse = true) { * Views are rendered by a template handler and returned as strings. * * Views are called with a special $vars variable set, - * which includes any variables passed as the second parameter, - * as well as some defaults: - * - All $_SESSION vars merged to $vars array. - * - $vars['config'] The $CONFIG global. (Use {@link get_config()} instead). - * - $vars['url'] The site URL. + * which includes any variables passed as the second parameter. + * For backward compatbility, the following variables are also set but we + * recommend that you do not use them: + * - $vars['config'] The $CONFIG global. (Use {@link elgg_get_config()} instead). + * - $vars['url'] The site URL. (use {@link elgg_get_site_url()} instead). + * - $vars['user'] The logged in user. (use {@link elgg_get_logged_in_user_entity()} instead). * * Custom template handlers can be set with {@link set_template_handler()}. * @@ -309,14 +369,14 @@ function elgg_view_exists($view, $viewtype = '', $recurse = true) { * view, $view_name plugin hook. * * @warning Any variables in $_SESSION will override passed vars - * upon name collision. See {@trac #2124}. + * upon name collision. See https://github.com/Elgg/Elgg/issues/2124 * * @param string $view The name and location of the view to use * @param array $vars Variables to pass to the view. * @param boolean $bypass If set to true, elgg_view will bypass any specified * alternative template handler; by default, it will * hand off to this if requested (see set_template_handler) - * @param boolean $debug If set to true, the viewer will complain if it can't find a view + * @param boolean $ignored This argument is ignored and will be removed eventually * @param string $viewtype If set, forces the viewtype for the elgg_view call to be * this value (default: standard detection) * @@ -324,69 +384,94 @@ function elgg_view_exists($view, $viewtype = '', $recurse = true) { * @see set_template_handler() * @example views/elgg_view.php * @link http://docs.elgg.org/View - * @todo $debug isn't used. - * @todo $usercache is redundant. */ -function elgg_view($view, $vars = array(), $bypass = false, $debug = false, $viewtype = '') { +function elgg_view($view, $vars = array(), $bypass = false, $ignored = false, $viewtype = '') { global $CONFIG; - static $usercache; - - $view = (string)$view; + if (!is_string($view) || !is_string($viewtype)) { + elgg_log("View and Viewtype in views must be a strings: $view", 'NOTICE'); + return ''; + } // basic checking for bad paths if (strpos($view, '..') !== false) { - return false; + return ''; + } + + if (!is_array($vars)) { + elgg_log("Vars in views must be an array: $view", 'ERROR'); + $vars = array(); + } + + // Get the current viewtype + if ($viewtype === '') { + $viewtype = elgg_get_viewtype(); + } elseif (preg_match('/\W/', $viewtype)) { + // Viewtypes can only be alphanumeric + return ''; } $view_orig = $view; // Trigger the pagesetup event - if (!isset($CONFIG->pagesetupdone)) { - elgg_trigger_event('pagesetup', 'system'); + if (!isset($CONFIG->pagesetupdone) && $CONFIG->boot_complete) { $CONFIG->pagesetupdone = true; + elgg_trigger_event('pagesetup', 'system'); } - if (!is_array($usercache)) { - $usercache = array(); + // @warning - plugin authors: do not expect user, config, and url to be + // set by elgg_view() in the future. Instead, use elgg_get_logged_in_user_entity(), + // elgg_get_config(), and elgg_get_site_url() in your views. + if (!isset($vars['user'])) { + $vars['user'] = elgg_get_logged_in_user_entity(); } - - if (!is_array($vars)) { - elgg_log('Vars in views must be an array!', 'ERROR'); - $vars = array(); + if (!isset($vars['config'])) { + $vars['config'] = $CONFIG; } - - if (empty($vars)) { - $vars = array(); + if (!isset($vars['url'])) { + $vars['url'] = elgg_get_site_url(); } - $vars['user'] = get_loggedin_user(); - - $vars['config'] = array(); + // full_view is the new preferred key for full view on entities @see elgg_view_entity() + // check if full_view is set because that means we've already rewritten it and this is + // coming from another view passing $vars directly. + if (isset($vars['full']) && !isset($vars['full_view'])) { + elgg_deprecated_notice("Use \$vars['full_view'] instead of \$vars['full']", 1.8, 2); + $vars['full_view'] = $vars['full']; + } + if (isset($vars['full_view'])) { + $vars['full'] = $vars['full_view']; + } - if (!empty($CONFIG)) { - $vars['config'] = $CONFIG; + // internalname => name (1.8) + if (isset($vars['internalname']) && !isset($vars['__ignoreInternalname']) && !isset($vars['name'])) { + elgg_deprecated_notice('You should pass $vars[\'name\'] now instead of $vars[\'internalname\']', 1.8, 2); + $vars['name'] = $vars['internalname']; + } elseif (isset($vars['name'])) { + if (!isset($vars['internalname'])) { + $vars['__ignoreInternalname'] = ''; + } + $vars['internalname'] = $vars['name']; } - $vars['url'] = elgg_get_site_url(); + // internalid => id (1.8) + if (isset($vars['internalid']) && !isset($vars['__ignoreInternalid']) && !isset($vars['name'])) { + elgg_deprecated_notice('You should pass $vars[\'id\'] now instead of $vars[\'internalid\']', 1.8, 2); + $vars['id'] = $vars['internalid']; + } elseif (isset($vars['id'])) { + if (!isset($vars['internalid'])) { + $vars['__ignoreInternalid'] = ''; + } + $vars['internalid'] = $vars['id']; + } // If it's been requested, pass off to a template handler instead if ($bypass == false && isset($CONFIG->template_handler) && !empty($CONFIG->template_handler)) { $template_handler = $CONFIG->template_handler; if (is_callable($template_handler)) { - return $template_handler($view, $vars); + return call_user_func($template_handler, $view, $vars); } } - // Get the current viewtype - if (empty($viewtype)) { - $viewtype = elgg_get_viewtype(); - } - - // Viewtypes can only be alphanumeric - if (preg_match('[\W]', $viewtype)) { - return ''; - } - // Set up any extensions to the requested view if (isset($CONFIG->views->extensions[$view])) { $viewlist = $CONFIG->views->extensions[$view]; @@ -398,19 +483,21 @@ function elgg_view($view, $vars = array(), $bypass = false, $debug = false, $vie ob_start(); foreach ($viewlist as $priority => $view) { + $view_location = elgg_get_view_location($view, $viewtype); $view_file = "$view_location$viewtype/$view.php"; - $default_location = elgg_get_view_location($view, 'default'); - $default_view_file = "{$default_location}default/$view.php"; - // try to include view if (!file_exists($view_file) || !include($view_file)) { // requested view does not exist $error = "$viewtype/$view view does not exist."; // attempt to load default view - if ($viewtype != 'default' && elgg_does_viewtype_fallback($viewtype)) { + if ($viewtype !== 'default' && elgg_does_viewtype_fallback($viewtype)) { + + $default_location = elgg_get_view_location($view, 'default'); + $default_view_file = "{$default_location}default/$view.php"; + if (file_exists($default_view_file) && include($default_view_file)) { // default view found $error .= " Using default/$view instead."; @@ -435,7 +522,7 @@ function elgg_view($view, $vars = array(), $bypass = false, $debug = false, $vie // backward compatibility with less granular hook will be gone in 2.0 $content_tmp = elgg_trigger_plugin_hook('display', 'view', $params, $content); - if ($content_tmp != $content) { + if ($content_tmp !== $content) { $content = $content_tmp; elgg_deprecated_notice('The display:view plugin hook is deprecated by view:view_name', 1.8); } @@ -461,33 +548,32 @@ function elgg_view($view, $vars = array(), $bypass = false, $debug = false, $vie * @param string $view_extension This view is added to $view * @param int $priority The priority, from 0 to 1000, * to add at (lowest numbers displayed first) - * @param string $viewtype Not used * * @return void * @since 1.7.0 - * @link http://docs.elgg.org/Views/Ejxtend + * @link http://docs.elgg.org/Views/Extend * @example views/extend.php */ -function elgg_extend_view($view, $view_extension, $priority = 501, $viewtype = '') { +function elgg_extend_view($view, $view_extension, $priority = 501) { global $CONFIG; if (!isset($CONFIG->views)) { - $CONFIG->views = new stdClass; - } - - if (!isset($CONFIG->views->extensions)) { - $CONFIG->views->extensions = array(); - } - - if (!isset($CONFIG->views->extensions[$view])) { - $CONFIG->views->extensions[$view][500] = "{$view}"; + $CONFIG->views = (object) array( + 'extensions' => array(), + ); + $CONFIG->views->extensions[$view][500] = (string)$view; + } else { + if (!isset($CONFIG->views->extensions[$view])) { + $CONFIG->views->extensions[$view][500] = (string)$view; + } } + // raise priority until it doesn't match one already registered while (isset($CONFIG->views->extensions[$view][$priority])) { $priority++; } - $CONFIG->views->extensions[$view][$priority] = "{$view_extension}"; + $CONFIG->views->extensions[$view][$priority] = (string)$view_extension; ksort($CONFIG->views->extensions[$view]); } @@ -503,14 +589,6 @@ function elgg_extend_view($view, $view_extension, $priority = 501, $viewtype = ' function elgg_unextend_view($view, $view_extension) { global $CONFIG; - if (!isset($CONFIG->views)) { - return FALSE; - } - - if (!isset($CONFIG->views->extensions)) { - return FALSE; - } - if (!isset($CONFIG->views->extensions[$view])) { return FALSE; } @@ -526,24 +604,6 @@ function elgg_unextend_view($view, $view_extension) { } /** - * Extend a view - * - * @deprecated 1.7. Use elgg_extend_view(). - * - * @param string $view The view to extend. - * @param string $view_name This view is added to $view - * @param int $priority The priority, from 0 to 1000, - * to add at (lowest numbers displayed first) - * @param string $viewtype Not used - * - * @return void - */ -function extend_view($view, $view_name, $priority = 501, $viewtype = '') { - elgg_deprecated_notice('extend_view() was deprecated by elgg_extend_view()!', 1.7); - elgg_extend_view($view, $view_name, $priority, $viewtype); -} - -/** * Assembles and outputs a full page. * * A "page" in Elgg is determined by the current view type and @@ -552,7 +612,7 @@ function extend_view($view, $view_name, $priority = 501, $viewtype = '') { * * @param string $title Title * @param string $body Body - * @param string $page_shell Optional page shell to use. See page_shells view directory + * @param string $page_shell Optional page shell to use. See page/shells view directory * @param array $vars Optional vars array to pass to the page * shell. Automatically adds title, body, and sysmessages * @@ -561,6 +621,7 @@ function extend_view($view, $view_name, $priority = 501, $viewtype = '') { */ function elgg_view_page($title, $body, $page_shell = 'default', $vars = array()) { + $messages = null; if (count_messages()) { // get messages - try for errors first $messages = system_messages(NULL, "error"); @@ -577,8 +638,15 @@ function elgg_view_page($title, $body, $page_shell = 'default', $vars = array()) $vars['body'] = $body; $vars['sysmessages'] = $messages; - // Draw the page - $output = elgg_view("page_shells/$page_shell", $vars); + $vars = elgg_trigger_plugin_hook('output:before', 'page', null, $vars); + + // check for deprecated view + if ($page_shell == 'default' && elgg_view_exists('pageshells/pageshell')) { + elgg_deprecated_notice("pageshells/pageshell is deprecated by page/$page_shell", 1.8); + $output = elgg_view('pageshells/pageshell', $vars); + } else { + $output = elgg_view("page/$page_shell", $vars); + } $vars['page_shell'] = $page_shell; @@ -587,44 +655,40 @@ function elgg_view_page($title, $body, $page_shell = 'default', $vars = array()) } /** - * @deprecated 1.8 Use elgg_view_page() - */ -function page_draw($title, $body, $sidebar = "") { - elgg_deprecated_notice("page_draw() was deprecated in favor of elgg_view_page() in 1.8.", 1.8); - - $vars = array( - 'sidebar' => $sidebar - ); - echo elgg_view_page($title, $body, 'default', $vars); -} - -/** * Displays a layout with optional parameters. * * Layouts provide consistent organization of pages and other blocks of content. * There are a few default layouts in core: - * - administration A special layout for the admin area. + * - admin A special layout for the admin area. * - one_column A single content column. - * - one_column_with_sidebar A content column with sidebar. + * - one_sidebar A content column with sidebar. + * - two_sidebar A content column with two sidebars. * - widgets A widget canvas. * - * The layout views take the form layout/shells/$layout_name - * See the individual layouts for what options are supported. The two most + * The layout views take the form page/layouts/$layout_name + * See the individual layouts for what options are supported. The three most * common layouts have these parameters: * one_column * content => string - * one_column_with_sidebar + * one_sidebar + * content => string + * sidebar => string (optional) + * content * content => string * sidebar => string (optional) + * buttons => string (override the default add button) + * title => string (override the default title) + * filter_context => string (selected content filter) + * See the content layout view for more parameters * - * @param string $layout The name of the view in layout/shells/. - * @param array $vars Associative array of parameters for the layout view + * @param string $layout_name The name of the view in page/layouts/. + * @param array $vars Associative array of parameters for the layout view * * @return string The layout */ function elgg_view_layout($layout_name, $vars = array()) { - if (is_string($vars)) { + if (is_string($vars) || $vars === null) { elgg_deprecated_notice("The use of unlimited optional string arguments in elgg_view_layout() was deprecated in favor of an options array", 1.8); $arg = 1; $param_array = array(); @@ -636,10 +700,83 @@ function elgg_view_layout($layout_name, $vars = array()) { $param_array = $vars; } - if (elgg_view_exists("layout/shells/$layout_name")) { - return elgg_view("layout/shells/$layout_name", $param_array); + $params = elgg_trigger_plugin_hook('output:before', 'layout', null, $param_array); + + // check deprecated location + if (elgg_view_exists("canvas/layouts/$layout_name")) { + elgg_deprecated_notice("canvas/layouts/$layout_name is deprecated by page/layouts/$layout_name", 1.8); + $output = elgg_view("canvas/layouts/$layout_name", $params); + } elseif (elgg_view_exists("page/layouts/$layout_name")) { + $output = elgg_view("page/layouts/$layout_name", $params); } else { - return elgg_view("layout/shells/default", $param_array); + $output = elgg_view("page/layouts/default", $params); + } + + return elgg_trigger_plugin_hook('output:after', 'layout', $params, $output); +} + +/** + * Render a menu + * + * @see elgg_register_menu_item() for documentation on adding menu items and + * navigation.php for information on the different menus available. + * + * This function triggers a 'register', 'menu:<menu name>' plugin hook that enables + * plugins to add menu items just before a menu is rendered. This is used by + * dynamic menus (menus that change based on some input such as the user hover + * menu). Using elgg_register_menu_item() in response to the hook can cause + * incorrect links to show up. See the blog plugin's blog_owner_block_menu() + * for an example of using this plugin hook. + * + * An additional hook is the 'prepare', 'menu:<menu name>' which enables plugins + * to modify the structure of the menu (sort it, remove items, set variables on + * the menu items). + * + * elgg_view_menu() uses views in navigation/menu + * + * @param string $menu_name The name of the menu + * @param array $vars An associative array of display options for the menu. + * Options include: + * sort_by => string or php callback + * string options: 'name', 'priority', 'title' (default), + * 'register' (registration order) or a + * php callback (a compare function for usort) + * handler: string the page handler to build action URLs + * entity: ElggEntity to use to build action URLs + * class: string the class for the entire menu. + * show_section_headers: bool show headers before menu sections. + * + * @return string + * @since 1.8.0 + */ +function elgg_view_menu($menu_name, array $vars = array()) { + global $CONFIG; + + $vars['name'] = $menu_name; + + $sort_by = elgg_extract('sort_by', $vars, 'text'); + + if (isset($CONFIG->menus[$menu_name])) { + $menu = $CONFIG->menus[$menu_name]; + } else { + $menu = array(); + } + + // Give plugins a chance to add menu items just before creation. + // This supports dynamic menus (example: user_hover). + $menu = elgg_trigger_plugin_hook('register', "menu:$menu_name", $vars, $menu); + + $builder = new ElggMenuBuilder($menu); + $vars['menu'] = $builder->getMenu($sort_by); + $vars['selected_item'] = $builder->getSelected(); + + // Let plugins modify the menu + $vars['menu'] = elgg_trigger_plugin_hook('prepare', "menu:$menu_name", $vars, $vars['menu']); + + if (elgg_view_exists("navigation/menu/$menu_name")) { + return elgg_view("navigation/menu/$menu_name", $vars); + } else { + return elgg_view("navigation/menu/default", $vars); } } @@ -653,14 +790,17 @@ function elgg_view_layout($layout_name, $vars = array()) { * * The entity view is called with the following in $vars: * - ElggEntity 'entity' The entity being viewed - * - bool 'full' Whether to show a full or condensed view. + * + * Other common view $vars paramters: + * - bool 'full_view' Whether to show a full or condensed view. * * @tip This function can automatically appends annotations to entities if in full - * view and a handler is registered for the entity:annotate. See {@trac 964} and + * view and a handler is registered for the entity:annotate. See https://github.com/Elgg/Elgg/issues/964 and * {@link elgg_view_entity_annotations()}. * * @param ElggEntity $entity The entity to display - * @param boolean $full Passed to entity view to decide how much information to show. + * @param array $vars Array of variables to pass to the entity view. + * In Elgg 1.7 and earlier it was the boolean $full_view * @param boolean $bypass If false, will not pass to a custom template handler. * {@see set_template_handler()} * @param boolean $debug Complain if views are missing @@ -670,26 +810,36 @@ function elgg_view_layout($layout_name, $vars = array()) { * @link http://docs.elgg.org/Entities * @todo The annotation hook might be better as a generic plugin hook to append content. */ -function elgg_view_entity(ElggEntity $entity, $full = false, $bypass = true, $debug = false) { - global $autofeed; - $autofeed = true; +function elgg_view_entity(ElggEntity $entity, $vars = array(), $bypass = true, $debug = false) { // No point continuing if entity is null - if (!$entity) { - return ''; + if (!$entity || !($entity instanceof ElggEntity)) { + return false; } - if (!($entity instanceof ElggEntity)) { - return false; + global $autofeed; + $autofeed = true; + + $defaults = array( + 'full_view' => false, + ); + + if (is_array($vars)) { + $vars = array_merge($defaults, $vars); + } else { + elgg_deprecated_notice("Update your use of elgg_view_entity()", 1.8); + $vars = array( + 'full_view' => $vars, + ); } + $vars['entity'] = $entity; + + // if this entity has a view defined, use it $view = $entity->view; if (is_string($view)) { - return elgg_view($view, - array('entity' => $entity, 'full' => $full), - $bypass, - $debug); + return elgg_view($view, $vars, $bypass, $debug); } $entity_type = $entity->getType(); @@ -700,21 +850,16 @@ function elgg_view_entity(ElggEntity $entity, $full = false, $bypass = true, $de } $contents = ''; - if (elgg_view_exists("{$entity_type}/{$subtype}")) { - $contents = elgg_view("{$entity_type}/{$subtype}", array( - 'entity' => $entity, - 'full' => $full - ), $bypass, $debug); + if (elgg_view_exists("$entity_type/$subtype")) { + $contents = elgg_view("$entity_type/$subtype", $vars, $bypass, $debug); } if (empty($contents)) { - $contents = elgg_view("{$entity_type}/default", array( - 'entity' => $entity, - 'full' => $full - ), $bypass, $debug); + $contents = elgg_view("$entity_type/default", $vars, $bypass, $debug); } + // Marcus Povey 20090616 : Speculative and low impact approach for fixing #964 - if ($full) { - $annotations = elgg_view_entity_annotations($entity, $full); + if ($vars['full_view']) { + $annotations = elgg_view_entity_annotations($entity, $vars['full_view']); if ($annotations) { $contents .= $annotations; @@ -724,6 +869,52 @@ function elgg_view_entity(ElggEntity $entity, $full = false, $bypass = true, $de } /** + * View the icon of an entity + * + * Entity views are determined by having a view named after the entity $type/$subtype. + * Entities that do not have a defined icon/$type/$subtype view will fall back to using + * the icon/$type/default view. + * + * @param ElggEntity $entity The entity to display + * @param string $size The size: tiny, small, medium, large + * @param array $vars An array of variables to pass to the view. Some possible + * variables are img_class and link_class. See the + * specific icon view for more parameters. + * + * @return string HTML to display or false + */ +function elgg_view_entity_icon(ElggEntity $entity, $size = 'medium', $vars = array()) { + + // No point continuing if entity is null + if (!$entity || !($entity instanceof ElggEntity)) { + return false; + } + + $vars['entity'] = $entity; + $vars['size'] = $size; + + $entity_type = $entity->getType(); + + $subtype = $entity->getSubtype(); + if (empty($subtype)) { + $subtype = 'default'; + } + + $contents = ''; + if (elgg_view_exists("icon/$entity_type/$subtype")) { + $contents = elgg_view("icon/$entity_type/$subtype", $vars); + } + if (empty($contents)) { + $contents = elgg_view("icon/$entity_type/default", $vars); + } + if (empty($contents)) { + $contents = elgg_view("icon/default", $vars); + } + + return $contents; +} + +/** * Returns a string of a rendered annotation. * * Annotation views are expected to be in annotation/$annotation_name. @@ -736,40 +927,40 @@ function elgg_view_entity(ElggEntity $entity, $full = false, $bypass = true, $de * - ElggEntity 'annotation' The annotation being viewed. * * @param ElggAnnotation $annotation The annotation to display - * @param bool $full Display the full view + * @param array $vars Variable array for view. * @param bool $bypass If false, will not pass to a custom * template handler. {@see set_template_handler()} * @param bool $debug Complain if views are missing * - * @return string HTML (etc) to display + * @return string/false Rendered annotation */ -function elgg_view_annotation(ElggAnnotation $annotation, $full = true, $bypass = true, $debug = false) { +function elgg_view_annotation(ElggAnnotation $annotation, array $vars = array(), $bypass = true, $debug = false) { global $autofeed; $autofeed = true; - $params = array( - 'annotation' => $annotation, - 'full' => $full, + $defaults = array( + 'full_view' => true, ); + $vars = array_merge($defaults, $vars); + $vars['annotation'] = $annotation; + + // @todo setting the view on an annotation is not advertised anywhere + // do we want to keep this? $view = $annotation->view; if (is_string($view)) { - return elgg_view($view, $params, $bypass, $debug); + return elgg_view($view, $vars, $bypass, $debug); } $name = $annotation->name; - $intname = (int) $name; - if ("{$intname}" == "{$name}") { - $name = get_metastring($intname); - } if (empty($name)) { - return ""; + return false; } - if (elgg_view_exists("annotation/{$name}")) { - return elgg_view("annotation/{$name}", $params, $bypass, $debug); + if (elgg_view_exists("annotation/$name")) { + return elgg_view("annotation/$name", $vars, $bypass, $debug); } else { - return elgg_view("annotation/default", $params, $bypass, $debug); + return elgg_view("annotation/default", $vars, $bypass, $debug); } } @@ -783,67 +974,110 @@ function elgg_view_annotation(ElggAnnotation $annotation, $full = true, $bypass * @see elgg_list_entities_from_relationships() * @see elgg_list_entities_from_annotations() * - * @param array $entities List of entities - * @param int $count The total number of entities across all pages - * @param int $offset The current indexing offset - * @param int $limit The number of entities to display per page - * @param bool $fullview Whether or not to display the full view (default: true) - * @param bool $listtypetoggle Whether or not to allow users to toggle to gallery view - * @param bool $pagination Whether pagination is offered. - * - * @return string The list of entities + * @param array $entities Array of entities + * @param array $vars Display variables + * 'count' The total number of entities across all pages + * 'offset' The current indexing offset + * 'limit' The number of entities to display per page + * 'full_view' Display the full view of the entities? + * 'list_class' CSS class applied to the list + * 'item_class' CSS class applied to the list items + * 'pagination' Display pagination? + * 'list_type' List type: 'list' (default), 'gallery' + * 'list_type_toggle' Display the list type toggle? + * + * @return string The rendered list of entities * @access private */ -function elgg_view_entity_list($entities, $count, $offset, $limit, $full_view = true, +function elgg_view_entity_list($entities, $vars = array(), $offset = 0, $limit = 10, $full_view = true, $list_type_toggle = true, $pagination = true) { - $count = (int) $count; - $limit = (int) $limit; - - // do not require views to explicitly pass in the offset - if (!$offset = (int) $offset) { - $offset = sanitise_int(get_input('offset', 0)); - } + if (!$vars["limit"] && !$vars["offset"]) { + // no need for pagination if listing is unlimited
+ $vars["pagination"] = false;
+ }
+ + if (!is_int($offset)) { + $offset = (int)get_input('offset', 0); + } + + // list type can be passed as request parameter + $list_type = get_input('list_type', 'list'); + if (get_input('listtype')) { + elgg_deprecated_notice("'listtype' has been deprecated by 'list_type' for lists", 1.8); + $list_type = get_input('listtype'); + } + + if (is_array($vars)) { + // new function + $defaults = array( + 'items' => $entities, + 'list_class' => 'elgg-list-entity', + 'full_view' => true, + 'pagination' => true, + 'list_type' => $list_type, + 'list_type_toggle' => false, + 'offset' => $offset, + ); - $context = elgg_get_context(); + $vars = array_merge($defaults, $vars); - $html = elgg_view('entities/list', array( - 'entities' => $entities, - 'count' => $count, - 'offset' => $offset, - 'limit' => $limit, - 'baseurl' => $_SERVER['REQUEST_URI'], - 'fullview' => $full_view, - 'context' => $context, - 'listtypetoggle' => $list_type_toggle, - 'listtype' => get_input('listtype', 'list'), - 'pagination' => $pagination - )); + } else { + // old function parameters + elgg_deprecated_notice("Please update your use of elgg_view_entity_list()", 1.8); + + $vars = array( + 'items' => $entities, + 'count' => (int) $vars, // the old count parameter + 'offset' => $offset, + 'limit' => (int) $limit, + 'full_view' => $full_view, + 'pagination' => $pagination, + 'list_type' => $list_type, + 'list_type_toggle' => $list_type_toggle, + 'list_class' => 'elgg-list-entity', + ); + } - return $html; + if ($vars['list_type'] != 'list') { + return elgg_view('page/components/gallery', $vars); + } else { + return elgg_view('page/components/list', $vars); + } } /** * Returns a rendered list of annotations, plus pagination. This function * should be called by wrapper functions. * - * @param array $annotations List of annotations - * @param int $count The total number of annotations across all pages - * @param int $offset The current indexing offset - * @param int $limit The number of annotations to display per page + * @param array $annotations Array of annotations + * @param array $vars Display variables + * 'count' The total number of annotations across all pages + * 'offset' The current indexing offset + * 'limit' The number of annotations to display per page + * 'full_view' Display the full view of the annotation? + * 'list_class' CSS Class applied to the list + * 'offset_key' The url parameter key used for offset * * @return string The list of annotations * @access private */ -function elgg_view_annotation_list($annotations, $count, $offset, $limit) { - $params = array( - 'annotations' => $annotations, - 'count' => (int) $count, - 'offset' => (int) $offset, - 'limit' => (int) $limit, +function elgg_view_annotation_list($annotations, array $vars = array()) { + $defaults = array( + 'items' => $annotations, + 'list_class' => 'elgg-list-annotation elgg-annotation-list', // @todo remove elgg-annotation-list in Elgg 1.9 + 'full_view' => true, + 'offset_key' => 'annoff', ); + + $vars = array_merge($defaults, $vars); + + if (!$vars["limit"] && !$vars["offset"]) {
+ // no need for pagination if listing is unlimited
+ $vars["pagination"] = false;
+ } - return elgg_view('annotation/list', $params); + return elgg_view('page/components/list', $vars); } /** @@ -854,17 +1088,13 @@ function elgg_view_annotation_list($annotations, $count, $offset, $limit) { * * This is called automatically by the framework from {@link elgg_view_entity()} * - * @param ElggEntity $entity Entity - * @param bool $full Full view? + * @param ElggEntity $entity Entity + * @param bool $full_view Display full view? * * @return mixed string or false on failure * @todo Change the hook name. */ -function elgg_view_entity_annotations(ElggEntity $entity, $full = true) { - if (!$entity) { - return false; - } - +function elgg_view_entity_annotations(ElggEntity $entity, $full_view = true) { if (!($entity instanceof ElggEntity)) { return false; } @@ -874,7 +1104,7 @@ function elgg_view_entity_annotations(ElggEntity $entity, $full = true) { $annotations = elgg_trigger_plugin_hook('entity:annotate', $entity_type, array( 'entity' => $entity, - 'full' => $full, + 'full_view' => $full_view, ) ); @@ -882,23 +1112,24 @@ function elgg_view_entity_annotations(ElggEntity $entity, $full = true) { } /** - * Returns a rendered title. + * Renders a title. * - * This is a shortcut for {@elgg_view page_elements/title}. + * This is a shortcut for {@elgg_view page/elements/title}. * - * @param string $title The page title - * @param string $submenu Should a submenu be displayed? (default false, use not recommended and deprecated) + * @param string $title The page title + * @param array $vars View variables (was submenu be displayed? (deprecated)) * * @return string The HTML (etc) */ -function elgg_view_title($title, $submenu = false) { - if ($submenu !== false) { +function elgg_view_title($title, $vars = array()) { + if (!is_array($vars)) { elgg_deprecated_notice('setting $submenu in elgg_view_title() is deprecated', 1.8); + $vars = array('submenu' => $vars); } - $title = elgg_view('page_elements/title', array('title' => $title, 'submenu' => $submenu)); + $vars['title'] = $title; - return $title; + return elgg_view('page/elements/title', $vars); } /** @@ -921,74 +1152,115 @@ function elgg_view_friendly_time($time) { * * @tip Plugins can override the output by registering a handler * for the comments, $entity_type hook. The handler is responsible - * for formatting the comments and add comment form. + * for formatting the comments and the add comment form. * * @param ElggEntity $entity The entity to view comments of - * @param bool $add_comment Include a form to add comments + * @param bool $add_comment Include a form to add comments? + * @param array $vars Variables to pass to comment view * - * @return string|false The HTML (etc) for the comments, or false on failure + * @return string|false Rendered comments or false on failure * @link http://docs.elgg.org/Entities/Comments * @link http://docs.elgg.org/Annotations/Comments */ -function elgg_view_comments($entity, $add_comment = true) { +function elgg_view_comments($entity, $add_comment = true, array $vars = array()) { if (!($entity instanceof ElggEntity)) { return false; } - $comments = elgg_trigger_plugin_hook('comments', $entity->getType(), array('entity' => $entity), false); - if ($comemnts) { - return $comments; - } else { - $params = array( - 'entity' => $entity, - 'show_add_form' => $add_comment, - ); - $comments = elgg_view('comments/list', $params); - /* - $comments = list_annotations($entity->getGUID(), 'generic_comment'); + $vars['entity'] = $entity; + $vars['show_add_form'] = $add_comment; + $vars['class'] = elgg_extract('class', $vars, "{$entity->getSubtype()}-comments"); - //display the new comment form if required - if ($add_comment) { - $comments .= elgg_view('comments/forms/edit', array('entity' => $entity)); - } -*/ - return $comments; + $output = elgg_trigger_plugin_hook('comments', $entity->getType(), $vars, false); + if ($output) { + return $output; + } else { + return elgg_view('page/elements/comments', $vars); } } /** - * Wrapper function for the media display pattern. + * Wrapper function for the image block display pattern. * * Fixed width media on the side (image, icon, flash, etc.). * Descriptive content filling the rest of the column. * - * This is a shortcut for {@elgg_view layout/objects/media}. + * This is a shortcut for {@elgg_view page/components/image_block}. * - * @param string $icon The icon and other information - * @param string $body Description content - * @param string $vars Additional parameters for the view + * @param string $image The icon and other information + * @param string $body Description content + * @param array $vars Additional parameters for the view * * @return string * @since 1.8.0 */ -function elgg_view_media($icon, $body, $vars = array()) { - $vars['icon'] = $icon; +function elgg_view_image_block($image, $body, $vars = array()) { + $vars['image'] = $image; $vars['body'] = $body; - return elgg_view('layout/objects/media', $vars); + return elgg_view('page/components/image_block', $vars); } /** - * Wrapper function to display search listings. + * Wrapper function for the module display pattern. + * + * Box with header, body, footer + * + * This is a shortcut for {@elgg_view page/components/module}. * - * @param string $icon The icon for the listing - * @param string $info Any information that needs to be displayed. + * @param string $type The type of module (main, info, popup, aside, etc.) + * @param string $title A title to put in the header + * @param string $body Content of the module + * @param array $vars Additional parameters for the module * - * @return string The HTML (etc) representing the listing - * @deprecated 1.8 use elgg_view_media() + * @return string + * @since 1.8.0 */ -function elgg_view_listing($icon, $info) { - elgg_deprecated_notice('elgg_view_listing deprecated by elgg_view_media', 1.8); - return elgg_view('entities/entity_listing', array('icon' => $icon, 'info' => $info)); +function elgg_view_module($type, $title, $body, array $vars = array()) { + $vars['class'] = elgg_extract('class', $vars, '') . " elgg-module-$type"; + $vars['title'] = $title; + $vars['body'] = $body; + return elgg_view('page/components/module', $vars); +} + +/** + * Renders a human-readable representation of a river item + * + * @param ElggRiverItem $item A river item object + * @param array $vars An array of variables for the view + * + * @return string returns empty string if could not be rendered + */ +function elgg_view_river_item($item, array $vars = array()) { + if (!($item instanceof ElggRiverItem)) { + return ''; + } + // checking default viewtype since some viewtypes do not have unique views per item (rss) + $view = $item->getView(); + if (!$view || !elgg_view_exists($view, 'default')) { + return ''; + } + + $subject = $item->getSubjectEntity(); + $object = $item->getObjectEntity(); + if (!$subject || !$object) { + // subject is disabled or subject/object deleted + return ''; + } + + // @todo this needs to be cleaned up + // Don't hide objects in closed groups that a user can see. + // see https://github.com/elgg/elgg/issues/4789 + // else { + // // hide based on object's container + // $visibility = ElggGroupItemVisibility::factory($object->container_guid); + // if ($visibility->shouldHideItems) { + // return ''; + // } + // } + + $vars['item'] = $item; + + return elgg_view('river/item', $vars); } /** @@ -998,6 +1270,9 @@ function elgg_view_listing($icon, $info) { * sets the action by default to "action/$action". Automatically wraps the forms/$action * view with a <form> tag and inserts the anti-csrf security tokens. * + * @tip This automatically appends elgg-form-action-name to the form's class. It replaces any + * slashes with dashes (blog/save becomes elgg-form-blog-save) + * * @example * <code>echo elgg_view_form('login');</code> * @@ -1015,7 +1290,8 @@ function elgg_view_listing($icon, $info) { * <input type="password" name="password" /> * </form> * - * @param string $action The name of the action (without the leading "action/") -- e.g. "login" + * @param string $action The name of the action. An action name does not include + * the leading "action/". For example, "login" is an action name. * @param array $form_vars $vars environment passed to the "input/form" view * @param array $body_vars $vars environment passed to the "forms/$action" view * @@ -1026,13 +1302,85 @@ function elgg_view_form($action, $form_vars = array(), $body_vars = array()) { $defaults = array( 'action' => $CONFIG->wwwroot . "action/$action", - 'body' => elgg_view("forms/$action", $body_vars), + 'body' => elgg_view("forms/$action", $body_vars) ); + $form_class = 'elgg-form-' . preg_replace('/[^a-z0-9]/i', '-', $action); + + // append elgg-form class to any class options set + if (isset($form_vars['class'])) { + $form_vars['class'] = $form_vars['class'] . " $form_class"; + } else { + $form_vars['class'] = $form_class; + } + return elgg_view('input/form', array_merge($defaults, $form_vars)); } /** + * View an item in a list + * + * @param ElggEntity|ElggAnnotation $item + * @param array $vars Additional parameters for the rendering + * + * @return string + * @since 1.8.0 + * @access private + */ +function elgg_view_list_item($item, array $vars = array()) { + global $CONFIG; + + $type = $item->getType(); + if (in_array($type, $CONFIG->entity_types)) { + return elgg_view_entity($item, $vars); + } else if ($type == 'annotation') { + return elgg_view_annotation($item, $vars); + } else if ($type == 'river') { + return elgg_view_river_item($item, $vars); + } + + return ''; +} + +/** + * View one of the elgg sprite icons + * + * Shorthand for <span class="elgg-icon elgg-icon-$name"></span> + * + * @param string $name The specific icon to display + * @param string $class Additional class: float, float-alt, or custom class + * + * @return string The html for displaying an icon + */ +function elgg_view_icon($name, $class = '') { + // @todo deprecate boolean in Elgg 1.9 + if ($class === true) { + $class = 'float'; + } + return "<span class=\"elgg-icon elgg-icon-$name $class\"></span>"; +} + +/** + * Displays a user's access collections, using the core/friends/collections view + * + * @param int $owner_guid The GUID of the owning user + * + * @return string A formatted rendition of the collections + * @todo Move to the friends/collection.php page. + * @access private + */ +function elgg_view_access_collections($owner_guid) { + if ($collections = get_user_access_collections($owner_guid)) { + foreach ($collections as $key => $collection) { + $collections[$key]->members = get_members_of_access_collection($collection->id, true); + $collections[$key]->entities = get_user_friends($owner_guid, "", 9999); + } + } + + return elgg_view('core/friends/collections', array('collections' => $collections)); +} + +/** * Registers a function to handle templates. * * Alternative template handlers can be registered to handle @@ -1055,7 +1403,8 @@ function elgg_view_form($action, $form_vars = array(), $body_vars = array()) { */ function set_template_handler($function_name) { global $CONFIG; - if (!empty($function_name) && is_callable($function_name)) { + + if (is_callable($function_name)) { $CONFIG->template_handler = $function_name; return true; } @@ -1074,6 +1423,7 @@ function set_template_handler($function_name) { * @since 1.7.0 * @todo Why isn't this used anywhere else but in elgg_view_tree()? * Seems like a useful function for autodiscovery. + * @access private */ function elgg_get_views($dir, $base) { $return = array(); @@ -1098,21 +1448,6 @@ function elgg_get_views($dir, $base) { } /** - * Get views in a dir - * - * @deprecated 1.7. Use elgg_get_views(). - * - * @param string $dir Dir - * @param string $base Base view - * - * @return array - */ -function get_views($dir, $base) { - elgg_deprecated_notice('get_views() was deprecated by elgg_get_views()!', 1.7); - elgg_get_views($dir, $base); -} - -/** * Returns all views below a partial view. * * Settings $view_root = 'profile' will show all available views under @@ -1124,20 +1459,17 @@ function get_views($dir, $base) { * * @return array A list of view names underneath that root view * @todo This is used once in the deprecated get_activity_stream_data() function. + * @access private */ function elgg_view_tree($view_root, $viewtype = "") { global $CONFIG; - static $treecache; + static $treecache = array(); // Get viewtype if (!$viewtype) { $viewtype = elgg_get_viewtype(); } - // Has the treecache been initialised? - if (!isset($treecache)) { - $treecache = array(); - } // A little light internal caching if (!empty($treecache[$view_root])) { return $treecache[$view_root]; @@ -1178,19 +1510,16 @@ function elgg_view_tree($view_root, $viewtype = "") { * * @param string $view_base Optional The base of the view name without the view type. * @param string $folder Required The folder to begin looking in - * @param string $base_location_path The base views directory to use with set_view_location + * @param string $base_location_path The base views directory to use with elgg_set_view_location() * @param string $viewtype The type of view we're looking at (default, rss, etc) * - * @return void + * @return bool returns false if folder can't be read * @since 1.7.0 - * @see set_view_location() + * @see elgg_set_view_location() * @todo This seems overly complicated. + * @access private */ function autoregister_views($view_base, $folder, $base_location_path, $viewtype) { - if (!isset($i)) { - $i = 0; - } - if ($handle = opendir($folder)) { while ($view = readdir($handle)) { if (!in_array($view, array('.', '..', '.svn', 'CVS')) && !is_dir($folder . "/" . $view)) { @@ -1203,7 +1532,7 @@ function autoregister_views($view_base, $folder, $base_location_path, $viewtype) $view_base_new = ""; } - set_view_location($view_base_new . str_replace('.php', '', $view), + elgg_set_view_location($view_base_new . str_replace('.php', '', $view), $base_location_path, $viewtype); } } else if (!in_array($view, array('.', '..', '.svn', 'CVS')) && is_dir($folder . "/" . $view)) { @@ -1223,252 +1552,114 @@ function autoregister_views($view_base, $folder, $base_location_path, $viewtype) } /** - * Registers a view to simple cache. - * - * Simple cache is a caching mechanism that saves the output of - * views and its extensions into a file. If the view is called - * by the {@link simplecache/view.php} file, the Elgg framework will - * not be loaded and the contents of the view will returned - * from file. - * - * @warning Simple cached views must take no parameters and return - * the same content no matter who is logged in. - * - * @note CSS and the basic JS views are cached by the engine. - * - * @param string $viewname View name - * - * @return void - * @link http://docs.elgg.org/Views/Simplecache - * @see elgg_view_regenerate_simplecache() - */ -function elgg_view_register_simplecache($viewname) { - global $CONFIG; - - if (!isset($CONFIG->views)) { - $CONFIG->views = new stdClass; - } - - if (!isset($CONFIG->views->simplecache)) { - $CONFIG->views->simplecache = array(); - } - - $CONFIG->views->simplecache[] = $viewname; -} - -/** - * Get the URL for the cached file - * - * @param string $type The file type: css or js - * @param string $view The view name - * @return string - * @since 1.8.0 - */ -function elgg_view_get_simplecache_url($type, $view) { - global $CONFIG; - $lastcache = (int)$CONFIG->lastcache; - - if (elgg_view_is_simplecache_enabled()) { - $viewtype = elgg_get_viewtype(); - $url = elgg_get_site_url() . "cache/$type/$view/$viewtype/$view.$lastcache.$type"; - } else { - $url = elgg_get_site_url() . "pg/$type/$view.$lastcache.$type"; - } - return $url; -} - -/** - * Regenerates the simple cache. - * - * @warning This does not invalidate the cache, but actively resets it. - * - * @param string $viewtype Optional viewtype to regenerate + * Add the rss link to the extras when if needed * * @return void - * @see elgg_view_register_simplecache() + * @access private */ -function elgg_view_regenerate_simplecache($viewtype = NULL) { - global $CONFIG; - - if (!isset($CONFIG->views->simplecache) || !is_array($CONFIG->views->simplecache)) { - return; - } - - $lastcached = time(); - - // @todo elgg_view() checks if the page set is done (isset($CONFIG->pagesetupdone)) and - // triggers an event if it's not. Calling elgg_view() here breaks submenus - // (at least) because the page setup hook is called before any - // contexts can be correctly set (since this is called before page_handler()). - // To avoid this, lie about $CONFIG->pagehandlerdone to force - // the trigger correctly when the first view is actually being output. - $CONFIG->pagesetupdone = TRUE; - - if (!file_exists($CONFIG->dataroot . 'views_simplecache')) { - mkdir($CONFIG->dataroot . 'views_simplecache'); - } - - if (isset($viewtype)) { - $viewtypes = array($viewtype); - } else { - $viewtypes = $CONFIG->view_types; - } - - $original_viewtype = elgg_get_viewtype(); - - foreach ($viewtypes as $viewtype) { - elgg_set_viewtype($viewtype); - foreach ($CONFIG->views->simplecache as $view) { - $viewcontents = elgg_view($view); - $viewname = md5(elgg_get_viewtype() . $view); - if ($handle = fopen($CONFIG->dataroot . 'views_simplecache/' . $viewname, 'w')) { - fwrite($handle, $viewcontents); - fclose($handle); - } +function elgg_views_add_rss_link() { + global $autofeed; + if (isset($autofeed) && $autofeed == true) { + $url = current_page_url(); + if (substr_count($url, '?')) { + $url .= "&view=rss"; + } else { + $url .= "?view=rss"; } - datalist_set("simplecache_lastupdate_$viewtype", $lastcached); - datalist_set("simplecache_lastcached_$viewtype", $lastcached); + $url = elgg_format_url($url); + elgg_register_menu_item('extras', array( + 'name' => 'rss', + 'text' => elgg_view_icon('rss'), + 'href' => $url, + 'title' => elgg_echo('feed:rss'), + )); } - - elgg_set_viewtype($original_viewtype); - - // needs to be set for links in html head - $CONFIG->lastcache = $lastcached; - - unset($CONFIG->pagesetupdone); } /** - * Is simple cache enabled + * Registers deprecated views to avoid making some pages from older plugins + * completely empty. * - * @return bool - * @since 1.8.0 + * @access private */ -function elgg_view_is_simplecache_enabled() { - global $CONFIG; - - if ($CONFIG->simplecache_enabled) { - return true; +function elgg_views_handle_deprecated_views() { + $location = elgg_get_view_location('page_elements/contentwrapper'); + if ($location === "/var/www/views/") { + elgg_extend_view('page_elements/contentwrapper', 'page/elements/wrapper'); } - - return false; } /** - * Enables the simple cache. + * Initialize viewtypes on system boot event + * This ensures simplecache is cleared during upgrades. See #2252 * - * @access private - * @see elgg_view_register_simplecache() * @return void - */ -function elgg_view_enable_simplecache() { - global $CONFIG; - - datalist_set('simplecache_enabled', 1); - $CONFIG->simplecache_enabled = 1; - elgg_view_regenerate_simplecache(); -} - -/** - * Disables the simple cache. - * - * @warning Simplecache is also purged when disabled. - * * @access private - * @see elgg_view_register_simplecache() - * @return void + * @elgg_event_handler boot system */ -function elgg_view_disable_simplecache() { +function elgg_views_boot() { global $CONFIG; - if ($CONFIG->simplecache_enabled) { - datalist_set('simplecache_enabled', 0); - $CONFIG->simplecache_enabled = 0; - - // purge simple cache - if ($handle = opendir($CONFIG->dataroot . 'views_simplecache')) { - while (false !== ($file = readdir($handle))) { - if ($file != "." && $file != "..") { - unlink($CONFIG->dataroot . 'views_simplecache/' . $file); - } - } - closedir($handle); - } - } -} -/** - * Invalidates all cached views in the simplecache - * - * @return bool - * @since 1.7.4 - */ -function elgg_invalidate_simplecache() { - global $CONFIG; + elgg_register_simplecache_view('css/ie'); + elgg_register_simplecache_view('css/ie6'); + elgg_register_simplecache_view('css/ie7'); - $return = TRUE; + elgg_register_js('jquery', '/vendors/jquery/jquery-1.6.4.min.js', 'head'); + elgg_register_js('jquery-ui', '/vendors/jquery/jquery-ui-1.8.16.min.js', 'head'); + elgg_register_js('jquery.form', '/vendors/jquery/jquery.form.js'); - if ($handle = opendir($CONFIG->dataroot . 'views_simplecache')) { - while (false !== ($file = readdir($handle))) { - if ($file != "." && $file != "..") { - $return = $return && unlink($CONFIG->dataroot . 'views_simplecache/' . $file); - } - } - closedir($handle); - } else { - $return = FALSE; - } + elgg_register_simplecache_view('js/elgg'); + $elgg_js_url = elgg_get_simplecache_url('js', 'elgg'); + elgg_register_js('elgg', $elgg_js_url, 'head'); - return $return; -} + elgg_load_js('jquery'); + elgg_load_js('jquery-ui'); + elgg_load_js('elgg'); -/** - * Add the core Elgg head elements that could be cached - */ -function elgg_views_register_core_head_elements() { - $url = elgg_view_get_simplecache_url('js', 'initialise_elgg'); - elgg_register_js($url, 'initialise_elgg'); + elgg_register_simplecache_view('js/lightbox'); + $lightbox_js_url = elgg_get_simplecache_url('js', 'lightbox'); + elgg_register_js('lightbox', $lightbox_js_url); - $url = elgg_view_get_simplecache_url('css', 'screen'); - elgg_register_css($url, 'screen'); -} + elgg_register_simplecache_view('css/lightbox'); + $lightbox_css_url = elgg_get_simplecache_url('css', 'lightbox'); + elgg_register_css('lightbox', $lightbox_css_url); -/** - * Initialize viewtypes on system boot event - * This ensures simplecache is cleared during upgrades. See #2252 - * - * @return void - * @access private - * @elgg_event_handler boot system - */ -function elgg_views_boot() { - global $CONFIG; + elgg_register_simplecache_view('css/elgg'); + $elgg_css_url = elgg_get_simplecache_url('css', 'elgg'); + elgg_register_css('elgg', $elgg_css_url); - elgg_view_register_simplecache('css/screen'); - elgg_view_register_simplecache('css/ie'); - elgg_view_register_simplecache('css/ie6'); - elgg_view_register_simplecache('js/friendsPickerv1'); - elgg_view_register_simplecache('js/initialise_elgg'); + elgg_load_css('elgg'); - $base = elgg_get_site_url(); - elgg_register_js("{$base}vendors/jquery/jquery-1.4.2.min.js", 'jquery'); - elgg_register_js("{$base}vendors/jquery/jquery-ui-1.7.2.min.js", 'jquery-ui'); - elgg_register_js("{$base}vendors/jquery/jquery.form.js", 'jquery.form'); + elgg_register_ajax_view('js/languages'); - elgg_register_event_handler('pagesetup', 'system', 'elgg_views_register_core_head_elements'); + elgg_register_plugin_hook_handler('output:before', 'layout', 'elgg_views_add_rss_link'); // discover the built-in view types - // @todo cache this + // @todo the cache is loaded in load_plugins() but we need to know view_types earlier $view_path = $CONFIG->viewpath; - $CONFIG->view_types = array(); $views = scandir($view_path); foreach ($views as $view) { - if ('.' !== substr($view, 0, 1) && is_dir($view_path . $view)) { - $CONFIG->view_types[] = $view; + if ($view[0] !== '.' && is_dir($view_path . $view)) { + elgg_register_viewtype($view); } } + + // set default icon sizes - can be overridden in settings.php or with plugin + if (!isset($CONFIG->icon_sizes)) { + $icon_sizes = array( + 'topbar' => array('w' => 16, 'h' => 16, 'square' => TRUE, 'upscale' => TRUE), + 'tiny' => array('w' => 25, 'h' => 25, 'square' => TRUE, 'upscale' => TRUE), + 'small' => array('w' => 40, 'h' => 40, 'square' => TRUE, 'upscale' => TRUE), + 'medium' => array('w' => 100, 'h' => 100, 'square' => TRUE, 'upscale' => TRUE), + 'large' => array('w' => 200, 'h' => 200, 'square' => FALSE, 'upscale' => FALSE), + 'master' => array('w' => 550, 'h' => 550, 'square' => FALSE, 'upscale' => FALSE), + ); + elgg_set_config('icon_sizes', $icon_sizes); + } } -elgg_register_event_handler('boot', 'system', 'elgg_views_boot', 1000);
\ No newline at end of file +elgg_register_event_handler('boot', 'system', 'elgg_views_boot'); +elgg_register_event_handler('init', 'system', 'elgg_views_handle_deprecated_views'); diff --git a/engine/lib/web_services.php b/engine/lib/web_services.php index e529711e1..51cad6f39 100644 --- a/engine/lib/web_services.php +++ b/engine/lib/web_services.php @@ -29,6 +29,7 @@ * ) * ) */ +global $API_METHODS; $API_METHODS = array(); /** @@ -153,6 +154,7 @@ function unexpose_function($method) { * @return true or throws an exception * @throws APIException * @since 1.7.0 + * @access private */ function authenticate_method($method) { global $API_METHODS; @@ -162,12 +164,6 @@ function authenticate_method($method) { throw new APIException(elgg_echo('APIException:MethodCallNotImplemented', array($method))); } - // make sure that POST variables are available if needed - // @todo this may not be needed anymore due to adding %{QUERY_STRING} in .htaccess in 1.7.2 - if (get_call_method() === 'POST' && empty($_POST)) { - include_post_data(); - } - // check API authentication if required if ($API_METHODS[$method]["require_api_auth"] == true) { $api_pam = new ElggPAM('api'); @@ -177,12 +173,12 @@ function authenticate_method($method) { } $user_pam = new ElggPAM('user'); - $user_auth_result = $user_pam->authenticate(); + $user_auth_result = $user_pam->authenticate(array()); // check if user authentication is required if ($API_METHODS[$method]["require_user_auth"] == true) { if ($user_auth_result == false) { - throw new APIException($user_pam->getFailureMessage()); + throw new APIException($user_pam->getFailureMessage(), ErrorResult::$RESULT_FAIL_AUTHTOKEN); } } @@ -197,6 +193,7 @@ function authenticate_method($method) { * * @return GenericResult The result of the execution. * @throws APIException, CallException + * @access private */ function execute_method($method) { global $API_METHODS, $CONFIG; @@ -235,6 +232,7 @@ function execute_method($method) { $function = $API_METHODS[$method]["function"]; $serialised_parameters = trim($serialised_parameters, ", "); + // @todo document why we cannot use call_user_func_array here $result = eval("return $function($serialised_parameters);"); // Sanity check result @@ -262,6 +260,7 @@ function execute_method($method) { * Get the request method. * * @return string HTTP request method + * @access private */ function get_call_method() { return $_SERVER['REQUEST_METHOD']; @@ -276,6 +275,7 @@ function get_call_method() { * @param string $method The method * * @return array containing parameters as key => value + * @access private */ function get_parameters_for_method($method) { global $API_METHODS; @@ -286,7 +286,7 @@ function get_parameters_for_method($method) { if (isset($API_METHODS[$method]['parameters'])) { foreach ($API_METHODS[$method]['parameters'] as $k => $v) { $param = get_input($k); // Make things go through the sanitiser - if ($param !== '') { + if ($param !== '' && $param !== null) { $sanitised[$k] = $param; } else { // parameter wasn't passed so check for default @@ -305,6 +305,7 @@ function get_parameters_for_method($method) { * Since this is called through a handler, we need to manually get the post data * * @return POST data as string encoded as multipart/form-data + * @access private */ function get_post_data() { @@ -314,37 +315,6 @@ function get_post_data() { } /** - * This fixes the post parameters that are munged due to page handler - * - * @since 1.7.0 - * - * @return void - */ -function include_post_data() { - - $postdata = get_post_data(); - - if (isset($postdata)) { - $query_arr = elgg_parse_str($postdata); - - // grrrr... magic quotes is turned on so we need to strip slashes - if (ini_get_bool('magic_quotes_gpc')) { - if (function_exists('stripslashes_deep')) { - // defined in input.php to handle magic quotes - $query_arr = stripslashes_deep($query_arr); - } - } - - if (is_array($query_arr)) { - foreach ($query_arr as $name => $val) { - set_input($name, $val); - } - } - - } -} - -/** * Verify that the required parameters are present * * @param string $method Method name @@ -353,6 +323,7 @@ function include_post_data() { * @return true on success or exception * @throws APIException * @since 1.7.0 + * @access private */ function verify_parameters($method, $parameters) { global $API_METHODS; @@ -390,6 +361,7 @@ function verify_parameters($method, $parameters) { * @return string or exception * @throws APIException * @since 1.7.0 + * @access private */ function serialise_parameters($method, $parameters) { global $API_METHODS; @@ -474,6 +446,7 @@ function serialise_parameters($method, $parameters) { * @return mixed * @throws APIException * @since 1.7.0 + * @access private */ function api_auth_key() { global $CONFIG; @@ -504,6 +477,7 @@ function api_auth_key() { * * @throws SecurityException * @since 1.7.0 + * @access private */ function api_auth_hmac() { global $CONFIG; @@ -568,6 +542,7 @@ function api_auth_hmac() { * * @return stdClass Containing all the values. * @throws APIException Detailing any error. + * @access private */ function get_and_validate_api_headers() { $result = new stdClass; @@ -640,6 +615,7 @@ function get_and_validate_api_headers() { * * @return string The php algorithm * @throws APIException if an algorithm is not supported. + * @access private */ function map_api_hash($algo) { $algo = strtolower(sanitise_string($algo)); @@ -672,6 +648,7 @@ function map_api_hash($algo) { * @param string $post_hash Optional sha1 hash of the post data. * * @return string The HMAC signature + * @access private */ function calculate_hmac($algo, $time, $nonce, $api_key, $secret_key, $get_variables, $post_hash = "") { @@ -702,6 +679,7 @@ $get_variables, $post_hash = "") { * @param string $algo The algorithm used. * * @return string The hash. + * @access private */ function calculate_posthash($postdata, $algo) { $ctx = hash_init(map_api_hash($algo)); @@ -718,6 +696,7 @@ function calculate_posthash($postdata, $algo) { * @param string $hmac The hmac string. * * @return bool True if replay detected, false if not. + * @access private */ function cache_hmac_check_replay($hmac) { // cache lifetime is 25 hours (this should be related to the time drift @@ -815,6 +794,7 @@ function remove_api_user($site_guid, $api_key) { * session code of Elgg, that user will be logged out of all other sessions. * * @return bool + * @access private */ function pam_auth_usertoken() { global $CONFIG; @@ -859,9 +839,10 @@ function pam_auth_usertoken() { * See if the user has a valid login sesson * * @return bool + * @access private */ function pam_auth_session() { - return isloggedin(); + return elgg_is_logged_in(); } // user token functions @@ -1004,6 +985,7 @@ function remove_expired_user_tokens() { * @param array $headers The array of headers "key" => "value" * * @return string + * @access private */ function serialise_api_headers(array $headers) { $headers_str = ""; @@ -1159,6 +1141,7 @@ function get_standard_api_key_array($secret_key, $api_key) { * Simple api to return a list of all api's installed on the system. * * @return array + * @access private */ function list_all_apis() { global $API_METHODS; @@ -1180,9 +1163,21 @@ function list_all_apis() { * * @return string Token string or exception * @throws SecurityException + * @access private */ function auth_gettoken($username, $password) { - if (authenticate($username, $password)) { + // check if username is an email address
+ if (is_email_address($username)) {
+ $users = get_user_by_email($username);
+
+ // check if we have a unique user
+ if (is_array($users) && (count($users) == 1)) {
+ $username = $users[0]->username;
+ }
+ }
+
+ // validate username and password + if (true === elgg_authenticate($username, $password)) { $token = create_user_token($username); if ($token) { return $token; @@ -1210,6 +1205,9 @@ $ERRORS = array(); * @param array $vars Vars * * @return void + * @access private + * + * @throws Exception */ function _php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) { global $ERRORS; @@ -1247,6 +1245,7 @@ function _php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) { * @param Exception $exception Exception * * @return void + * @access private */ function _php_api_exception_handler($exception) { @@ -1269,6 +1268,7 @@ function _php_api_exception_handler($exception) { * @param array $request Request string * * @return void + * @access private */ function service_handler($handler, $request) { global $CONFIG; @@ -1278,25 +1278,23 @@ function service_handler($handler, $request) { $request = explode('/', $request); // after the handler, the first identifier is response format - // ex) http://example.org/services/api/rest/xml/?method=test - $reponse_format = array_shift($request); + // ex) http://example.org/services/api/rest/json/?method=test + $response_format = array_shift($request); // Which view - xml, json, ... - if ($reponse_format) { - elgg_set_viewtype($reponse_format); + if ($response_format && elgg_is_valid_view_type($response_format)) { + elgg_set_viewtype($response_format); } else { - // default to xml - elgg_set_viewtype("xml"); + // default to json + elgg_set_viewtype("json"); } if (!isset($CONFIG->servicehandler) || empty($handler)) { // no handlers set or bad url header("HTTP/1.0 404 Not Found"); exit; - } else if (isset($CONFIG->servicehandler[$handler]) - && is_callable($CONFIG->servicehandler[$handler])) { - + } else if (isset($CONFIG->servicehandler[$handler]) && is_callable($CONFIG->servicehandler[$handler])) { $function = $CONFIG->servicehandler[$handler]; - $function($request, $handler); + call_user_func($function, $request, $handler); } else { // no handler for this web service header("HTTP/1.0 404 Not Found"); @@ -1315,10 +1313,11 @@ function service_handler($handler, $request) { */ function register_service_handler($handler, $function) { global $CONFIG; + if (!isset($CONFIG->servicehandler)) { $CONFIG->servicehandler = array(); } - if (is_callable($function)) { + if (is_callable($function, true)) { $CONFIG->servicehandler[$handler] = $function; return true; } @@ -1333,11 +1332,13 @@ function register_service_handler($handler, $function) { * * @param string $handler web services type * - * @return 1.7.0 + * @return void + * @since 1.7.0 */ function unregister_service_handler($handler) { global $CONFIG; - if (isset($CONFIG->servicehandler) && isset($CONFIG->servicehandler[$handler])) { + + if (isset($CONFIG->servicehandler, $CONFIG->servicehandler[$handler])) { unset($CONFIG->servicehandler[$handler]); } } @@ -1346,6 +1347,9 @@ function unregister_service_handler($handler) { * REST API handler * * @return void + * @access private + * + * @throws SecurityException|APIException */ function rest_handler() { global $CONFIG; @@ -1400,15 +1404,17 @@ function rest_handler() { /** * Unit tests for API * - * @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 api_unit_test($hook, $type, $value, $params) { global $CONFIG; + $value[] = $CONFIG->path . 'engine/tests/services/api.php'; return $value; } @@ -1417,6 +1423,7 @@ function api_unit_test($hook, $type, $value, $params) { * Initialise the API subsystem. * * @return void + * @access private */ function api_init() { // Register a page handler, so we can have nice URLs @@ -1429,15 +1436,18 @@ function api_init() { elgg_echo("system.api.list"), "GET", false, false); // The authentication token api - expose_function("auth.gettoken", - "auth_gettoken", array( - 'username' => array ('type' => 'string'), - 'password' => array ('type' => 'string'), - ), - elgg_echo('auth.gettoken'), - 'POST', - false, - false); + expose_function( + "auth.gettoken", + "auth_gettoken", + array( + 'username' => array ('type' => 'string'), + 'password' => array ('type' => 'string'), + ), + elgg_echo('auth.gettoken'), + 'POST', + false, + false + ); } diff --git a/engine/lib/widgets.php b/engine/lib/widgets.php index e11e4b8e3..699462a1b 100644 --- a/engine/lib/widgets.php +++ b/engine/lib/widgets.php @@ -11,12 +11,12 @@ * Get widgets for a particular context * * The widgets are ordered for display and grouped in columns. - * $widgets = elgg_get_widgets(get_loggedin_userid(), 'dashboard'); + * $widgets = elgg_get_widgets(elgg_get_logged_in_user_guid(), 'dashboard'); * $first_column_widgets = $widgets[1]; * * @param int $user_guid The owner user GUID * @param string $context The context (profile, dashboard, etc) - * + * * @return array An 2D array of ElggWidget objects * @since 1.8.0 */ @@ -26,7 +26,8 @@ function elgg_get_widgets($user_guid, $context) { 'subtype' => 'widget', 'owner_guid' => $user_guid, 'private_setting_name' => 'context', - 'private_setting_value' => $context + 'private_setting_value' => $context, + 'limit' => 0 ); $widgets = elgg_get_entities_from_private_settings($options); if (!$widgets) { @@ -51,12 +52,13 @@ function elgg_get_widgets($user_guid, $context) { /** * Create a new widget instance * - * @param int $entity_guid GUID of entity that owns this widget - * @param string $handler The handler for this widget - * @param int $access_id If not specified, it is set to the default access level - * + * @param int $owner_guid GUID of entity that owns this widget + * @param string $handler The handler for this widget + * @param string $context The context for this widget + * @param int $access_id If not specified, it is set to the default access level + * * @return int|false Widget GUID or false on failure - * @since 1.8 + * @since 1.8.0 */ function elgg_create_widget($owner_guid, $handler, $context, $access_id = null) { if (empty($owner_guid) || empty($handler) || !elgg_is_widget_type($handler)) { @@ -103,11 +105,11 @@ function elgg_can_edit_widget_layout($context, $user_guid = 0) { $user = get_entity((int)$user_guid); if (!$user) { - $user = get_loggedin_user(); + $user = elgg_get_logged_in_user_entity(); } $return = false; - if (isadminloggedin()) { + if (elgg_is_admin_logged_in()) { $return = true; } if (elgg_get_page_owner_guid() == $user->guid) { @@ -117,7 +119,7 @@ function elgg_can_edit_widget_layout($context, $user_guid = 0) { $params = array( 'user' => $user, 'context' => $context, - 'page_owner' => elgg_get_page_owner() + 'page_owner' => elgg_get_page_owner_entity() ); return elgg_trigger_plugin_hook('permissions_check', 'widget_layout', $params, $return); } @@ -134,7 +136,7 @@ function elgg_can_edit_widget_layout($context, $user_guid = 0) { * widget is allowed (default: 'all') * @param bool $multiple Whether or not multiple instances of this widget * are allowed in a single layout (default: false) - * + * * @return bool * @since 1.8.0 */ @@ -168,7 +170,7 @@ function elgg_register_widget_type($handler, $name, $description, $context = "al * Remove a widget type * * @param string $handler The identifier for the widget - * + * * @return void * @since 1.8.0 */ @@ -192,7 +194,7 @@ function elgg_unregister_widget_type($handler) { * Has a widget type with the specified handler been registered * * @param string $handler The widget handler identifying string - * + * * @return bool Whether or not that widget type exists * @since 1.8.0 */ @@ -215,12 +217,13 @@ function elgg_is_widget_type($handler) { * * The widget types are stdClass objects. * - * @param string context The widget context or empty string for current context - * + * @param string $context The widget context or empty string for current context + * @param bool $exact Only return widgets registered for this context (false) + * * @return array * @since 1.8.0 */ -function elgg_get_widget_types($context = "") { +function elgg_get_widget_types($context = "", $exact = false) { global $CONFIG; if (empty($CONFIG->widgets) || @@ -236,8 +239,14 @@ function elgg_get_widget_types($context = "") { $widgets = array(); foreach ($CONFIG->widgets->handlers as $key => $handler) { - if (in_array('all', $handler->context) || in_array($context, $handler->context)) { - $widgets[$key] = $handler; + if ($exact) { + if (in_array($context, $handler->context)) { + $widgets[$key] = $handler; + } + } else { + if (in_array('all', $handler->context) || in_array($context, $handler->context)) { + $widgets[$key] = $handler; + } } } @@ -248,6 +257,7 @@ function elgg_get_widget_types($context = "") { * Regsiter entity of object, widget as ElggWidget objects * * @return void + * @access private */ function elgg_widget_run_once() { add_subtype("object", "widget", "ElggWidget"); @@ -257,443 +267,154 @@ function elgg_widget_run_once() { * Function to initialize widgets functionality * * @return void + * @access private */ function elgg_widgets_init() { elgg_register_action('widgets/save'); elgg_register_action('widgets/add'); elgg_register_action('widgets/move'); elgg_register_action('widgets/delete'); + elgg_register_action('widgets/upgrade', '', 'admin'); run_function_once("elgg_widget_run_once"); } -elgg_register_event_handler('init', 'system', 'elgg_widgets_init'); - - - -/** - * When given a widget entity and a new requested location, saves the new location - * and also provides a sensible ordering for all widgets in that column - * - * @param ElggObject $widget The widget entity - * @param int $order The order within the column - * @param int $column The column (1, 2 or 3) - * - * @return bool Depending on success - * @deprecated 1.8 use ElggWidget::move() - */ -function save_widget_location(ElggObject $widget, $order, $column) { - elgg_deprecated_notice('save_widget_location() is deprecated', 1.8); - if ($widget instanceof ElggObject) { - if ($widget->subtype == "widget") { - // If you can't move the widget, don't save a new location - if (!$widget->draggable) { - return false; - } - - // Sanitise the column value - if ($column != 1 || $column != 2 || $column != 3) { - $column = 1; - } - - $widget->column = (int) $column; - - $ordertmp = array(); - $params = array( - 'context' => $widget->context, - 'column' => $column, - ); - - if ($entities = get_entities_from_metadata_multi($params, 'object', 'widget')) { - foreach ($entities as $entity) { - $entityorder = $entity->order; - if ($entityorder < $order) { - $ordertmp[$entityorder] = $entity; - } - if ($entityorder >= $order) { - $ordertmp[$entityorder + 10000] = $entity; - } - } - } - - $ordertmp[$order] = $widget; - ksort($ordertmp); - - $orderticker = 10; - foreach ($ordertmp as $orderval => $entity) { - $entity->order = $orderticker; - $orderticker += 10; - } - - return true; - } else { - register_error($widget->subtype); - } - - } - - return false; -} - -/** - * Get widgets for a particular context and column, in order of display - * - * @param int $user_guid The owner user GUID - * @param string $context The context (profile, dashboard etc) - * @param int $column The column (1 or 2) - * - * @return array|false An array of widget ElggObjects, or false - * @deprecated 1.8 Use elgg_get_widgets() - */ -function get_widgets($user_guid, $context, $column) { - elgg_deprecated_notice('get_widgets is depecated for elgg_get_widgets', 1.8); - $params = array( - 'column' => $column, - 'context' => $context - ); - $widgets = get_entities_from_private_setting_multi($params, "object", - "widget", $user_guid, "", 10000); - - if ($widgets) { - $widgetorder = array(); - foreach ($widgets as $widget) { - $order = $widget->order; - while (isset($widgetorder[$order])) { - $order++; - } - $widgetorder[$order] = $widget; - } - - ksort($widgetorder); - - return $widgetorder; - } - - return false; -} - /** - * Add a new widget instance - * - * @param int $entity_guid GUID of entity that owns this widget - * @param string $handler The handler for this widget - * @param string $context The page context for this widget - * @param int $order The order to display this widget in - * @param int $column The column to display this widget in (1, 2 or 3) - * @param int $access_id If not specified, it is set to the default access level - * - * @return int|false Widget GUID or false on failure - * @deprecated 1.8 use elgg_create_widget() - */ -function add_widget($entity_guid, $handler, $context, $order = 0, $column = 1, $access_id = null) { - elgg_deprecated_notice('add_widget has been deprecated for elgg_create_widget', 1.8); - if (empty($entity_guid) || empty($context) || empty($handler) || !widget_type_exists($handler)) { - return false; - } - - if ($entity = get_entity($entity_guid)) { - $widget = new ElggWidget; - $widget->owner_guid = $entity_guid; - $widget->container_guid = $entity_guid; - if (isset($access_id)) { - $widget->access_id = $access_id; - } else { - $widget->access_id = get_default_access(); - } - - $guid = $widget->save(); - - // private settings cannot be set until ElggWidget saved - $widget->handler = $handler; - $widget->context = $context; - $widget->column = $column; - $widget->order = $order; - - return $guid; - } - - return false; -} - -/** - * Define a new widget type - * - * @param string $handler The identifier for the widget handler - * @param string $name The name of the widget type - * @param string $description A description for the widget type - * @param string $context A comma-separated list of contexts where this - * widget is allowed (default: 'all') - * @param bool $multiple Whether or not multiple instances of this widget - * are allowed on a single dashboard (default: false) - * @param string $positions A comma-separated list of positions on the page - * (side or main) where this widget is allowed (default: "side,main") - * - * @return bool Depending on success - * @deprecated 1.8 Use elgg_register_widget_type - */ -function add_widget_type($handler, $name, $description, $context = "all", -$multiple = false, $positions = "side,main") { - elgg_deprecated_notice("add_widget_type deprecated for elgg_register_widget_type", 1.8); - - return elgg_register_widget_type($handler, $name, $description, $context, $multiple); -} - -/** - * Remove a widget type - * - * @param string $handler The identifier for the widget handler + * Gets a list of events to create default widgets for and + * register menu items for default widgets with the admin section. + * + * A plugin that wants to register a new context for default widgets should + * register for the plugin hook 'get_list', 'default_widgets'. The handler + * can register the new type of default widgets by adding an associate array to + * the return value array like this: + * array( + * 'name' => elgg_echo('profile'), + * 'widget_context' => 'profile', + * 'widget_columns' => 3, + * + * 'event' => 'create', + * 'entity_type' => 'user', + * 'entity_subtype' => ELGG_ENTITIES_ANY_VALUE, + * ); + * + * The first set of keys define information about the new type of default + * widgets and the second set determine what event triggers the creation of the + * new widgets. * * @return void - * @since 1.7.1 - * @deprecated 1.8 Use elgg_unregister_widget_type - */ -function remove_widget_type($handler) { - elgg_deprecated_notice("remove_widget_type deprecated for elgg_unregister_widget_type", 1.8); - return elgg_unregister_widget_type($handler); -} - -/** - * Determines whether or not widgets with the specified handler have been defined - * - * @param string $handler The widget handler identifying string - * - * @return bool Whether or not those widgets exist - * @deprecated 1.8 Use elgg_is_widget_type - */ -function widget_type_exists($handler) { - elgg_deprecated_notice("widget_type_exists deprecated for elgg_is_widget_type", 1.8); - return elgg_is_widget_type($handler); -} - -/** - * Returns an array of stdClass objects representing the defined widget types - * - * @return array A list of types defined (if any) - * @deprecated 1.8 Use elgg_get_widget_types - */ -function get_widget_types() { - elgg_deprecated_notice("get_widget_types deprecrated for elgg_get_widget_types", 1.8); - return elgg_get_widget_types(); -} - -/** - * Saves a widget's settings (by passing an array of - * (name => value) pairs to save_{$handler}_widget) - * - * @param int $widget_guid The GUID of the widget we're saving to - * @param array $params An array of name => value parameters - * - * @return bool - * @deprecated 1.8 Use elgg_save_widget_settings + * @access private */ -function save_widget_info($widget_guid, $params) { - elgg_deprecated_notice("save_widget_info() is deprecated for elgg_save_widget_settings", 1.8); - if ($widget = get_entity($widget_guid)) { +function elgg_default_widgets_init() { + global $CONFIG; + $default_widgets = elgg_trigger_plugin_hook('get_list', 'default_widgets', null, array()); - $subtype = $widget->getSubtype(); + $CONFIG->default_widget_info = $default_widgets; - if ($subtype != "widget") { - return false; - } - $handler = $widget->handler; - if (empty($handler) || !widget_type_exists($handler)) { - return false; - } + if ($default_widgets) { + elgg_register_admin_menu_item('configure', 'default_widgets', 'appearance'); - if (!$widget->canEdit()) { - return false; - } + // override permissions for creating widget on logged out / just created entities + elgg_register_plugin_hook_handler('container_permissions_check', 'object', 'elgg_default_widgets_permissions_override'); - // Save the params to the widget - if (is_array($params) && sizeof($params) > 0) { - foreach ($params as $name => $value) { - - if (!empty($name) && !in_array($name, array( - 'guid', 'owner_guid', 'site_guid' - ))) { - if (is_array($value)) { - // @todo Handle arrays securely - $widget->setMetaData($name, $value, "", true); - } else { - $widget->$name = $value; - } - } - } - $widget->save(); + // only register the callback once per event + $events = array(); + foreach ($default_widgets as $info) { + $events[$info['event'] . ',' . $info['entity_type']] = $info; } - - $function = "save_{$handler}_widget"; - if (is_callable($function)) { - return $function($params); + foreach ($events as $info) { + elgg_register_event_handler($info['event'], $info['entity_type'], 'elgg_create_default_widgets'); } - - return true; } - - return false; } /** - * Reorders the widgets from a widget panel + * Creates default widgets * - * @param string $panelstring1 String of guids of ElggWidget objects separated by :: - * @param string $panelstring2 String of guids of ElggWidget objects separated by :: - * @param string $panelstring3 String of guids of ElggWidget objects separated by :: - * @param string $context Profile or dashboard - * @param int $owner Owner guid + * This plugin hook handler is registered for events based on what kinds of + * default widgets have been registered. See elgg_default_widgets_init() for + * information on registering new default widget contexts. * + * @param string $event The event + * @param string $type The type of object + * @param ElggEntity $entity The entity being created * @return void - * @deprecated 1.8 + * @access private */ -function reorder_widgets_from_panel($panelstring1, $panelstring2, $panelstring3, $context, $owner) { - elgg_deprecated_notice("reorder_widgets_from_panel() is deprecated", 1.8); - $return = true; - - $mainwidgets = explode('::', $panelstring1); - $sidewidgets = explode('::', $panelstring2); - $rightwidgets = explode('::', $panelstring3); - - $handlers = array(); - $guids = array(); - - if (is_array($mainwidgets) && sizeof($mainwidgets) > 0) { - foreach ($mainwidgets as $widget) { - - $guid = (int) $widget; - - if ("{$guid}" == "{$widget}") { - $guids[1][] = $widget; - } else { - $handlers[1][] = $widget; - } - } - } - if (is_array($sidewidgets) && sizeof($sidewidgets) > 0) { - foreach ($sidewidgets as $widget) { - - $guid = (int) $widget; +function elgg_create_default_widgets($event, $type, $entity) { + $default_widget_info = elgg_get_config('default_widget_info'); - if ("{$guid}" == "{$widget}") { - $guids[2][] = $widget; - } else { - $handlers[2][] = $widget; - } - - } - } - if (is_array($rightwidgets) && sizeof($rightwidgets) > 0) { - foreach ($rightwidgets as $widget) { - - $guid = (int) $widget; - - if ("{$guid}" == "{$widget}") { - $guids[3][] = $widget; - } else { - $handlers[3][] = $widget; - } - - } + if (!$default_widget_info || !$entity) { + return; } - // Reorder existing widgets or delete ones that have vanished - foreach (array(1, 2, 3) as $column) { - if ($dbwidgets = get_widgets($owner, $context, $column)) { - - foreach ($dbwidgets as $dbwidget) { - if (in_array($dbwidget->getGUID(), $guids[1]) - || in_array($dbwidget->getGUID(), $guids[2]) || in_array($dbwidget->getGUID(), $guids[3])) { - - if (in_array($dbwidget->getGUID(), $guids[1])) { - $pos = array_search($dbwidget->getGUID(), $guids[1]); - $col = 1; - } else if (in_array($dbwidget->getGUID(), $guids[2])) { - $pos = array_search($dbwidget->getGUID(), $guids[2]); - $col = 2; - } else { - $pos = array_search($dbwidget->getGUID(), $guids[3]); - $col = 3; - } - $pos = ($pos + 1) * 10; - $dbwidget->column = $col; - $dbwidget->order = $pos; - } else { - $dbguid = $dbwidget->getGUID(); - if (!$dbwidget->delete()) { - $return = false; - } else { - // Remove state cookie - setcookie('widget' + $dbguid, null); + $type = $entity->getType(); + $subtype = $entity->getSubtype(); + + // event is already guaranteed by the hook registration. + // need to check subtype and type. + foreach ($default_widget_info as $info) { + if ($info['entity_type'] == $type) { + if ($info['entity_subtype'] == ELGG_ENTITIES_ANY_VALUE || $info['entity_subtype'] == $subtype) { + + // need to be able to access everything + $old_ia = elgg_set_ignore_access(true); + elgg_push_context('create_default_widgets'); + + // pull in by widget context with widget owners as the site + // not using elgg_get_widgets() because it sorts by columns and we don't care right now. + $options = array( + 'type' => 'object', + 'subtype' => 'widget', + 'owner_guid' => elgg_get_site_entity()->guid, + 'private_setting_name' => 'context', + 'private_setting_value' => $info['widget_context'], + 'limit' => 0 + ); + + $widgets = elgg_get_entities_from_private_settings($options); + /* @var ElggWidget[] $widgets */ + + foreach ($widgets as $widget) { + // change the container and owner + $new_widget = clone $widget; + $new_widget->container_guid = $entity->guid; + $new_widget->owner_guid = $entity->guid; + + // pull in settings + $settings = get_all_private_settings($widget->guid); + + foreach ($settings as $name => $value) { + $new_widget->$name = $value; } - } - } - } - // Add new ones - if (sizeof($guids[$column]) > 0) { - foreach ($guids[$column] as $key => $guid) { - if ($guid == 0) { - $pos = ($key + 1) * 10; - $handler = $handlers[$column][$key]; - if (!add_widget($owner, $handler, $context, $pos, $column)) { - $return = false; - } + $new_widget->save(); } + + elgg_set_ignore_access($old_ia); + elgg_pop_context(); } } } - - return $return; -} - -/** - * Register a particular context for use with widgets. - * - * @param string $context The context we wish to enable context for - * - * @return void - * @deprecated 1.8 - */ -function use_widgets($context) { - elgg_deprecated_notice("use_widgets is deprecated", 1.8); - global $CONFIG; - - if (!isset($CONFIG->widgets)) { - $CONFIG->widgets = new stdClass; - } - - if (!isset($CONFIG->widgets->contexts)) { - $CONFIG->widgets->contexts = array(); - } - - if (!empty($context)) { - $CONFIG->widgets->contexts[] = $context; - } } /** - * Determines whether or not the current context is using widgets - * - * @return bool Depending on widget status - * @deprecated 1.8 + * Overrides permissions checks when creating widgets for logged out users. + * + * @param string $hook The permissions hook. + * @param string $type The type of entity being created. + * @param string $return Value + * @param mixed $params Params + * @return true|null + * @access private */ -function using_widgets() { - elgg_deprecated_notice("using_widgets is deprecated", 1.8); - global $CONFIG; - - $context = elgg_get_context(); - if (isset($CONFIG->widgets->contexts) && is_array($CONFIG->widgets->contexts)) { - if (in_array($context, $CONFIG->widgets->contexts)) { - return true; - } +function elgg_default_widgets_permissions_override($hook, $type, $return, $params) { + if ($type == 'object' && $params['subtype'] == 'widget') { + return elgg_in_context('create_default_widgets') ? true : null; } - return false; + return null; } -/** - * @deprecated 1.8 - */ -function display_widget(ElggObject $widget) { - elgg_deprecated_notice("display_widget() was been deprecated. Use elgg_view_entity().", 1.8); - return elgg_view_entity($widget); -} +elgg_register_event_handler('init', 'system', 'elgg_widgets_init'); +// register default widget hooks from plugins +elgg_register_event_handler('ready', 'system', 'elgg_default_widgets_init'); diff --git a/engine/lib/xml-rpc.php b/engine/lib/xml-rpc.php index 6c1084f70..bfe1a8645 100644 --- a/engine/lib/xml-rpc.php +++ b/engine/lib/xml-rpc.php @@ -15,6 +15,7 @@ * @param array $parameters An array of params * * @return array + * @access private */ function xmlrpc_parse_params($parameters) { $result = array(); @@ -32,6 +33,7 @@ function xmlrpc_parse_params($parameters) { * @param XMLObject $object And object * * @return mixed + * @access private */ function xmlrpc_scalar_value($object) { if ($object->name == 'param') { @@ -86,6 +88,7 @@ function xmlrpc_scalar_value($object) { // Functions for adding handlers ////////////////////////////////////////////////////////// /** XML-RPC Handlers */ +global $XML_RPC_HANDLERS; $XML_RPC_HANDLERS = array(); /** @@ -109,6 +112,7 @@ function register_xmlrpc_handler($method, $handler) { * @param XMLRPCCall $parameters The call and parameters. * * @return XMLRPCCall + * @access private */ function trigger_xmlrpc_handler(XMLRPCCall $parameters) { global $XML_RPC_HANDLERS; @@ -147,6 +151,7 @@ function trigger_xmlrpc_handler(XMLRPCCall $parameters) { * @param array $vars Vars * * @return void + * @access private */ function _php_xmlrpc_error_handler($errno, $errmsg, $filename, $linenum, $vars) { $error = date("Y-m-d H:i:s (T)") . ": \"" . $errmsg . "\" in file " @@ -176,6 +181,7 @@ function _php_xmlrpc_error_handler($errno, $errmsg, $filename, $linenum, $vars) * @param Exception $exception The exception * * @return void + * @access private */ function _php_xmlrpc_exception_handler($exception) { diff --git a/engine/lib/xml.php b/engine/lib/xml.php index 0d0d83da0..497459d83 100644 --- a/engine/lib/xml.php +++ b/engine/lib/xml.php @@ -101,47 +101,11 @@ function serialise_array_to_xml(array $data, $n = 0) { /** * Parse an XML file into an object. - * Based on code from http://de.php.net/manual/en/function.xml-parse-into-struct.php by - * efredricksen at gmail dot com * * @param string $xml The XML * - * @return object + * @return ElggXMLElement */ function xml_to_object($xml) { - $parser = xml_parser_create(); - - // Parse $xml into a structure - xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); - xml_parse_into_struct($parser, $xml, $tags); - - xml_parser_free($parser); - - $elements = array(); - $stack = array(); - - foreach ($tags as $tag) { - $index = count($elements); - - if ($tag['type'] == "complete" || $tag['type'] == "open") { - $elements[$index] = new XmlElement; - $elements[$index]->name = $tag['tag']; - $elements[$index]->attributes = $tag['attributes']; - $elements[$index]->content = $tag['value']; - - if ($tag['type'] == "open") { - $elements[$index]->children = array(); - $stack[count($stack)] = &$elements; - $elements = &$elements[$index]->children; - } - } - - if ($tag['type'] == "close") { - $elements = &$stack[count($stack) - 1]; - unset($stack[count($stack) - 1]); - } - } - - return $elements[0]; + return new ElggXMLElement($xml); } |
