diff options
| author | Silvio Rhatto <rhatto@riseup.net> | 2014-03-14 21:25:01 -0300 | 
|---|---|---|
| committer | Silvio Rhatto <rhatto@riseup.net> | 2014-03-14 21:25:01 -0300 | 
| commit | 3651c99a195685f3a868e159e72c4daf8cb371d3 (patch) | |
| tree | cb004dd7b6ca55215a2c20112fe0c5209d98c18e /engine/lib | |
| parent | 97e689213ff4e829f251af526ed4e796a3cc2b71 (diff) | |
| parent | c2707bb867ddb285af85d7a0e75db26ef692d68c (diff) | |
| download | elgg-3651c99a195685f3a868e159e72c4daf8cb371d3.tar.gz elgg-3651c99a195685f3a868e159e72c4daf8cb371d3.tar.bz2 | |
Merge branch 'master' into saravea
Conflicts:
	mod/blog/views/default/blog/sidebar/archives.php
Diffstat (limited to 'engine/lib')
45 files changed, 708 insertions, 286 deletions
| diff --git a/engine/lib/actions.php b/engine/lib/actions.php index f78ca63df..8047914ac 100644 --- a/engine/lib/actions.php +++ b/engine/lib/actions.php @@ -74,8 +74,7 @@ function action($action, $forwarder = "") {  	);  	if (!in_array($action, $exceptions)) { -		// All actions require a token. -		action_gatekeeper(); +		action_gatekeeper($action);  	}  	$forwarder = str_replace(elgg_get_site_url(), "", $forwarder); @@ -188,6 +187,26 @@ function elgg_unregister_action($action) {  }  /** + * 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 @@ -205,8 +224,6 @@ function elgg_unregister_action($action) {   * @access private   */  function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL) { -	global $CONFIG; -  	if (!$token) {  		$token = get_input('__elgg_token');  	} @@ -215,29 +232,18 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL)  		$ts = get_input('__elgg_ts');  	} -	if (!isset($CONFIG->action_token_timeout)) { -		// default to 2 hours -		$timeout = 2; -	} else { -		$timeout = $CONFIG->action_token_timeout; -	} -  	$session_id = session_id();  	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; -			$timeout = $timeout * $hour; -			$now = time(); - -			// Validate time to ensure its not crazy -			if ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout)) { +		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( @@ -293,12 +299,33 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL)   * 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() { -	if (validate_action_token()) { -		return TRUE; +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(REFERER, 'csrf'); @@ -337,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;  	} @@ -373,6 +403,26 @@ function get_site_secret() {  }  /** + * Get the strength of the site secret + * + * @return string "strong", "moderate", or "weak" + * @access private + */ +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'; +} + +/**   * Check if an action is registered and its script exists.   *   * @param string $action Action name diff --git a/engine/lib/admin.php b/engine/lib/admin.php index ec19a5476..f36f29668 100644 --- a/engine/lib/admin.php +++ b/engine/lib/admin.php @@ -134,11 +134,11 @@ function elgg_delete_admin_notice($id) {  }  /** - * List all admin messages. + * Get admin notices. An admin must be logged in since the notices are private.   *   * @param int $limit Limit   * - * @return array List of admin notices + * @return array Array of admin notices   * @since 1.8.0   */  function elgg_get_admin_notices($limit = 10) { @@ -158,11 +158,13 @@ function elgg_get_admin_notices($limit = 10) {   * @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;  } @@ -234,6 +236,7 @@ function admin_init() {  	elgg_register_action('admin/site/update_advanced', '', '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'); @@ -289,6 +292,7 @@ function admin_init() {  	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 @@ -468,14 +472,18 @@ function admin_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") || elgg_view_exists("plugins/{$page[1]}/settings"))) { +	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/plugin_settings'; -		$plugin = elgg_get_plugin_from_id($page[1]); -		$vars['plugin'] = $plugin; +			$view = 'admin/plugin_settings'; +			$plugin = elgg_get_plugin_from_id($page[1]); +			$vars['plugin'] = $plugin; -		$title = elgg_echo("admin:{$page[0]}"); +			$title = elgg_echo("admin:{$page[0]}"); +		} else { +			forward('', '404'); +		}  	} else {  		$view = 'admin/' . implode('/', $page);  		$title = elgg_echo("admin:{$page[0]}"); diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php index bd5ea1a1f..5e9b530de 100644 --- a/engine/lib/annotations.php +++ b/engine/lib/annotations.php @@ -224,7 +224,7 @@ function elgg_get_annotations(array $options = array()) {   *          annotation_name(s), annotation_value(s), or guid(s) must be set.   *   * @param array $options An options array. {@See elgg_get_annotations()} - * @return mixed Null if the metadata name is invalid. Bool on success or fail. + * @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) { @@ -242,16 +242,20 @@ function elgg_delete_annotations(array $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 mixed + * @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();  	$options['metastring_type'] = 'annotations'; -	return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', false); +	return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset);  }  /** @@ -259,8 +263,11 @@ function elgg_disable_annotations(array $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 mixed + * @return bool|null true on success, false on failure, null if no metadata enabled.   * @since 1.8.0   */  function elgg_enable_annotations(array $options) { @@ -416,8 +423,8 @@ function elgg_list_entities_from_annotations($options = array()) {  function elgg_get_entities_from_annotation_calculation($options) {  	$db_prefix = elgg_get_config('dbprefix');  	$defaults = array( -		'calculation'	=>	'sum', -		'order_by'		=>	'annotation_calculation desc' +		'calculation' => 'sum', +		'order_by' => 'annotation_calculation desc'  	);  	$options = array_merge($defaults, $options); @@ -454,6 +461,12 @@ function elgg_get_entities_from_annotation_calculation($options) {   * @return string   */  function elgg_list_entities_from_annotation_calculation($options) { +	$defaults = array( +		'calculation' => 'sum', +		'order_by' => 'annotation_calculation desc' +	); +	$options = array_merge($defaults, $options); +  	return elgg_list_entities($options, 'elgg_get_entities_from_annotation_calculation');  } @@ -532,15 +545,16 @@ function elgg_annotation_exists($entity_guid, $annotation_type, $owner_guid = NU  		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;  	} diff --git a/engine/lib/cache.php b/engine/lib/cache.php index 59359124e..3116c1a9b 100644 --- a/engine/lib/cache.php +++ b/engine/lib/cache.php @@ -208,6 +208,7 @@ 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 { diff --git a/engine/lib/configuration.php b/engine/lib/configuration.php index a0f297f0c..55e5bbd36 100644 --- a/engine/lib/configuration.php +++ b/engine/lib/configuration.php @@ -486,9 +486,9 @@ function get_config($name, $site_guid = 0) {  	// @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"; +		//	$msg = "Config value $name has been renamed as $new_name";  		$name = $new_name; -	//	elgg_deprecated_notice($msg, $dep_version); +		//	elgg_deprecated_notice($msg, $dep_version);  	}  	// decide from where to return the value diff --git a/engine/lib/database.php b/engine/lib/database.php index 2b348366d..a7949788d 100644 --- a/engine/lib/database.php +++ b/engine/lib/database.php @@ -12,17 +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.   * - * @warning be array this var may be an array or ElggStaticVariableCache depending on when called :( + * @warning Elgg used to set this as an empty array to turn off the cache   * - * @global ElggStaticVariableCache|array $DB_QUERY_CACHE + * @global ElggLRUCache|null $DB_QUERY_CACHE + * @access private   */  global $DB_QUERY_CACHE; -$DB_QUERY_CACHE = array(); +$DB_QUERY_CACHE = null;  /**   * Queries to be executed upon shutdown. @@ -40,6 +42,7 @@ $DB_QUERY_CACHE = array();   * </code>   *   * @global array $DB_DELAYED_QUERIES + * @access private   */  global $DB_DELAYED_QUERIES;  $DB_DELAYED_QUERIES = array(); @@ -51,6 +54,7 @@ $DB_DELAYED_QUERIES = array();   * $dblink as $dblink[$name] => resource.  Use get_db_link($name) to retrieve it.   *   * @global resource[] $dblink + * @access private   */  global $dblink;  $dblink = array(); @@ -61,6 +65,7 @@ $dblink = array();   * Each call to the database increments this counter.   *   * @global integer $dbcalls + * @access private   */  global $dbcalls;  $dbcalls = 0; @@ -123,9 +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)) { -		// @todo everywhere else this is assigned to array(), making it dangerous to call -		// object methods on this. We should consider making this an plain array -		$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);  	}  } @@ -395,16 +399,14 @@ function elgg_query_runner($query, $callback = null, $single = false) {  	// 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. -	// http://trac.elgg.org/ticket/4049 +	// https://github.com/elgg/elgg/issues/4049  	$hash = (string)$callback . (int)$single . $query;  	// Is cached?  	if ($DB_QUERY_CACHE) { -		$cached_query = $DB_QUERY_CACHE[$hash]; - -		if ($cached_query !== FALSE) { +		if (isset($DB_QUERY_CACHE[$hash])) {  			elgg_log("DB query $query results returned from cache (hash: $hash)", 'NOTICE'); -			return $cached_query; +			return $DB_QUERY_CACHE[$hash];  		}  	} @@ -456,19 +458,12 @@ function elgg_query_runner($query, $callback = null, $single = false) {   * @access private   */  function insert_data($query) { -	global $DB_QUERY_CACHE;  	elgg_log("DB query $query", 'NOTICE');  	$dblink = get_db_link('write'); -	// Invalidate query cache -	if ($DB_QUERY_CACHE) { -		/* @var ElggStaticVariableCache $DB_QUERY_CACHE */ -		$DB_QUERY_CACHE->clear(); -	} - -	elgg_log("Query cache invalidated", 'NOTICE'); +	_elgg_invalidate_query_cache();  	if (execute_query("$query", $dblink)) {  		return mysql_insert_id($dblink); @@ -478,7 +473,7 @@ 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}.   * @@ -488,18 +483,12 @@ function insert_data($query) {   * @access private   */  function update_data($query) { -	global $DB_QUERY_CACHE;  	elgg_log("DB query $query", 'NOTICE');  	$dblink = get_db_link('write'); -	// Invalidate query cache -	if ($DB_QUERY_CACHE) { -		/* @var ElggStaticVariableCache $DB_QUERY_CACHE */ -		$DB_QUERY_CACHE->clear(); -		elgg_log("Query cache invalidated", 'NOTICE'); -	} +	_elgg_invalidate_query_cache();  	if (execute_query("$query", $dblink)) {  		return TRUE; @@ -509,7 +498,7 @@ 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}.   * @@ -519,18 +508,12 @@ function update_data($query) {   * @access private   */  function delete_data($query) { -	global $DB_QUERY_CACHE;  	elgg_log("DB query $query", 'NOTICE');  	$dblink = get_db_link('write'); -	// Invalidate query cache -	if ($DB_QUERY_CACHE) { -		/* @var ElggStaticVariableCache $DB_QUERY_CACHE */ -		$DB_QUERY_CACHE->clear(); -		elgg_log("Query cache invalidated", 'NOTICE'); -	} +	_elgg_invalidate_query_cache();  	if (execute_query("$query", $dblink)) {  		return mysql_affected_rows($dblink); @@ -539,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 @@ -669,7 +668,7 @@ function run_sql_script($scriptlocation) {  /**   * Format a query string for logging - *  + *   * @param string $query Query string   * @return string   * @access private diff --git a/engine/lib/deprecated-1.7.php b/engine/lib/deprecated-1.7.php index 519eea89d..ee95b5611 100644 --- a/engine/lib/deprecated-1.7.php +++ b/engine/lib/deprecated-1.7.php @@ -1137,6 +1137,7 @@ function make_register_object($register_name, $register_value, $children_array =   * @param int $guid GUID   *   * @return 1 + * @deprecated 1.7   */  function delete_object_entity($guid) {  	system_message(elgg_echo('deprecatedfunction', array('delete_user_entity'))); @@ -1154,6 +1155,7 @@ function delete_object_entity($guid) {   * @param int $guid User GUID   *   * @return 1 + * @deprecated 1.7   */  function delete_user_entity($guid) {  	system_message(elgg_echo('deprecatedfunction', array('delete_user_entity'))); diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php index 2b4ffcc4f..91068d047 100644 --- a/engine/lib/deprecated-1.8.php +++ b/engine/lib/deprecated-1.8.php @@ -3414,6 +3414,7 @@ function list_annotations($entity_guid, $name = "", $limit = 25, $asc = true) {   * @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, @@ -4667,6 +4668,7 @@ function display_widget(ElggObject $widget) {   *   * @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); @@ -4772,3 +4774,47 @@ function default_page_handler($page, $handler) {  	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/elgglib.php b/engine/lib/elgglib.php index 74b70f9fb..34111c69d 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -93,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();  	} @@ -113,6 +120,8 @@ function elgg_load_library($name) {  		);  		throw new InvalidParameterException($error);  	} + +	$loaded_libraries[] = $name;  }  /** @@ -128,7 +137,7 @@ function elgg_load_library($name) {   * @throws SecurityException   */  function forward($location = "", $reason = 'system') { -	if (!headers_sent()) { +	if (!headers_sent($file, $line)) {  		if ($location === REFERER) {  			$location = $_SERVER['HTTP_REFERER'];  		} @@ -147,7 +156,7 @@ function forward($location = "", $reason = 'system') {  			exit;  		}  	} else { -		throw new SecurityException(elgg_echo('SecurityException:ForwardFailedToRedirect')); +		throw new SecurityException(elgg_echo('SecurityException:ForwardFailedToRedirect', array($file, $line)));  	}  } @@ -737,7 +746,7 @@ function elgg_unregister_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. @@ -1185,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); @@ -1336,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'); @@ -1383,8 +1397,8 @@ function elgg_http_build_url(array $parts, $html_encode = TRUE) {   * add tokens to the action.  The form view automatically handles   * tokens.   * - * @param string  $url         Full action URL - * @param bool $html_encode HTML encode the url? (default: false) + * @param string $url         Full action URL + * @param bool   $html_encode HTML encode the url? (default: false)   *   * @return string URL with action tokens   * @since 1.7.0 @@ -1446,7 +1460,7 @@ function elgg_http_remove_url_query_element($url, $element) {   * Adds an element or elements to a URL's query string.   *   * @param string $url      The URL - * @param array $elements Key/value pairs to add to the URL + * @param array  $elements Key/value pairs to add to the URL   *   * @return string The new URL with the query strings added   * @since 1.7.0 @@ -2233,6 +2247,9 @@ function elgg_api_test($hook, $type, $value, $params) {  /**#@+   * 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   */  define('ACCESS_DEFAULT', -1); diff --git a/engine/lib/entities.php b/engine/lib/entities.php index 156eec040..4fcf1c657 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -17,6 +17,15 @@ global $ENTITY_CACHE;  $ENTITY_CACHE = array();  /** + * GUIDs of entities banned from the entity cache (during this request) + * + * @global array $ENTITY_CACHE_DISABLED_GUIDS + * @access private + */ +global $ENTITY_CACHE_DISABLED_GUIDS; +$ENTITY_CACHE_DISABLED_GUIDS = array(); + +/**   * Cache subtypes and related class names.   *   * @global array|null $SUBTYPE_CACHE array once populated from DB, initially null @@ -26,14 +35,42 @@ 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.   *   * @param int $guid The entity guid   * - * @return null + * @return void   * @access private   */ -function invalidate_cache_for_entity($guid) { +function _elgg_invalidate_cache_for_entity($guid) {  	global $ENTITY_CACHE;  	$guid = (int)$guid; @@ -50,14 +87,14 @@ function invalidate_cache_for_entity($guid) {   *   * @param ElggEntity $entity Entity to cache   * - * @return null - * @see retrieve_cached_entity() - * @see invalidate_cache_for_entity() + * @return void + * @see _elgg_retrieve_cached_entity() + * @see _elgg_invalidate_cache_for_entity()   * @access private - * TODO(evan): Use an ElggCache object + * @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;  	// 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. @@ -65,8 +102,13 @@ function cache_entity(ElggEntity $entity) {  		return;  	} +	$guid = $entity->getGUID(); +	if (isset($ENTITY_CACHE_DISABLED_GUIDS[$guid])) { +		return; +	} +  	// Don't store too many or we'll have memory problems -	// TODO(evan): Pick a less arbitrary limit +	// @todo Pick a less arbitrary limit  	if (count($ENTITY_CACHE) > 256) {  		$random_guid = array_rand($ENTITY_CACHE); @@ -79,7 +121,7 @@ function cache_entity(ElggEntity $entity) {  		elgg_get_metadata_cache()->clear($random_guid);  	} -	$ENTITY_CACHE[$entity->guid] = $entity; +	$ENTITY_CACHE[$guid] = $entity;  }  /** @@ -88,11 +130,11 @@ function cache_entity(ElggEntity $entity) {   * @param int $guid The guid   *   * @return ElggEntity|bool false if entity not cached, or not fully loaded - * @see cache_entity() - * @see invalidate_cache_for_entity() + * @see _elgg_cache_entity() + * @see _elgg_invalidate_cache_for_entity()   * @access private   */ -function retrieve_cached_entity($guid) { +function _elgg_retrieve_cached_entity($guid) {  	global $ENTITY_CACHE;  	if (isset($ENTITY_CACHE[$guid])) { @@ -105,31 +147,6 @@ function retrieve_cached_entity($guid) {  }  /** - * As retrieve_cached_entity, but returns the result as a stdClass - * (compatible with load functions that expect a database row.) - * - * @param int $guid The guid - * - * @return mixed - * @todo unused - * @access private - */ -function retrieve_cached_entity_row($guid) { -	$obj = retrieve_cached_entity($guid); -	if ($obj) { -		$tmp = new stdClass; - -		foreach ($obj as $k => $v) { -			$tmp->$k = $v; -		} - -		return $tmp; -	} - -	return false; -} - -/**   * Return the id for a given subtype.   *   * ElggEntity objects have a type and a subtype.  Subtypes @@ -432,7 +449,7 @@ function update_subtype($type, $subtype, $class = '') {   * @param int $time_created   The time creation timestamp   *   * @return bool - * @link http://docs.elgg.org/DataModel/Entities + * @throws InvalidParameterException   * @access private   */  function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $time_created = null) { @@ -455,6 +472,10 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $  		$time_created = (int) $time_created;  	} +	if ($access_id == ACCESS_DEFAULT) { +		throw new InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h'); +	} +  	if ($entity && $entity->canEdit()) {  		if (elgg_trigger_event('update', $entity->type, $entity)) {  			$ret = update_data("UPDATE {$CONFIG->dbprefix}entities @@ -581,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; @@ -590,6 +610,10 @@ $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_guid = elgg_get_logged_in_user_guid();  	if (!can_write_to_container($user_guid, $owner_guid, $type, $subtype)) { @@ -737,7 +761,7 @@ function get_entity($guid) {  	// @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 },  +	// 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') { @@ -745,7 +769,7 @@ function get_entity($guid) {  	}  	// Check local cache first -	$new_entity = retrieve_cached_entity($guid); +	$new_entity = _elgg_retrieve_cached_entity($guid);  	if ($new_entity) {  		return $new_entity;  	} @@ -767,7 +791,7 @@ function get_entity($guid) {  	if ($shared_cache) {  		$cached_entity = $shared_cache->load($guid); -		// @todo store ACLs in memcache http://trac.elgg.org/ticket/3018#comment:3 +		// @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; @@ -782,7 +806,7 @@ function get_entity($guid) {  	}  	if ($new_entity) { -		cache_entity($new_entity); +		_elgg_cache_entity($new_entity);  	}  	return $new_entity;  } @@ -909,6 +933,8 @@ function elgg_get_entities(array $options = array()) {  		'joins'					=>	array(),  		'callback'				=> 'entity_row_to_elggstar', + +		'__ElggBatch'			=> null,  	);  	$options = array_merge($defaults, $options); @@ -1026,7 +1052,7 @@ function elgg_get_entities(array $options = array()) {  		}  		if ($options['callback'] === 'entity_row_to_elggstar') { -			$dt = _elgg_fetch_entities_from_sql($query); +			$dt = _elgg_fetch_entities_from_sql($query, $options['__ElggBatch']);  		} else {  			$dt = get_data($query, $options['callback']);  		} @@ -1037,7 +1063,7 @@ function elgg_get_entities(array $options = 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) { -					cache_entity($item); +					_elgg_cache_entity($item);  					// plugins usually have only settings  					if (!$item instanceof ElggPlugin) {  						$guids[] = $item->guid; @@ -1061,13 +1087,14 @@ function elgg_get_entities(array $options = array()) {  /**   * Return entities from an SQL query generated by elgg_get_entities.   * - * @param string $sql + * @param string    $sql + * @param ElggBatch $batch   * @return ElggEntity[]   *   * @access private   * @throws LogicException   */ -function _elgg_fetch_entities_from_sql($sql) { +function _elgg_fetch_entities_from_sql($sql, ElggBatch $batch = null) {  	static $plugin_subtype;  	if (null === $plugin_subtype) {  		$plugin_subtype = get_subtype_id('object', 'plugin'); @@ -1102,7 +1129,7 @@ function _elgg_fetch_entities_from_sql($sql) {  		if (empty($row->guid) || empty($row->type)) {  			throw new LogicException('Entity row missing guid or type');  		} -		if ($entity = retrieve_cached_entity($row->guid)) { +		if ($entity = _elgg_retrieve_cached_entity($row->guid)) {  			$rows[$i] = $entity;  			continue;  		} @@ -1144,6 +1171,11 @@ function _elgg_fetch_entities_from_sql($sql) {  			} 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); +				}  			}  		}  	} @@ -1441,8 +1473,10 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti  	global $autofeed;  	$autofeed = true; +	$offset_key = isset($options['offset_key']) ? $options['offset_key'] : 'offset'; +  	$defaults = array( -		'offset' => (int) max(get_input('offset', 0), 0), +		'offset' => (int) max(get_input($offset_key, 0), 0),  		'limit' => (int) max(get_input('limit', 10), 0),  		'full_view' => TRUE,  		'list_type_toggle' => FALSE, @@ -1628,7 +1662,7 @@ function disable_entity($guid, $reason = "", $recursive = true) {  				$entity->disableMetadata();  				$entity->disableAnnotations(); -				invalidate_cache_for_entity($guid); +				_elgg_invalidate_cache_for_entity($guid);  				$res = update_data("UPDATE {$CONFIG->dbprefix}entities  					SET enabled = 'no' @@ -1644,8 +1678,8 @@ 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 bool $recursive Recursively enable all entities disabled with the entity? @@ -1726,7 +1760,7 @@ 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 @@ -1773,6 +1807,10 @@ function delete_entity($guid, $recursive = true) {  					elgg_set_ignore_access($ia);  				} +				$entity_disable_override = access_get_show_hidden_status(); +				access_show_hidden_entities(true); +				$ia = elgg_set_ignore_access(true); +  				// Now delete the entity itself  				$entity->deleteMetadata();  				$entity->deleteOwnedMetadata(); @@ -1780,6 +1818,9 @@ function delete_entity($guid, $recursive = true) {  				$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); @@ -2087,7 +2128,7 @@ 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)) { @@ -2488,11 +2529,18 @@ function update_entity_last_action($guid, $posted = NULL) {  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})");  	}  } diff --git a/engine/lib/extender.php b/engine/lib/extender.php index 8756e051b..8323bd3ce 100644 --- a/engine/lib/extender.php +++ b/engine/lib/extender.php @@ -126,14 +126,20 @@ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params)   * @return bool   */  function can_edit_extender($extender_id, $type, $user_guid = 0) { -	if (!elgg_is_logged_in()) { -		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 = elgg_get_logged_in_user_entity(); +		$user_guid = elgg_get_logged_in_user_guid();  	}  	$functionname = "elgg_get_{$type}_from_id"; @@ -149,16 +155,16 @@ function can_edit_extender($extender_id, $type, $user_guid = 0) {  	/* @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 +	// 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);  } diff --git a/engine/lib/group.php b/engine/lib/group.php index 624029d98..6ded8a825 100644 --- a/engine/lib/group.php +++ b/engine/lib/group.php @@ -240,9 +240,11 @@ 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);  } diff --git a/engine/lib/input.php b/engine/lib/input.php index 2d9bae4dd..80b0b8766 100644 --- a/engine/lib/input.php +++ b/engine/lib/input.php @@ -60,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   */ diff --git a/engine/lib/languages.php b/engine/lib/languages.php index 17db14d98..61ba91ddb 100644 --- a/engine/lib/languages.php +++ b/engine/lib/languages.php @@ -139,6 +139,9 @@ function get_language() {  	return false;  } +/** + * @access private + */  function _elgg_load_translations() {  	global $CONFIG; diff --git a/engine/lib/location.php b/engine/lib/location.php index b319bb3bb..1534c7d7b 100644 --- a/engine/lib/location.php +++ b/engine/lib/location.php @@ -139,7 +139,7 @@ function elgg_get_entities_from_location(array $options = array()) {  /**   * 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() diff --git a/engine/lib/memcache.php b/engine/lib/memcache.php index f79fba4a9..79b87e850 100644 --- a/engine/lib/memcache.php +++ b/engine/lib/memcache.php @@ -35,3 +35,23 @@ 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 305e9918b..fdb1b85f6 100644 --- a/engine/lib/metadata.php +++ b/engine/lib/metadata.php @@ -191,19 +191,19 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i  	}  	// 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 name_id='$name', value_id='$value', value_type='$value_type', access_id=$access_id," +		. " 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); @@ -300,10 +300,8 @@ function elgg_get_metadata(array $options = array()) {   *          This requires at least one constraint: metadata_owner_guid(s),   *          metadata_name(s), metadata_value(s), or guid(s) must be set.   * - * @warning This returns null on no ops. - *   * @param array $options An options array. {@see elgg_get_metadata()} - * @return mixed Null if the metadata name is invalid. Bool on success or fail. + * @return bool|null true on success, false on failure, null if no metadata to delete.   * @since 1.8.0   */  function elgg_delete_metadata(array $options) { @@ -325,10 +323,8 @@ function elgg_delete_metadata(array $options) {   *   * @warning Unlike elgg_get_metadata() this will not accept an empty options array!   * - * @warning This returns null on no ops. - *   * @param array $options An options array. {@See elgg_get_metadata()} - * @return mixed + * @return bool|null true on success, false on failure, null if no metadata disabled.   * @since 1.8.0   */  function elgg_disable_metadata(array $options) { @@ -337,9 +333,13 @@ function elgg_disable_metadata(array $options) {  	}  	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();  	$options['metastring_type'] = 'metadata'; -	return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', false); +	return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset);  }  /** @@ -347,10 +347,11 @@ function elgg_disable_metadata(array $options) {   *   * @warning Unlike elgg_get_metadata() this will not accept an empty options array!   * - * @warning This returns null on no ops. + * @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 mixed + * @return bool|null true on success, false on failure, null if no metadata enabled.   * @since 1.8.0   */  function elgg_enable_metadata(array $options) { @@ -405,9 +406,11 @@ function elgg_enable_metadata(array $options) {   *                                         '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 @@ -619,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(); @@ -920,8 +925,8 @@ function elgg_get_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 + * @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 diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php index f49b4a163..57d876c06 100644 --- a/engine/lib/metastrings.php +++ b/engine/lib/metastrings.php @@ -421,9 +421,11 @@ function elgg_get_metastring_based_objects($options) {  	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) { +	if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {  		$selects = array_unique($selects);  		// evalutate selects  		$select_str = ''; @@ -434,6 +436,9 @@ function elgg_get_metastring_based_objects($options) {  		}  		$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";  	} @@ -462,7 +467,7 @@ function elgg_get_metastring_based_objects($options) {  			$defaults['order_by']);  	} -	if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE) { +	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']}"; @@ -510,7 +515,7 @@ function elgg_get_metastring_sql($table, $names = null, $values = null,  		&& !$ids  		&& (!$pairs && $pairs !== 0)) { -		return ''; +		return array();  	}  	$db_prefix = elgg_get_config('dbprefix'); @@ -520,8 +525,6 @@ function elgg_get_metastring_sql($table, $names = null, $values = null,  	// only supported on values.  	$binary = ($case_sensitive) ? ' BINARY ' : ''; -	$access = get_access_sql_suffix($table); -  	$return = array (  		'joins' => array (),  		'wheres' => array() @@ -586,13 +589,15 @@ function elgg_get_metastring_sql($table, $names = null, $values = null,  	}  	if ($names_where && $values_where) { -		$wheres[] = "($names_where AND $values_where AND $access)"; +		$wheres[] = "($names_where AND $values_where)";  	} elseif ($names_where) { -		$wheres[] = "($names_where AND $access)"; +		$wheres[] = $names_where;  	} elseif ($values_where) { -		$wheres[] = "($values_where AND $access)"; +		$wheres[] = $values_where;  	} +	$wheres[] = get_access_sql_suffix($table); +  	if ($where = implode(' AND ', $wheres)) {  		$return['wheres'][] = "($where)";  	} diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php index 118a7214c..ab9cc05e8 100644 --- a/engine/lib/navigation.php +++ b/engine/lib/navigation.php @@ -218,7 +218,7 @@ function elgg_push_breadcrumb($title, $link = NULL) {  	}  	// avoid key collisions. -	$CONFIG->breadcrumbs[] = array('title' => $title, 'link' => $link); +	$CONFIG->breadcrumbs[] = array('title' => elgg_get_excerpt($title, 100), 'link' => $link);  }  /** @@ -323,7 +323,8 @@ function elgg_site_menu_setup($hook, $type, $return, $params) {  	}  	if (!$selected) { -		// nothing selected, match name to context +		// 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 @@ -332,6 +333,10 @@ function elgg_site_menu_setup($hook, $type, $return, $params) {  						$return[$section_name][$key]->setSelected(true);  						break 2;  					} +					if ($item->getHref() == $current_url) { +						$return[$section_name][$key]->setSelected(true); +						break 2; +					}  				}  			}  		} diff --git a/engine/lib/notification.php b/engine/lib/notification.php index b6399b3c6..be0c359d4 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -110,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; +						}  					}  				}  			} @@ -165,7 +168,7 @@ 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; diff --git a/engine/lib/opendd.php b/engine/lib/opendd.php index f00ea6aab..7d635a295 100644 --- a/engine/lib/opendd.php +++ b/engine/lib/opendd.php @@ -7,6 +7,8 @@   * @version 0.4   */ +// @codingStandardsIgnoreStart +  /**   * Attempt to construct an ODD object out of a XmlElement or sub-elements.   * @@ -103,3 +105,5 @@ function ODD_Import($xml) {  function ODD_Export(ODDDocument $document) {  	return "$document";  } + +// @codingStandardsIgnoreEnd diff --git a/engine/lib/output.php b/engine/lib/output.php index da8e1ab86..de4f911fb 100644 --- a/engine/lib/output.php +++ b/engine/lib/output.php @@ -13,28 +13,33 @@   * @param string $text The input string   *   * @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 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\">$urltext</a>$period"; +			return "<a href=\"$url\" rel=\"nofollow\">$urltext</a>$punc";  		'  	), $text); @@ -284,11 +289,9 @@ function elgg_get_friendly_title($title) {  		return $result;  	} -	// handle some special cases -	$title = str_replace('&', 'and', $title); -	// quotes and angle brackets stored in the database as html encoded -	$title = htmlspecialchars_decode($title); - +	// titles are often stored HTML encoded +	$title = html_entity_decode($title, ENT_QUOTES, 'UTF-8'); +	  	$title = ElggTranslit::urlize($title);  	return $title; @@ -418,6 +421,25 @@ function _elgg_html_decode($string) {  }  /** + * Prepares query string for output to prevent CSRF attacks. + *  + * @param string $string + * @return string + * + * @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  $hook   unit_test diff --git a/engine/lib/pageowner.php b/engine/lib/pageowner.php index 7fd79e68a..4aaffc160 100644 --- a/engine/lib/pageowner.php +++ b/engine/lib/pageowner.php @@ -29,7 +29,9 @@ function elgg_get_page_owner_guid($guid = 0) {  	// 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;  } diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index f281b1416..d5d3db466 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -312,10 +312,10 @@ function elgg_is_active_plugin($plugin_id, $site_guid = null) {   */  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; +	$start_flags = ELGG_PLUGIN_INCLUDE_START | +					ELGG_PLUGIN_REGISTER_VIEWS | +					ELGG_PLUGIN_REGISTER_LANGUAGES | +					ELGG_PLUGIN_REGISTER_CLASSES;  	if (!$plugins_path) {  		return false; @@ -865,7 +865,7 @@ function elgg_set_plugin_user_setting($name, $value, $user_guid = null, $plugin_   * Unsets a user-specific plugin setting   *   * @param string $name      Name of the setting - * @param int $user_guid Defaults to logged in user + * @param int    $user_guid Defaults to logged in user   * @param string $plugin_id Defaults to contextual plugin name   *   * @return bool @@ -1105,6 +1105,49 @@ function plugins_test($hook, $type, $value, $params) {  }  /** + * 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   * @@ -1115,6 +1158,10 @@ 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"); diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php index fe0b8364d..b0cd627fc 100644 --- a/engine/lib/relationships.php +++ b/engine/lib/relationships.php @@ -363,7 +363,7 @@ $relationship_guid = NULL, $inverse_relationship = FALSE) {  /**   * 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() diff --git a/engine/lib/river.php b/engine/lib/river.php index f2ec1e101..e92040eb7 100644 --- a/engine/lib/river.php +++ b/engine/lib/river.php @@ -120,7 +120,7 @@ $posted = 0, $annotation_id = 0) {   *   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 - *  + *   *   posted_time_lower    => INT     The lower bound on the time posted   *   posted_time_upper    => INT     The upper bound on the time posted   * @@ -380,10 +380,10 @@ function _elgg_prefetch_river_entities(array $river_items) {  	// prefetch objects and subjects  	$guids = array();  	foreach ($river_items as $item) { -		if ($item->subject_guid && !retrieve_cached_entity($item->subject_guid)) { +		if ($item->subject_guid && !_elgg_retrieve_cached_entity($item->subject_guid)) {  			$guids[$item->subject_guid] = true;  		} -		if ($item->object_guid && !retrieve_cached_entity($item->object_guid)) { +		if ($item->object_guid && !_elgg_retrieve_cached_entity($item->object_guid)) {  			$guids[$item->object_guid] = true;  		}  	} @@ -402,7 +402,7 @@ function _elgg_prefetch_river_entities(array $river_items) {  	$guids = array();  	foreach ($river_items as $item) {  		$object = $item->getObjectEntity(); -		if ($object->container_guid && !retrieve_cached_entity($object->container_guid)) { +		if ($object->container_guid && !_elgg_retrieve_cached_entity($object->container_guid)) {  			$guids[$object->container_guid] = true;  		}  	} @@ -434,8 +434,13 @@ function elgg_list_river(array $options = array()) {  		'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); @@ -445,6 +450,7 @@ function elgg_list_river(array $options = array()) {  	$options['count'] = $count;  	$options['items'] = $items; +	  	return elgg_view('page/components/list', $options);  } diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php index a34c2045b..e3d5ce9cd 100644 --- a/engine/lib/sessions.php +++ b/engine/lib/sessions.php @@ -87,6 +87,9 @@ function elgg_is_admin_logged_in() {   */  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 @@ -323,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;  } diff --git a/engine/lib/statistics.php b/engine/lib/statistics.php index 0c9a3c945..4cb0bb0b8 100644 --- a/engine/lib/statistics.php +++ b/engine/lib/statistics.php @@ -95,13 +95,17 @@ function get_number_users($show_deactivated = false) {   * @return string    */  function get_online_users() { -	$count = find_active_users(600, 10, 0, true); -	$objects = find_active_users(600, 10); +	$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, array(  			'count' => $count, -			'limit' => 10, +			'limit' => $limit, +			'offset' => $offset  		));  	}  	return ''; diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php index 5a153afb2..84302632e 100644 --- a/engine/lib/system_log.php +++ b/engine/lib/system_log.php @@ -187,7 +187,16 @@ function system_log($object, $event) {  		$object_subtype = $object->getSubtype();  		$event = sanitise_string($event);  		$time = time(); -		$ip_address = sanitise_string($_SERVER['REMOTE_ADDR']); + +		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)) { diff --git a/engine/lib/upgrade.php b/engine/lib/upgrade.php index d684af862..158ec9ec1 100644 --- a/engine/lib/upgrade.php +++ b/engine/lib/upgrade.php @@ -245,7 +245,7 @@ function version_upgrade() {  	// 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. +	// 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 @@ -354,16 +354,12 @@ function _elgg_upgrade_unlock() {   * @access private   */  function _elgg_upgrade_is_locked() { -	global $CONFIG, $DB_QUERY_CACHE; -	 +	global $CONFIG; +  	$is_locked = count(get_data("show tables like '{$CONFIG->dbprefix}upgrade_lock'")); -	 -	// Invalidate query cache -	if ($DB_QUERY_CACHE) { -		/* @var ElggStaticVariableCache $DB_QUERY_CACHE */ -		$DB_QUERY_CACHE->clear(); -		elgg_log("Query cache invalidated", 'NOTICE'); -	} -	 + +	// @todo why? +	_elgg_invalidate_query_cache(); +  	return $is_locked;  } diff --git a/engine/lib/upgrades/2009102801.php b/engine/lib/upgrades/2009102801.php index cab9a6835..3ad113fb2 100644 --- a/engine/lib/upgrades/2009102801.php +++ b/engine/lib/upgrades/2009102801.php @@ -203,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   */  $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 0bffee001..4779295fd 100644 --- a/engine/lib/upgrades/2010033101.php +++ b/engine/lib/upgrades/2010033101.php @@ -1,7 +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. diff --git a/engine/lib/upgrades/2010061501.php b/engine/lib/upgrades/2010061501.php index 9ff7d3102..744c28fd5 100644 --- a/engine/lib/upgrades/2010061501.php +++ b/engine/lib/upgrades/2010061501.php @@ -45,7 +45,7 @@ if ($dbversion < 2009100701) {  			}  		} -		global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE; +		global $ENTITY_CACHE;  		/**  			Upgrade file locations @@ -60,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) { 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/2011052801.php b/engine/lib/upgrades/2011052801.php index 8084bc06c..b5a8e1018 100644 --- a/engine/lib/upgrades/2011052801.php +++ b/engine/lib/upgrades/2011052801.php @@ -2,7 +2,7 @@  /**   * Make sure all users have the relationship member_of_site   */ -global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG; +global $ENTITY_CACHE;  $db_prefix = get_config('dbprefix');  $limit = 100; @@ -17,7 +17,8 @@ $q = "SELECT e.* FROM {$db_prefix}entities e  $users = get_data($q);  while ($users) { -	$DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array(); +	$ENTITY_CACHE = array(); +	_elgg_invalidate_query_cache();  	// do manually to not trigger any events because these aren't new users.  	foreach ($users as $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 index 07732f261..780038c32 100644 --- 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 @@ -3,7 +3,7 @@   * Elgg 1.8.3 upgrade 2012041801   * multiple_user_tokens   * - * Fixes http://trac.elgg.org/ticket/4291 + * Fixes https://github.com/elgg/elgg/issues/4291   * Removes the unique index on users_apisessions for user_guid and site_guid   */ 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 index b38eb5100..8eccf05e2 100644 --- 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 @@ -7,8 +7,6 @@   * This script turns that back into a string.   */ -global $DB_QUERY_CACHE; -  $ia = elgg_set_ignore_access(true);  $options = array(  	'type' => 'user', @@ -17,7 +15,7 @@ $options = array(  $batch = new ElggBatch('elgg_get_entities', $options);  foreach ($batch as $entity) { -	$DB_QUERY_CACHE = array(); +	_elgg_invalidate_query_cache();  	if (is_array($entity->location)) {  		$entity->location = implode(', ', $entity->location); 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/user_settings.php b/engine/lib/user_settings.php index 3466c25f9..0e36dc46d 100644 --- a/engine/lib/user_settings.php +++ b/engine/lib/user_settings.php @@ -308,7 +308,7 @@ function usersettings_page_handler($page) {  		$user = get_user_by_username($page[1]);  		elgg_set_page_owner_guid($user->guid);  	} else { -		$user = elgg_get_logged_in_user_guid(); +		$user = elgg_get_logged_in_user_entity();  		elgg_set_page_owner_guid($user->guid);  	} diff --git a/engine/lib/users.php b/engine/lib/users.php index 4a585c07f..a8fb9121c 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -237,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;  		} @@ -273,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;  		} @@ -553,13 +553,18 @@ 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 @@ -592,9 +597,9 @@ 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 @@ -705,18 +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) { -		$salt = generate_random_cleartext_password(); // Reset the salt -		$user->salt = $salt; +		$ia = elgg_set_ignore_access(); -		$hash = generate_user_password($user, $password); +		$user->salt = generate_random_cleartext_password(); +		$hash = generate_user_password($user, $password);		 +		$user->password = $hash; +		$result = (bool)$user->save(); -		$query = "UPDATE {$CONFIG->dbprefix}users_entity -			set password='$hash', salt='$salt' where guid=$user_guid"; -		return update_data($query); +		elgg_set_ignore_access($ia); + +		return $result;  	}  	return false; @@ -1091,6 +1096,7 @@ function friends_page_handler($segments, $handler) {   * @access private   */  function collections_page_handler($page_elements) { +	gatekeeper();  	elgg_set_context('friends');  	$base = elgg_get_config('path');  	if (isset($page_elements[0])) { diff --git a/engine/lib/views.php b/engine/lib/views.php index cfceccec0..1142461fe 100644 --- a/engine/lib/views.php +++ b/engine/lib/views.php @@ -218,7 +218,7 @@ function elgg_register_ajax_view($view) {  /**   * Unregister a view for ajax calls - *  + *   * @param string $view The view name   * @return void   * @since 1.8.3 @@ -369,7 +369,7 @@ 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. @@ -795,7 +795,7 @@ function elgg_view_menu($menu_name, array $vars = array()) {   *  - 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 @@ -992,6 +992,11 @@ function elgg_view_annotation(ElggAnnotation $annotation, array $vars = array(),  function elgg_view_entity_list($entities, $vars = array(), $offset = 0, $limit = 10, $full_view = true,  $list_type_toggle = true, $pagination = true) { +	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);  	} @@ -1064,8 +1069,13 @@ function elgg_view_annotation_list($annotations, array $vars = array()) {  		'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('page/components/list', $vars);  } @@ -1107,7 +1117,7 @@ function elgg_view_entity_annotations(ElggEntity $entity, $full_view = true) {   * This is a shortcut for {@elgg_view page/elements/title}.   *   * @param string $title The page title - * @param array $vars   View variables (was submenu be displayed? (deprecated)) + * @param array  $vars  View variables (was submenu be displayed? (deprecated))   *   * @return string The HTML (etc)   */ @@ -1179,7 +1189,7 @@ function elgg_view_comments($entity, $add_comment = true, array $vars = array())   *   * @param string $image The icon and other information   * @param string $body  Description content - * @param array $vars   Additional parameters for the view + * @param array  $vars  Additional parameters for the view   *   * @return string   * @since 1.8.0 @@ -1236,15 +1246,17 @@ function elgg_view_river_item($item, array $vars = array()) {  		// 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 http://trac.elgg.org/ticket/4789 -//	else { -//		// hide based on object's container -//		$visibility = ElggGroupItemVisibility::factory($object->container_guid); -//		if ($visibility->shouldHideItems) { -//			return ''; -//		} -//	} +	// 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; @@ -1332,12 +1344,12 @@ function elgg_view_list_item($item, array $vars = array()) {  /**   * 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 = '') { @@ -1636,7 +1648,7 @@ function elgg_views_boot() {  	}  	// set default icon sizes - can be overridden in settings.php or with plugin -	if (!$CONFIG->icon_sizes) { +	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), diff --git a/engine/lib/web_services.php b/engine/lib/web_services.php index b440e3afb..51cad6f39 100644 --- a/engine/lib/web_services.php +++ b/engine/lib/web_services.php @@ -1166,6 +1166,17 @@ function list_all_apis() {   * @access private   */  function auth_gettoken($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) { @@ -1195,7 +1206,7 @@ $ERRORS = array();   *   * @return void   * @access private - *  + *   * @throws Exception   */  function _php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) { | 
