diff options
25 files changed, 767 insertions, 118 deletions
| diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php index 77c2bbf4d..929abceb2 100644 --- a/engine/classes/ElggEntity.php +++ b/engine/classes/ElggEntity.php @@ -248,7 +248,9 @@ abstract class ElggEntity extends ElggData implements  	 * @return mixed The value, or NULL if not found.  	 */  	public function getMetaData($name) { -		if ((int) ($this->guid) == 0) { +		$guid = $this->getGUID(); + +		if (! $guid) {  			if (isset($this->temp_metadata[$name])) {  				// md is returned as an array only if more than 1 entry  				if (count($this->temp_metadata[$name]) == 1) { @@ -261,21 +263,38 @@ abstract class ElggEntity extends ElggData implements  			}  		} +		// upon first cache miss, just load/cache all the metadata and retry. +		// if this works, the rest of this function may not be needed! +		$cache = elgg_get_metadata_cache(); +		if ($cache->isKnown($guid, $name)) { +			return $cache->load($guid, $name); +		} else { +			$cache->populateFromEntities(array($guid)); +			// in case ignore_access was on, we have to check again... +			if ($cache->isKnown($guid, $name)) { +				return $cache->load($guid, $name); +			} +		} +  		$md = elgg_get_metadata(array( -			'guid' => $this->getGUID(), +			'guid' => $guid,  			'metadata_name' => $name,  			'limit' => 0,  		)); +		$value = null; +  		if ($md && !is_array($md)) { -			return $md->value; +			$value = $md->value;  		} elseif (count($md) == 1) { -			return $md[0]->value; +			$value = $md[0]->value;  		} else if ($md && is_array($md)) { -			return metadata_array_to_values($md); +			$value = metadata_array_to_values($md);  		} -		return null; +		$cache->save($guid, $name, $value); + +		return $value;  	}  	/** @@ -1007,7 +1026,7 @@ abstract class ElggEntity extends ElggData implements  	/**  	 * Returns the guid.  	 * -	 * @return int GUID +	 * @return int|null GUID  	 */  	public function getGUID() {  		return $this->get('guid'); @@ -1245,16 +1264,16 @@ abstract class ElggEntity extends ElggData implements  	/**  	 * Save an entity.  	 * -	 * @return bool/int +	 * @return bool|int  	 * @throws IOException  	 */  	public function save() { -		$guid = (int) $this->guid; +		$guid = $this->getGUID();  		if ($guid > 0) {  			cache_entity($this);  			return update_entity( -				$this->get('guid'), +				$guid,  				$this->get('owner_guid'),  				$this->get('access_id'),  				$this->get('container_guid'), @@ -1301,10 +1320,7 @@ abstract class ElggEntity extends ElggData implements  			$this->attributes['subtype'] = get_subtype_id($this->attributes['type'],  				$this->attributes['subtype']); -			// Cache object handle -			if ($this->attributes['guid']) { -				cache_entity($this); -			} +			cache_entity($this);  			return $this->attributes['guid'];  		} diff --git a/engine/classes/ElggMetadata.php b/engine/classes/ElggMetadata.php index 634a122e5..7f45dc3ea 100644 --- a/engine/classes/ElggMetadata.php +++ b/engine/classes/ElggMetadata.php @@ -26,8 +26,6 @@ class ElggMetadata extends ElggExtender {  	 * Construct a metadata object  	 *  	 * @param mixed $id ID of metadata or a database row as stdClass object -	 * -	 * @return void  	 */  	function __construct($id = null) {  		$this->initializeAttributes(); @@ -54,7 +52,7 @@ class ElggMetadata extends ElggExtender {  	 *  	 * @param int $user_guid The GUID of the user (defaults to currently logged in user)  	 * -	 * @return true|false Depending on permissions +	 * @return bool Depending on permissions  	 */  	function canEdit($user_guid = 0) {  		if ($entity = get_entity($this->get('entity_guid'))) { @@ -64,9 +62,11 @@ class ElggMetadata extends ElggExtender {  	}  	/** -	 * Save matadata object +	 * Save metadata object  	 * -	 * @return int the metadata object id +	 * @return int|bool the metadata object id or true if updated +	 * +	 * @throws IOException  	 */  	function save() {  		if ($this->id > 0) { @@ -89,7 +89,13 @@ class ElggMetadata extends ElggExtender {  	 * @return bool  	 */  	function delete() { -		return elgg_delete_metastring_based_object_by_id($this->id, 'metadata'); +		$success = elgg_delete_metastring_based_object_by_id($this->id, 'metadata'); +		if ($success) { +			// we mark unknown here because this deletes only one value +			// under this name, and there may be others remaining. +			elgg_get_metadata_cache()->markUnknown($this->entity_guid, $this->name); +		} +		return $success;  	}  	/** @@ -99,17 +105,27 @@ class ElggMetadata extends ElggExtender {  	 * @since 1.8  	 */  	function disable() { -		return elgg_set_metastring_based_object_enabled_by_id($this->id, 'no', 'metadata'); +		$success = elgg_set_metastring_based_object_enabled_by_id($this->id, 'no', 'metadata'); +		if ($success) { +			// we mark unknown here because this disables only one value +			// under this name, and there may be others remaining. +			elgg_get_metadata_cache()->markUnknown($this->entity_guid, $this->name); +		} +		return $success;  	}  	/** -	 * Disable the metadata +	 * Enable the metadata  	 *  	 * @return bool  	 * @since 1.8  	 */  	function enable() { -		return elgg_set_metastring_based_object_enabled_by_id($this->id, 'yes', 'metadata'); +		$success = elgg_set_metastring_based_object_enabled_by_id($this->id, 'yes', 'metadata'); +		if ($success) { +			elgg_get_metadata_cache()->markUnknown($this->entity_guid, $this->name); +		} +		return $success;  	}  	/** diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index 8c9093834..3e43c8e81 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -101,7 +101,6 @@ class ElggPlugin extends ElggObject {  			$missing_attributes = array_diff_key($expected_attributes, $row);  			if ($missing_attributes) {  				$needs_loaded = true; -				$old_guid = $guid;  				$guid = $row['guid'];  			} else {  				$this->attributes = $row; @@ -132,10 +131,7 @@ class ElggPlugin extends ElggObject {  		// guid needs to be an int  http://trac.elgg.org/ticket/4111  		$this->attributes['guid'] = (int)$this->attributes['guid']; -		// cache the entity -		if ($this->attributes['guid']) { -			cache_entity($this); -		} +		cache_entity($this);  		return true;  	} diff --git a/engine/classes/ElggVolatileMetadataCache.php b/engine/classes/ElggVolatileMetadataCache.php new file mode 100644 index 000000000..24ae58d42 --- /dev/null +++ b/engine/classes/ElggVolatileMetadataCache.php @@ -0,0 +1,344 @@ +<?php +/** + * ElggVolatileMetadataCache + * In memory cache of known metadata values stored by entity. + * + * @package    Elgg.Core + * @subpackage Cache + * + * @access private + */ +class ElggVolatileMetadataCache { + +	/** +	 * The cached values (or null for known to be empty). If the portion of the cache +	 * is synchronized, missing values are assumed to indicate that values do not +	 * exist in storage, otherwise, we don't know what's there. +	 * +	 * @var array +	 */ +	protected $values = array(); + +	/** +	 * Does the cache know that it contains all names fetch-able from storage? +	 * The keys are entity GUIDs and either the value exists (true) or it's not set. +	 * +	 * @var array +	 */ +	protected $isSynchronized = array(); + +	/** +	 * @var null|bool +	 */ +	protected $ignoreAccess = null; + +	/** +	 * @param int $entity_guid +	 * +	 * @param array $values +	 */ +	public function saveAll($entity_guid, array $values) { +		if (!$this->getIgnoreAccess()) { +			$this->values[$entity_guid] = $values; +			$this->isSynchronized[$entity_guid] = true; +		} +	} + +	/** +	 * @param int $entity_guid +	 * +	 * @return array +	 */ +	public function loadAll($entity_guid) { +		if (isset($this->values[$entity_guid])) { +			return $this->values[$entity_guid]; +		} else { +			return array(); +		} +	} + +	/** +	 * Declare that there may be fetch-able metadata names in storage that this +	 * cache doesn't know about +	 * +	 * @param int $entity_guid +	 */ +	public function markOutOfSync($entity_guid) { +		unset($this->isSynchronized[$entity_guid]); +	} + +	/** +	 * @param $entity_guid +	 * +	 * @return bool +	 */ +	public function isSynchronized($entity_guid) { +		return isset($this->isSynchronized[$entity_guid]); +	} + +	/** +	 * @param int $entity_guid +	 * +	 * @param string $name +	 * +	 * @param array|int|string|null $value  null means it is known that there is no +	 *                                      fetch-able metadata under this name +	 * @param bool $allow_multiple +	 */ +	public function save($entity_guid, $name, $value, $allow_multiple = false) { +		if ($this->getIgnoreAccess()) { +			// we don't know if what gets saves here will be available to user once +			// access control returns, hence it's best to forget :/ +			$this->markUnknown($entity_guid, $name); +		} else { +			if ($allow_multiple) { +				if ($this->isKnown($entity_guid, $name)) { +					$existing = $this->load($entity_guid, $name); +					if ($existing !== null) { +						$existing = (array) $existing; +						$existing[] = $value; +						$value = $existing; +					} +				} else { +					// we don't know whether there are unknown values, so it's +					// safest to leave that assumption +					$this->markUnknown($entity_guid, $name); +					return; +				} +			} +			$this->values[$entity_guid][$name] = $value; +		} +	} + +	/** +	 * Warning: You should always call isKnown() beforehand to verify that this +	 * function's return value should be trusted (otherwise a null return value +	 * is ambiguous). +	 * +	 * @param int $entity_guid +	 * +	 * @param string $name +	 * +	 * @return array|string|int|null null = value does not exist +	 */ +	public function load($entity_guid, $name) { +		if (isset($this->values[$entity_guid]) && array_key_exists($name, $this->values[$entity_guid])) { +			return $this->values[$entity_guid][$name]; +		} else { +			return null; +		} +	} + +	/** +	 * Forget about this metadata entry. We don't want to try to guess what the +	 * next fetch from storage will return +	 * +	 * @param int $entity_guid +	 * +	 * @param string $name +	 */ +	public function markUnknown($entity_guid, $name) { +		unset($this->values[$entity_guid][$name]); +		$this->markOutOfSync($entity_guid); +	} + +	/** +	 * If true, load() will return an accurate value for this name +	 * +	 * @param int $entity_guid +	 * +	 * @param string $name +	 * +	 * @return bool +	 */ +	public function isKnown($entity_guid, $name) { +		if (isset($this->isSynchronized[$entity_guid])) { +			return true; +		} else { +			return (isset($this->values[$entity_guid]) && array_key_exists($name, $this->values[$entity_guid])); +		} + +	} + +	/** +	 * Declare that metadata under this name is known to be not fetch-able from storage +	 * +	 * @param int $entity_guid +	 * +	 * @param string $name +	 * +	 * @return array +	 */ +	public function markEmpty($entity_guid, $name) { +		$this->values[$entity_guid][$name] = null; +	} + +	/** +	 * Forget about all metadata for an entity +	 * +	 * @param int $entity_guid +	 */ +	public function clear($entity_guid) { +		$this->values[$entity_guid] = array(); +		$this->markOutOfSync($entity_guid); +	} + +	/** +	 * Clear entire cache and mark all entities as out of sync +	 */ +	public function flush() { +		$this->values = array(); +		$this->isSynchronized = array(); +	} + +	/** +	 * Use this value instead of calling elgg_get_ignore_access(). By default that +	 * function will be called. +	 * +	 * This setting makes this component a little more loosely-coupled. +	 * +	 * @param bool $ignore +	 */ +	public function setIgnoreAccess($ignore) { +		$this->ignoreAccess = (bool) $ignore; +	} + +	/** +	 * Tell the cache to call elgg_get_ignore_access() to determing access status. +	 */ +	public function unsetIgnoreAccess() { +		$this->ignoreAccess = null; +	} + +	/** +	 * @return bool +	 */ +	protected function getIgnoreAccess() { +		if (null === $this->ignoreAccess) { +			return elgg_get_ignore_access(); +		} else { +			return $this->ignoreAccess; +		} +	} + +	/** +	 * Invalidate based on options passed to the global *_metadata functions +	 * +	 * @param string $action  Action performed on metadata. "delete", "disable", or "enable" +	 * +	 * @param array $options  Options passed to elgg_(delete|disable|enable)_metadata +	 * +	 *   "guid" if given, invalidation will be limited to this entity +	 * +	 *   "metadata_name" if given, invalidation will be limited to metadata with this name +	 */ +	public function invalidateByOptions($action, array $options) { +		// remove as little as possible, optimizing for common cases +		if (empty($options['guid'])) { +			// safest to clear everything unless we want to make this even more complex :( +			$this->flush(); +		} else { +			if (empty($options['metadata_name'])) { +				// safest to clear the whole entity +				$this->clear($options['guid']); +			} else { +				switch ($action) { +					case 'delete': +						$this->markEmpty($options['guid'], $options['metadata_name']); +						break; +					default: +						$this->markUnknown($options['guid'], $options['metadata_name']); +				} +			} +		} +	} + +	/** +	 * @param int|array $guids +	 */ +	public function populateFromEntities($guids) { +		if (empty($guids)) { +			return; +		} +		if (!is_array($guids)) { +			$guids = array($guids); +		} +		$guids = array_unique($guids); + +		// could be useful at some point in future +		//$guids = $this->filterMetadataHeavyEntities($guids); + +		$db_prefix = elgg_get_config('dbprefix'); +		$options = array( +			'guids' => $guids, +			'limit' => 0, +			'callback' => false, +			'joins' => array( +				"JOIN {$db_prefix}metastrings v ON n_table.value_id = v.id", +				"JOIN {$db_prefix}metastrings n ON n_table.name_id = n.id", +			), +			'selects' => array('n.string AS name', 'v.string AS value'), +			'order_by' => 'n_table.entity_guid, n_table.time_created ASC', +		); +		$data = elgg_get_metadata($options); + +		// build up metadata for each entity, save when GUID changes (or data ends) +		$last_guid = null; +		$metadata = array(); +		$last_row_idx = count($data) - 1; +		foreach ($data as $i => $row) { +			$name = $row->name; +			$value = ($row->value_type === 'text') ? $row->value : (int) $row->value; +			$guid = $row->entity_guid; +			if ($guid !== $last_guid) { +				if ($last_guid) { +					$this->saveAll($last_guid, $metadata); +				} +				$metadata = array(); +			} +			if (isset($metadata[$name])) { +				$metadata[$name] = (array) $metadata[$name]; +				$metadata[$name][] = $value; +			} else { +				$metadata[$name] = $value; +			} +			if (($i == $last_row_idx)) { +				$this->saveAll($guid, $metadata); +			} +			$last_guid = $guid; +		} +	} + +	/** +	 * Filter out entities whose concatenated metadata values (INTs casted as string) +	 * exceed a threshold in characters. This could be used to avoid overpopulating the +	 * cache if RAM usage becomes an issue. +	 * +	 * @param array $guids GUIDs of entities to examine +	 * +	 * @param int $limit Limit in characters of all metadata (with ints casted to strings) +	 * +	 * @return array +	 */ +	public function filterMetadataHeavyEntities(array $guids, $limit = 1024000) { +		$db_prefix = elgg_get_config('dbprefix'); + +		$options = array( +			'guids' => $guids, +			'limit' => 0, +			'callback' => false, +			'joins' => "JOIN {$db_prefix}metastrings v ON n_table.value_id = v.id", +			'selects' => array('SUM(LENGTH(v.string)) AS bytes'), +			'order_by' => 'n_table.entity_guid, n_table.time_created ASC', +			'group_by' => 'n_table.entity_guid', +		); +		$data = elgg_get_metadata($options); +		// don't cache if metadata for entity is over 10MB (or rolled INT) +		foreach ($data as $row) { +			if ($row->bytes > $limit || $row->bytes < 0) { +				array_splice($guids, array_search($row->entity_guid, $guids), 1); +			} +		} +		return $guids; +	} +} diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index 554b0561f..26c1cccfd 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -1070,7 +1070,11 @@ function _elgg_php_error_handler($errno, $errmsg, $filename, $linenum, $vars) {  		case E_WARNING :  		case E_USER_WARNING :  		case E_RECOVERABLE_ERROR: // (e.g. type hint violation) -			error_log("PHP WARNING: $error"); +			 +			// check if the error wasn't suppressed by @-functionname +			if(error_reporting()){ +				error_log("PHP WARNING: $error"); +			}  			break;  		default: diff --git a/engine/lib/entities.php b/engine/lib/entities.php index 3896cd58f..a50567d9f 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -39,6 +39,8 @@ function invalidate_cache_for_entity($guid) {  	$guid = (int)$guid;  	unset($ENTITY_CACHE[$guid]); + +	elgg_get_metadata_cache()->clear($guid);  }  /** @@ -606,12 +608,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)) { @@ -698,7 +702,7 @@ function get_entity($guid) {  	// 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; +		return false;  	}  	// Check local cache first @@ -715,14 +719,23 @@ function get_entity($guid) {  			$shared_cache = false;  		}  	} + +	// until ACLs in memcache, DB query is required to determine access +	$entity_row = get_entity_as_row($guid); +	if (!$entity_row) { +		return false; +	} +  	if ($shared_cache) { -		$new_entity = $shared_cache->load($guid); -		if ($new_entity) { -			return $new_entity; +		$cached_entity = $shared_cache->load($guid); +		// @todo store ACLs in memcache http://trac.elgg.org/ticket/3018#comment:3 +		if ($cached_entity) { +			// @todo use ACL and cached entity access_id to determine if user can see it +			return $cached_entity;  		}  	} -	$new_entity = entity_row_to_elggstar(get_entity_as_row($guid)); +	$new_entity = entity_row_to_elggstar($entity_row);  	if ($new_entity) {  		cache_entity($new_entity);  	} @@ -969,17 +982,25 @@ function elgg_get_entities(array $options = array()) {  		$dt = get_data($query, $options['callback']);  		if ($dt) { -			foreach ($dt as $entity) { -				// If a custom callback is provided, it could return something other than ElggEntity, -				// so we have to do an explicit check here. -				if ($entity instanceof ElggEntity) { -					cache_entity($entity); +			// 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) { +					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); @@ -1153,7 +1174,7 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair   *                           best to provide in table.column format.   * @param NULL|array $guids  Array of GUIDs.   * - * @return false|str + * @return false|string   * @since 1.8.0   * @access private   */ @@ -1202,7 +1223,7 @@ function elgg_get_guid_based_where_sql($column, $guids) {   * @param NULL|int $time_updated_upper Time updated upper limit   * @param NULL|int $time_updated_lower Time updated lower limit   * - * @return FALSE|str FALSE on fail, string on success. + * @return FALSE|string FALSE on fail, string on success.   * @since 1.7.0   * @access private   */ @@ -1304,7 +1325,7 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti   * @param string $subtype        The subtype of entity   * @param int    $container_guid The container GUID that the entinties 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   */ @@ -1649,7 +1670,7 @@ function delete_entity($guid, $recursive = true) {   * @param string $returnvalue Return value from previous hook   * @param array  $params      The parameters, passed 'guid' and 'varname'   * - * @return void + * @return ElggMetadata|null   * @elgg_plugin_hook_handler volatile metadata   * @todo investigate more.   * @access private @@ -1694,6 +1715,8 @@ 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 @@ -1736,6 +1759,8 @@ 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'); @@ -1807,6 +1832,8 @@ function oddentity_to_elggentity(ODDEntity $element) {   * @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']; @@ -1853,8 +1880,6 @@ function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {   * @link http://docs.elgg.org/Entities/AccessControl   */  function can_edit_entity($entity_guid, $user_guid = 0) { -	global $CONFIG; -  	$user_guid = (int)$user_guid;  	$user = get_entity($user_guid);  	if (!$user) { @@ -1978,7 +2003,7 @@ function get_entity_url($entity_guid) {   * @param string $entity_subtype The entity subtype   * @param string $function_name  The function to register   * - * @return true|false Depending on success + * @return bool Depending on success   * @see get_entity_url()   * @see ElggEntity::getURL()   * @since 1.8.0 @@ -2014,7 +2039,7 @@ function elgg_register_entity_url_handler($entity_type, $entity_subtype, $functi   * @param string $type    The type of entity (object, site, user, group)   * @param string $subtype The subtype to register (may be blank)   * - * @return true|false Depending on success + * @return bool Depending on success   * @see get_registered_entity_types()   * @link http://docs.elgg.org/Search   * @link http://docs.elgg.org/Tutorials/Search @@ -2051,7 +2076,7 @@ function elgg_register_entity_type($type, $subtype = null) {   * @param string $type    The type of entity (object, site, user, group)   * @param string $subtype The subtype to register (may be blank)   * - * @return true|false Depending on success + * @return bool Depending on success   * @see elgg_register_entity_type()   */  function unregister_entity_type($type, $subtype) { @@ -2118,7 +2143,7 @@ function get_registered_entity_types($type = null) {   * @param string $type    The type of entity (object, site, user, group)   * @param string $subtype The subtype (may be blank)   * - * @return true|false Depending on whether or not the type has been registered + * @return bool Depending on whether or not the type has been registered   */  function is_registered_entity_type($type, $subtype = null) {  	global $CONFIG; @@ -2318,7 +2343,7 @@ function entities_gc() {  /**   * 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 diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php index 77fa30e41..f76c20f24 100644 --- a/engine/lib/metadata.php +++ b/engine/lib/metadata.php @@ -12,7 +12,7 @@   *   * @param stdClass $row An object from the database   * - * @return stdClass or ElggMetadata + * @return stdClass|ElggMetadata   * @access private   */  function row_to_elggmetadata($row) { @@ -30,7 +30,7 @@ function row_to_elggmetadata($row) {   *   * @param int $id The id of the metadata object being retrieved.   * - * @return false|ElggMetadata + * @return ElggMetadata|false  FALSE if not found   */  function elgg_get_metadata_from_id($id) {  	return elgg_get_metastring_based_object_from_id($id, 'metadata'); @@ -64,7 +64,7 @@ function elgg_delete_metadata_by_id($id) {   * @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 = 0,  	$access_id = ACCESS_PRIVATE, $allow_multiple = false) { @@ -90,8 +90,6 @@ function create_metadata($entity_guid, $name, $value, $value_type = '', $owner_g  	$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"; @@ -106,34 +104,33 @@ function create_metadata($entity_guid, $name, $value, $value_type = '', $owner_g  	} 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 = 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 {  				elgg_delete_metadata_by_id($id); @@ -175,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}");  	} @@ -187,15 +185,9 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i  	$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 @@ -216,6 +208,9 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i  	$result = update_data($query);  	if ($result !== false) { + +		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. @@ -234,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 @@ -308,6 +303,8 @@ function elgg_delete_metadata(array $options) {  		return false;  	} +	elgg_get_metadata_cache()->invalidateByOptions('delete', $options); +  	$options['metastring_type'] = 'metadata';  	return elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false);  } @@ -328,6 +325,8 @@ function elgg_disable_metadata(array $options) {  		return false;  	} +	elgg_get_metadata_cache()->invalidateByOptions('disable', $options); +  	$options['metastring_type'] = 'metadata';  	return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', false);  } @@ -348,6 +347,8 @@ function elgg_enable_metadata(array $options) {  		return false;  	} +	elgg_get_metadata_cache()->invalidateByOptions('enable', $options); +  	$options['metastring_type'] = 'metadata';  	return elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback');  } @@ -449,16 +450,16 @@ function elgg_get_entities_from_metadata(array $options = array()) {   * 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 + * @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 @@ -732,6 +733,8 @@ function elgg_list_entities_from_metadata($options) {   *   * @return array   * @access private + * + * @throws InvalidParameterException   */  function export_metadata_plugin_hook($hook, $entity_type, $returnvalue, $params) {  	// Sanity check values @@ -743,15 +746,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 = elgg_get_metadata(array( -		'guid' => $guid, -		'limit' => 0 +		'guid' => (int)$params['guid'], +		'limit' => 0,  	));  	if ($result) { +		/* @var ElggMetadata[] $result */  		foreach ($result as $r) {  			$returnvalue[] = $r->export();  		} @@ -889,6 +890,50 @@ 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 + */ +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 */  elgg_register_plugin_hook_handler("export", "all", "export_metadata_plugin_hook", 2); @@ -912,5 +957,6 @@ elgg_register_plugin_hook_handler('unit_test', 'system', 'metadata_test');  function metadata_test($hook, $type, $value, $params) {  	global $CONFIG;  	$value[] = $CONFIG->path . 'engine/tests/api/metadata.php'; +	$value[] = $CONFIG->path . 'engine/tests/api/metadata_cache.php';  	return $value; -}
\ No newline at end of file +} diff --git a/engine/lib/sites.php b/engine/lib/sites.php index 850092cad..8b772668d 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;  }  /** diff --git a/engine/lib/views.php b/engine/lib/views.php index b00334062..6135026a7 100644 --- a/engine/lib/views.php +++ b/engine/lib/views.php @@ -1653,7 +1653,7 @@ function elgg_views_boot() {  	}  	// set default icon sizes - can be overridden in settings.php or with plugin -	if (!elgg_get_config('icon_sizes')) { +	if (!$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/tests/api/metadata_cache.php b/engine/tests/api/metadata_cache.php new file mode 100644 index 000000000..846116a7b --- /dev/null +++ b/engine/tests/api/metadata_cache.php @@ -0,0 +1,169 @@ +<?php +/** + * Elgg Test metadata cache + * + * @package Elgg + * @subpackage Test + */ +class ElggCoreMetadataCacheTest extends ElggCoreUnitTest { + +	/** +	 * @var ElggVolatileMetadataCache +	 */ +	protected $cache; + +	/** +	 * @var ElggObject +	 */ +	protected $obj1; + +	/** +	 * @var int +	 */ +	protected $guid1; + +	/** +	 * @var ElggObject +	 */ +	protected $obj2; + +	/** +	 * @var int +	 */ +	protected $guid2; + +	protected $name = 'test'; +	protected $value = 'test'; +	protected $ignoreAccess; + +	/** +	 * Called before each test method. +	 */ +	public function setUp() { +		$this->ignoreAccess = elgg_set_ignore_access(false); + +		$this->cache = elgg_get_metadata_cache(); + +		$this->obj1 = new ElggObject(); +		$this->obj1->save(); +		$this->guid1 = $this->obj1->guid; + +		$this->obj2 = new ElggObject(); +		$this->obj2->save(); +		$this->guid2 = $this->obj2->guid; +	} + +	/** +	 * Called after each test method. +	 */ +	public function tearDown() { +		$this->obj1->delete(); +		$this->obj2->delete(); + +		elgg_set_ignore_access($this->ignoreAccess); +	} + +	public function testBasicApi() { +		// test de-coupled instance +		$cache = new ElggVolatileMetadataCache(); +		$cache->setIgnoreAccess(false); +		$guid = 1; + +		$this->assertFalse($cache->isKnown($guid, $this->name)); + +		$cache->markEmpty($guid, $this->name); +		$this->assertTrue($cache->isKnown($guid, $this->name)); +		$this->assertNull($cache->load($guid, $this->name)); + +		$cache->markUnknown($guid, $this->name); +		$this->assertFalse($cache->isKnown($guid, $this->name)); + +		$cache->save($guid, $this->name, $this->value); +		$this->assertIdentical($cache->load($guid, $this->name), $this->value); + +		$cache->save($guid, $this->name, 1, true); +		$this->assertIdentical($cache->load($guid, $this->name), array($this->value, 1)); + +		$cache->clear($guid); +		$this->assertFalse($cache->isKnown($guid, $this->name)); +	} + +	public function testReadsAreCached() { +		// test that reads fill cache +		$this->obj1->setMetaData($this->name, $this->value); +		$this->cache->flush(); + +		$this->obj1->getMetaData($this->name); +		$this->assertIdentical($this->cache->load($this->guid1, $this->name), $this->value); +	} + +	public function testWritesAreCached() { +		// delete should mark cache as known to be empty +		$this->obj1->deleteMetadata($this->name); +		$this->assertTrue($this->cache->isKnown($this->guid1, $this->name)); +		$this->assertNull($this->cache->load($this->guid1, $this->name)); + +		// without name, delete should invalidate the entire entity +		$this->cache->save($this->guid1, $this->name, $this->value); +		elgg_delete_metadata(array( +			'guid' => $this->guid1, +		)); +		$this->assertFalse($this->cache->isKnown($this->guid1, $this->name)); + +		// test set +		$this->obj1->setMetaData($this->name, $this->value); +		$this->assertIdentical($this->cache->load($this->guid1, $this->name), $this->value); + +		// test set multiple +		$this->obj1->setMetaData($this->name, 1, 'integer', true); +		$this->assertIdentical($this->cache->load($this->guid1, $this->name), array($this->value, 1)); + +		// writes when access is ignore should invalidate +		$tmp_ignore = elgg_set_ignore_access(true); +		$this->obj1->setMetaData($this->name, $this->value); +		$this->assertFalse($this->cache->isKnown($this->guid1, $this->name)); +		elgg_set_ignore_access($tmp_ignore); +	} + +	public function testDisableAndEnable() { +		// both should mark cache unknown +		$this->obj1->setMetaData($this->name, $this->value); +		$this->obj1->disableMetadata($this->name); +		$this->assertFalse($this->cache->isKnown($this->guid1, $this->name)); + +		$this->cache->save($this->guid1, $this->name, $this->value); +		$this->obj1->enableMetadata($this->name); +		$this->assertFalse($this->cache->isKnown($this->guid1, $this->name)); +	} + +	public function testPopulateFromEntities() { +		// test populating cache from set of entities +		$this->obj1->setMetaData($this->name, $this->value); +		$this->obj1->setMetaData($this->name, 4, 'integer', true); +		$this->obj1->setMetaData("{$this->name}-2", "{$this->value}-2"); +		$this->obj2->setMetaData($this->name, $this->value); + +		$this->cache->flush(); +		$this->cache->populateFromEntities(array($this->guid1, $this->guid2)); + +		$expected = array(); +		$expected[$this->name][] = $this->value; +		$expected[$this->name][] = 4; +		$expected["{$this->name}-2"] = "{$this->value}-2"; +		$this->assertIdentical($this->cache->loadAll($this->guid1), $expected); + +		$expected = array(); +		$expected[$this->name] = $this->value; +		$this->assertIdentical($this->cache->loadAll($this->guid2), $expected); +	} + +	public function testFilterHeavyEntities() { +		$big_str = str_repeat('-', 5000); +		$this->obj2->setMetaData($this->name, array($big_str, $big_str)); + +		$guids = array($this->guid1, $this->guid2); +		$expected = array($this->guid1); +		$actual = $this->cache->filterMetadataHeavyEntities($guids, 6000); +		$this->assertIdentical($actual, $expected); +	} +} diff --git a/mod/blog/actions/blog/auto_save_revision.php b/mod/blog/actions/blog/auto_save_revision.php index 66b65c5fd..e33edfaab 100644 --- a/mod/blog/actions/blog/auto_save_revision.php +++ b/mod/blog/actions/blog/auto_save_revision.php @@ -7,7 +7,7 @@  $guid = get_input('guid');  $user = elgg_get_logged_in_user_entity(); -$title = get_input('title'); +$title = htmlspecialchars(get_input('title', '', false), ENT_QUOTES, 'UTF-8');  $description = get_input('description');  $excerpt = get_input('excerpt'); diff --git a/mod/blog/actions/blog/save.php b/mod/blog/actions/blog/save.php index 048bc00be..070c96398 100644 --- a/mod/blog/actions/blog/save.php +++ b/mod/blog/actions/blog/save.php @@ -57,7 +57,11 @@ $required = array('title', 'description');  // load from POST and do sanity and access checking  foreach ($values as $name => $default) { -	$value = get_input($name, $default); +	if ($name === 'title') { +		$value = htmlspecialchars(get_input('title', $default, false), ENT_QUOTES, 'UTF-8'); +	} else { +		$value = get_input($name, $default); +	}  	if (in_array($name, $required) && empty($value)) {  		$error = elgg_echo("blog:error:missing:$name"); diff --git a/mod/bookmarks/actions/bookmarks/save.php b/mod/bookmarks/actions/bookmarks/save.php index 3ca6bef32..46090b115 100644 --- a/mod/bookmarks/actions/bookmarks/save.php +++ b/mod/bookmarks/actions/bookmarks/save.php @@ -5,7 +5,7 @@  * @package Bookmarks  */ -$title = strip_tags(get_input('title')); +$title = htmlspecialchars(get_input('title', '', false), ENT_QUOTES, 'UTF-8');  $description = get_input('description');  $address = get_input('address');  $access_id = get_input('access_id'); diff --git a/mod/file/actions/file/upload.php b/mod/file/actions/file/upload.php index d72d04eb7..d6dce2528 100644 --- a/mod/file/actions/file/upload.php +++ b/mod/file/actions/file/upload.php @@ -6,7 +6,7 @@   */  // Get variables -$title = get_input("title"); +$title = htmlspecialchars(get_input('title', '', false), ENT_QUOTES, 'UTF-8');  $desc = get_input("description");  $access_id = (int) get_input("access_id");  $container_guid = (int) get_input('container_guid', 0); @@ -44,7 +44,7 @@ if ($new_file) {  	// if no title on new upload, grab filename  	if (empty($title)) { -		$title = $_FILES['upload']['name']; +		$title = htmlspecialchars($_FILES['upload']['name'], ENT_QUOTES, 'UTF-8');  	}  } else { diff --git a/mod/groups/actions/discussion/save.php b/mod/groups/actions/discussion/save.php index de4afadfb..b3e9da654 100644 --- a/mod/groups/actions/discussion/save.php +++ b/mod/groups/actions/discussion/save.php @@ -4,7 +4,7 @@   */  // Get variables -$title = get_input("title"); +$title = htmlspecialchars(get_input('title', '', false), ENT_QUOTES, 'UTF-8');  $desc = get_input("description");  $status = get_input("status");  $access_id = (int) get_input("access_id"); diff --git a/mod/groups/actions/groups/edit.php b/mod/groups/actions/groups/edit.php index df2464a65..a4169461a 100644 --- a/mod/groups/actions/groups/edit.php +++ b/mod/groups/actions/groups/edit.php @@ -33,8 +33,7 @@ foreach ($CONFIG->group as $shortname => $valuetype) {  	}  } -$input['name'] = get_input('name'); -$input['name'] = html_entity_decode($input['name'], ENT_COMPAT, 'UTF-8'); +$input['name'] = htmlspecialchars(get_input('name', '', false), ENT_QUOTES, 'UTF-8');  $user = elgg_get_logged_in_user_entity(); diff --git a/mod/groups/start.php b/mod/groups/start.php index c591410c5..9dca7dc16 100644 --- a/mod/groups/start.php +++ b/mod/groups/start.php @@ -194,6 +194,15 @@ function groups_setup_sidebar_menus() {   */  function groups_page_handler($page) { +	// forward old profile urls +	if (is_numeric($page[0])) { +		$group = get_entity($page[0]); +		if (elgg_instanceof($group, 'group', '', 'ElggGroup')) { +			system_message(elgg_echo('changebookmark')); +			forward($group->getURL()); +		} +	} +	  	elgg_load_library('elgg:groups');  	if (!isset($page[0])) { diff --git a/mod/messages/pages/messages/inbox.php b/mod/messages/pages/messages/inbox.php index fdfc20c43..de5b8b231 100644 --- a/mod/messages/pages/messages/inbox.php +++ b/mod/messages/pages/messages/inbox.php @@ -8,8 +8,13 @@  gatekeeper();  $page_owner = elgg_get_page_owner_entity(); -if (!$page_owner) { -	register_error(elgg_echo()); + +if (!$page_owner || !$page_owner->canEdit()) { +	$guid = 0; +	if($page_owner){ +		$guid = $page_owner->getGUID(); +	} +	register_error(elgg_echo("pageownerunavailable", array($guid)));  	forward();  } diff --git a/mod/messages/pages/messages/read.php b/mod/messages/pages/messages/read.php index 19e3ecdd7..eb36eaa4b 100644 --- a/mod/messages/pages/messages/read.php +++ b/mod/messages/pages/messages/read.php @@ -8,8 +8,8 @@  gatekeeper();  $message = get_entity(get_input('guid')); -if (!$message) { -	forward('messages/inbox'); +if (!$message || !elgg_instanceof($message, "object", "messages")) { +	forward('messages/inbox/' . elgg_get_logged_in_user_entity()->username);  }  // mark the message as read @@ -38,8 +38,9 @@ if ($inbox) {  	);  	$body_params = array('message' => $message);  	$content .= elgg_view_form('messages/reply', $form_params, $body_params); - -	if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid()) { +	$from_user = get_user($message->fromID); +	 +	if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid() && $from_user) {  		elgg_register_menu_item('title', array(  			'name' => 'reply',  			'href' => '#messages-reply-form', diff --git a/mod/messages/pages/messages/sent.php b/mod/messages/pages/messages/sent.php index af06ab273..3d08cd5ee 100644 --- a/mod/messages/pages/messages/sent.php +++ b/mod/messages/pages/messages/sent.php @@ -8,8 +8,13 @@  gatekeeper();  $page_owner = elgg_get_page_owner_entity(); -if (!$page_owner) { -	register_error(elgg_echo()); + +if (!$page_owner || !$page_owner->canEdit()) { +	$guid = 0; +	if($page_owner){ +		$guid = $page_owner->getGUID(); +	} +	register_error(elgg_echo("pageownerunavailable", array($guid)));  	forward();  } diff --git a/mod/pages/actions/pages/edit.php b/mod/pages/actions/pages/edit.php index a32e4a4ba..fe5754d76 100644 --- a/mod/pages/actions/pages/edit.php +++ b/mod/pages/actions/pages/edit.php @@ -8,9 +8,10 @@  $variables = elgg_get_config('pages');  $input = array();  foreach ($variables as $name => $type) { -	$input[$name] = get_input($name);  	if ($name == 'title') { -		$input[$name] = strip_tags($input[$name]); +		$input[$name] = htmlspecialchars(get_input($name, '', false), ENT_QUOTES, 'UTF-8'); +	} else { +		$input[$name] = get_input($name);  	}  	if ($type == 'tags') {  		$input[$name] = string_to_tag_array($input[$name]); diff --git a/mod/search/views/default/search/search_box.php b/mod/search/views/default/search/search_box.php index ff12ae4f0..7474a280c 100644 --- a/mod/search/views/default/search/search_box.php +++ b/mod/search/views/default/search/search_box.php @@ -32,12 +32,11 @@ if (function_exists('mb_convert_encoding')) {  }  $display_query = htmlspecialchars($display_query, ENT_QUOTES, 'UTF-8', false); -  ?>  <form class="<?php echo $class; ?>" action="<?php echo elgg_get_site_url(); ?>search" method="get">  	<fieldset> -		<input type="text" class="search-input" size="21" name="q" value="<?php echo elgg_echo('search'); ?>" onblur="if (this.value=='') { this.value='<?php echo elgg_echo('search'); ?>' }" onfocus="if (this.value=='<?php echo elgg_echo('search'); ?>') { this.value='' };" /> +		<input type="text" class="search-input" size="21" name="q" value="<?php echo $display_query; ?>" onblur="if (this.value=='') { this.value='<?php echo elgg_echo('search'); ?>' }" onfocus="if (this.value=='<?php echo elgg_echo('search'); ?>') { this.value='' };" />  		<input type="hidden" name="search_type" value="all" />  		<input type="submit" value="<?php echo elgg_echo('search:go'); ?>" class="search-submit-button" />  	</fieldset> diff --git a/views/default/core/settings/statistics/numentities.php b/views/default/core/settings/statistics/numentities.php index ce1705a2e..3782fd8bc 100644 --- a/views/default/core/settings/statistics/numentities.php +++ b/views/default/core/settings/statistics/numentities.php @@ -7,7 +7,7 @@   */  // Get entity statistics -$entity_stats = get_entity_statistics(elgg_get_logged_in_user_guid()); +$entity_stats = get_entity_statistics(elgg_get_page_owner_guid());  if ($entity_stats) {  	$rows = ''; diff --git a/views/default/core/settings/statistics/online.php b/views/default/core/settings/statistics/online.php index ce7ff35fb..f50ad9acc 100644 --- a/views/default/core/settings/statistics/online.php +++ b/views/default/core/settings/statistics/online.php @@ -6,7 +6,7 @@   * @subpackage Core   */ -$user = elgg_get_logged_in_user_entity(); +$user = elgg_get_page_owner_entity();  $logged_in = 0;  $log = get_system_log($user->guid, "login", "", 'user', '', 1); diff --git a/views/default/page/default.php b/views/default/page/default.php index 3724bffd8..567494d0c 100644 --- a/views/default/page/default.php +++ b/views/default/page/default.php @@ -33,9 +33,11 @@ $footer = elgg_view('page/elements/footer', $vars);  // Set the content type  header("Content-type: text/html; charset=UTF-8"); +$lang = get_current_language(); +  ?>  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang; ?>" lang="<?php echo $lang; ?>">  <head>  <?php echo elgg_view('page/elements/head', $vars); ?>  </head> @@ -45,13 +47,13 @@ header("Content-type: text/html; charset=UTF-8");  		<?php echo $messages; ?>  	</div> -	<?php if (elgg_is_logged_in()): ?> +	<?php if (elgg_is_logged_in()){ ?>  	<div class="elgg-page-topbar">  		<div class="elgg-inner">  			<?php echo $topbar; ?>  		</div>  	</div> -	<?php endif; ?> +	<?php } ?>  	<div class="elgg-page-header">  		<div class="elgg-inner"> | 
