diff options
| author | Brett Profitt <brett.profitt@gmail.com> | 2011-08-25 10:00:38 -0700 | 
|---|---|---|
| committer | Brett Profitt <brett.profitt@gmail.com> | 2011-08-25 10:00:38 -0700 | 
| commit | dccc333c765bb28da55b4a55d9c916acdb88413a (patch) | |
| tree | bdd26a0b4cd85241a19b7fcb2c0770f0ac3eb9f0 /engine | |
| parent | ec7b94a64aef23b85866ecdac8e8acc712d29bb6 (diff) | |
| parent | 003cb81c7888f4d2fd763e5814027c6f8d71186f (diff) | |
| download | elgg-dccc333c765bb28da55b4a55d9c916acdb88413a.tar.gz elgg-dccc333c765bb28da55b4a55d9c916acdb88413a.tar.bz2  | |
Merge branch 'master' of github.com:brettp/Elgg
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/classes/ElggBatch.php | 10 | ||||
| -rw-r--r-- | engine/classes/ElggCache.php | 37 | ||||
| -rw-r--r-- | engine/classes/ElggEntity.php | 4 | ||||
| -rw-r--r-- | engine/classes/ElggMenuItem.php | 295 | ||||
| -rw-r--r-- | engine/classes/ElggPlugin.php | 23 | ||||
| -rw-r--r-- | engine/classes/ElggPriorityList.php | 358 | ||||
| -rw-r--r-- | engine/classes/ElggSite.php | 3 | ||||
| -rw-r--r-- | engine/classes/ElggUser.php | 3 | ||||
| -rw-r--r-- | engine/lib/actions.php | 3 | ||||
| -rw-r--r-- | engine/lib/admin.php | 2 | ||||
| -rw-r--r-- | engine/lib/configuration.php | 19 | ||||
| -rw-r--r-- | engine/lib/deprecated-1.8.php | 7 | ||||
| -rw-r--r-- | engine/lib/elgglib.php | 130 | ||||
| -rw-r--r-- | engine/lib/entities.php | 9 | ||||
| -rw-r--r-- | engine/lib/group.php | 2 | ||||
| -rw-r--r-- | engine/lib/input.php | 5 | ||||
| -rw-r--r-- | engine/lib/navigation.php | 66 | ||||
| -rw-r--r-- | engine/lib/relationships.php | 33 | ||||
| -rw-r--r-- | engine/lib/river.php | 8 | ||||
| -rw-r--r-- | engine/lib/statistics.php | 2 | ||||
| -rw-r--r-- | engine/lib/upgrade.php | 104 | ||||
| -rw-r--r-- | engine/lib/upgrades/2011010101.php | 5 | ||||
| -rw-r--r-- | engine/lib/users.php | 39 | ||||
| -rw-r--r-- | engine/lib/views.php | 92 | ||||
| -rw-r--r-- | engine/tests/api/helpers.php | 332 | 
25 files changed, 1254 insertions, 337 deletions
diff --git a/engine/classes/ElggBatch.php b/engine/classes/ElggBatch.php index 49aed800a..62128e34f 100644 --- a/engine/classes/ElggBatch.php +++ b/engine/classes/ElggBatch.php @@ -6,7 +6,7 @@   * This is usually used with elgg_get_entities() and friends, elgg_get_annotations()   * and elgg_get_metadata().   * - * If pass a valid PHP callback, all results will be run through that callback. + * If you pass a valid PHP callback, all results will be run through that callback.   * You can still foreach() through the result set after.  Valid PHP callbacks   * can be a string, an array, or a closure.   * {@link http://php.net/manual/en/language.pseudo-types.php} @@ -14,10 +14,10 @@   * The callback function must accept 3 arguments: an entity, the getter used, and the options used.   *   * Results from the callback are stored in callbackResult. - * If the callback returns only booleans callbackResults will be the combined + * If the callback returns only booleans, callbackResults will be the combined   * result of all calls.   * - * If the callback returns anything else callbackresult will be an indexed array + * If the callback returns anything else, callbackresult will be an indexed array   * of whatever the callback returns.  If returning error handling information,   * you should include enough information to determine which result you're referring   * to. @@ -90,7 +90,7 @@ class ElggBatch  	private $offset = 0;  	/** -	 * Stop of this many results. +	 * Stop after this many results.  	 *  	 * @var unknown_type  	 */ @@ -333,7 +333,7 @@ class ElggBatch  			$result = current($this->results);  		} else { -			// the function above resets the indexes, so don't only inc if not +			// the function above resets the indexes, so only inc if not  			// getting new set  			$this->resultIndex++;  			$result = next($this->results); diff --git a/engine/classes/ElggCache.php b/engine/classes/ElggCache.php index 2e697e0bb..5c2cafcb7 100644 --- a/engine/classes/ElggCache.php +++ b/engine/classes/ElggCache.php @@ -6,9 +6,7 @@   * @package    Elgg.Core   * @subpackage Cache   */ -abstract class ElggCache implements -	// Override for array access -	ArrayAccess  { +abstract class ElggCache implements ArrayAccess {  	/**  	 * Variables for the cache object.  	 * @@ -141,6 +139,9 @@ abstract class ElggCache implements  	/**  	 * Load data from the cache using a given key.  	 * +	 * @todo $offset is a horrible variable name because it creates confusion +	 * with the ArrayAccess methods +	 *  	 * @param string $key    Name  	 * @param int    $offset Offset  	 * @param int    $limit  Limit @@ -186,12 +187,12 @@ abstract class ElggCache implements  	// ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////  	/** -	 * Set offset +	 * Assigns a value for the specified key  	 *  	 * @see ArrayAccess::offsetSet()  	 * -	 * @param mixed $key   Name -	 * @param mixed $value Value +	 * @param mixed $key    The key (offset) to assign the value to. +	 * @param mixed $value  The value to set.  	 *  	 * @return void  	 */ @@ -200,43 +201,43 @@ abstract class ElggCache implements  	}  	/** -	 * Get offset +	 * Get the value for specified key  	 *  	 * @see ArrayAccess::offsetGet()  	 * -	 * @param mixed $key Name +	 * @param mixed $offset The key (offset) to retrieve.  	 * -	 * @return void +	 * @return mixed  	 */  	function offsetGet($key) {  		return $this->load($key);  	}  	/** -	 * Unsets offset +	 * Unsets a key.  	 *  	 * @see ArrayAccess::offsetUnset()  	 * -	 * @param mixed $key Name +	 * @param mixed $key The key (offset) to unset.  	 *  	 * @return void  	 */  	function offsetUnset($key) { -		if (isset($this->key)) { -			unset($this->key); +		if (isset($this->$key)) { +			unset($this->$key);  		}  	}  	/** -	 * Does offset exist +	 * Does key exist  	 *  	 * @see ArrayAccess::offsetExists()  	 * -	 * @param mixed $offset Offset +	 * @param mixed $key A key (offset) to check for.  	 * -	 * @return void +	 * @return bool  	 */ -	function offsetExists($offset) { -		return isset($this->$offset); +	function offsetExists($key) { +		return isset($this->$key);  	}  } diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php index 8fc1e46cb..6edc99dd4 100644 --- a/engine/classes/ElggEntity.php +++ b/engine/classes/ElggEntity.php @@ -851,7 +851,7 @@ abstract class ElggEntity extends ElggData implements  	 */  	function countComments() {  		$params = array('entity' => $this); -		$num = trigger_plugin_hook('comments:count', $this->getType(), $params); +		$num = elgg_trigger_plugin_hook('comments:count', $this->getType(), $params);  		if (is_int($num)) {  			return $num; @@ -1628,7 +1628,7 @@ abstract class ElggEntity extends ElggData implements  		 */  		elgg_set_viewtype('default'); -		$view = elgg_view_entity($this, true); +		$view = elgg_view_entity($this, array('full_view' => true));  		elgg_set_viewtype();  		$tmp[] = new ODDMetaData($uuid . "volatile/renderedentity/", $uuid, diff --git a/engine/classes/ElggMenuItem.php b/engine/classes/ElggMenuItem.php index 157ed9ceb..cfdc2f5fa 100644 --- a/engine/classes/ElggMenuItem.php +++ b/engine/classes/ElggMenuItem.php @@ -10,75 +10,62 @@   * @since 1.8.0   */  class ElggMenuItem { -	/** -	 * @var string Identifier of the menu -	 */ -	protected $name;  	/** -	 * @var string The menu display string +	 * @var array Non-rendered data about the menu item  	 */ -	protected $text; +	protected $data = array( +		// string Identifier of the menu +		'name' => '', -	/** -	 * @var string The menu url -	 */ -	protected $href = null; +		// array Page contexts this menu item should appear on +		'contexts' => array('all'), -	/** -	 * @var string The string to display if link is clicked -	 */ -	protected $confirm = ''; +		// string Menu section identifier +		'section' => 'default', -	/** -	 * @var array Classes to apply to the anchor tag. -	 */ -	protected $linkClass = array(); +		// int Smaller priorities float to the top +		'priority' => 100, -	/** -	 * @var array Classes to apply to the li tag. -	 */ -	protected $itemClass = array(); +		// bool Is this the currently selected menu item +		'selected' => false, -	/** -	 * @var array Page context array -	 */ -	protected $contexts = array('all'); +		// string Identifier of this item's parent +		'parent_name' => '', -	/** -	 * @var string Menu section identifier -	 */ -	protected $section = 'default'; +		// ElggMenuItem The parent object or null +		'parent' => null, -	/** -	 * @var string Tooltip -	 */ -	protected $title = ''; +		// array Array of children objects or empty array +		'children' => array(), -	/** -	 * @var int Menu priority - smaller prioritys float to the top -	 */ -	protected $priority = 100; +		// array Classes to apply to the li tag +		'itemClass' => array(), + +		// array Classes to apply to the anchor tag +		'linkClass' => array(), +	);  	/** -	 * @var bool Is this the currently selected menu item +	 * @var string The menu display string  	 */ -	protected $selected = false; +	protected $text;  	/** -	 * @var string Identifier of this item's parent +	 * @var string The menu url  	 */ -	protected $parent_name = ''; +	protected $href = null;  	/** -	 * @var ElggMenuItem The parent object or null +	 * @var string Tooltip  	 */ -	protected $parent = null; +	protected $title = '';  	/** -	 * @var array Array of children objects or empty array +	 * @var string The string to display if link is clicked  	 */ -	protected $children = array(); +	protected $confirm = ''; +  	/**  	 * ElggMenuItem constructor @@ -88,13 +75,15 @@ class ElggMenuItem {  	 * @param string $href  URL of the menu item (false if not a link)  	 */  	public function __construct($name, $text, $href) { -		$this->name = $name; +		//$this->name = $name;  		$this->text = $text;  		if ($href) {  			$this->href = elgg_normalize_url($href);  		} else {  			$this->href = $href;  		} + +		$this->data['name'] = $name;  	}  	/** @@ -122,6 +111,12 @@ class ElggMenuItem {  			$options['contexts'] = $options['context'];  			unset($options['context']);  		} +		 +		// make sure contexts is set correctly +		if (isset($options['contexts'])) { +			$item->setContext($options['contexts']); +			unset($options['contexts']); +		}  		if (isset($options['link_class'])) {  			$item->setLinkClass($options['link_class']); @@ -132,17 +127,63 @@ class ElggMenuItem {  			$item->setItemClass($options['item_class']);  			unset($options['item_class']);  		} + +		if (isset($options['data']) && is_array($options['data'])) { +			$item->setData($options['data']); +			unset($options['data']); +		}  		foreach ($options as $key => $value) { -			$item->$key = $value; +			if (isset($item->data[$key])) { +				$item->data[$key] = $value; +			} else { +				$item->$key = $value; +			}  		} -		// make sure contexts is set correctly -		if (isset($options['contexts'])) { -			$item->setContext($options['contexts']); +		return $item; +	} + +	/** +	 * Set a data key/value pair or a set of key/value pairs +	 * +	 * This method allows storage of arbitrary data with this menu item. The +	 * data can be used for sorting, custom rendering, or any other use. +	 * +	 * @param mixed $key   String key or an associative array of key/value pairs +	 * @param mixed $value The value if $key is a string +	 * @return void +	 */ +	public function setData($key, $value = null) { +		if (is_array($key)) { +			$this->data += $key; +		} else { +			$this->data[$key] = $value;  		} +	} -		return $item; +	/** +	 * Get stored data +	 * +	 * @param string $key The key for the requested key/value pair +	 * @return mixed +	 */ +	public function getData($key) { +		if (isset($this->data[$key])) { +			return $this->data[$key]; +		} else { +			return null; +		} +	} + +	/** +	 * Set the identifier of the menu item +	 * +	 * @param string Unique identifier +	 * @return void +	 */ +	public function setName($name) { +		$this->data['name'] = $name;  	}  	/** @@ -151,11 +192,21 @@ class ElggMenuItem {  	 * @return string  	 */  	public function getName() { -		return $this->name; +		return $this->data['name']; +	} + +	/** +	 * Set the display text of the menu item +	 *  +	 * @param string $text The display text +	 * @return void +	 */ +	public function setText($text) { +		$this->text = $text;  	}  	/** -	 * Get the display text of the menu +	 * Get the display text of the menu item  	 *  	 * @return string  	 */ @@ -164,6 +215,16 @@ class ElggMenuItem {  	}  	/** +	 * Set the URL of the menu item +	 * +	 * @param string $href URL or false if not a link +	 * @return void +	 */ +	public function setHref($href) { +		$this->href = $href; +	} + +	/**  	 * Get the URL of the menu item  	 *  	 * @return string @@ -176,14 +237,13 @@ class ElggMenuItem {  	 * Set the contexts that this menu item is available for  	 *  	 * @param array $contexts An array of context strings -	 *  	 * @return void  	 */  	public function setContext($contexts) {  		if (is_string($contexts)) {  			$contexts = array($contexts);  		} -		$this->contexts = $contexts; +		$this->data['contexts'] = $contexts;  	}  	/** @@ -192,27 +252,26 @@ class ElggMenuItem {  	 * @return array  	 */  	public function getContext() { -		return $this->contexts; +		return $this->data['contexts'];  	}  	/**  	 * Should this menu item be used given the current context  	 *  	 * @param string $context A context string (default is empty string for -	 *                        current context stack. -	 * +	 *                        current context stack).  	 * @return bool  	 */  	public function inContext($context = '') {  		if ($context) { -			return in_array($context, $this->contexts); +			return in_array($context, $this->data['contexts']);  		} -		if (in_array('all', $this->contexts)) { +		if (in_array('all', $this->data['contexts'])) {  			return true;  		} -		foreach ($this->contexts as $context) { +		foreach ($this->data['contexts'] as $context) {  			if (elgg_in_context($context)) {  				return true;  			} @@ -224,11 +283,10 @@ class ElggMenuItem {  	 * Set the selected flag  	 *  	 * @param bool $state Selected state (default is true) -	 *  	 * @return void  	 */  	public function setSelected($state = true) { -		$this->selected = $state; +		$this->data['selected'] = $state;  	}  	/** @@ -237,14 +295,13 @@ class ElggMenuItem {  	 * @return bool  	 */  	public function getSelected() { -		return $this->selected; +		return $this->data['selected'];  	}  	/**  	 * Set the tool tip text  	 *  	 * @param string $text The text of the tool tip -	 *  	 * @return void  	 */  	public function setTooltip($text) { @@ -264,7 +321,6 @@ class ElggMenuItem {  	 * Set the confirm text shown when link is clicked  	 *  	 * @param string $text The text to show -	 *  	 * @return void  	 */  	public function setConfirmText($text) { @@ -284,14 +340,13 @@ class ElggMenuItem {  	 * Set the anchor class  	 *  	 * @param mixed $class An array of class names, or a single string class name. -	 *  	 * @return void  	 */  	public function setLinkClass($class) {  		if (!is_array($class)) { -			$this->linkClass[] = $class; +			$this->data['linkClass'] = array($class);  		} else { -			$this->linkClass = $class; +			$this->data['linkClass'] = $class;  		}  	} @@ -301,21 +356,34 @@ class ElggMenuItem {  	 * @return string  	 */  	public function getLinkClass() { -		return implode(' ', $this->linkClass); +		return implode(' ', $this->data['linkClass']);  	}  	/** -	 * Set the li classes +	 * Add a link class  	 *  	 * @param mixed $class An array of class names, or a single string class name. +	 * @return void +	 */ +	public function addLinkClass($class) { +		if (!is_array($class)) { +			$this->data['linkClass'][] = $class; +		} else { +			$this->data['linkClass'] += $class; +		} +	} + +	/** +	 * Set the li classes  	 * +	 * @param mixed $class An array of class names, or a single string class name.  	 * @return void  	 */  	public function setItemClass($class) {  		if (!is_array($class)) { -			$this->itemClass[] = $class; +			$this->data['itemClass'] = array($class);  		} else { -			$this->itemClass = $class; +			$this->data['itemClass'] = $class;  		}  	} @@ -325,11 +393,13 @@ class ElggMenuItem {  	 * @return string  	 */  	public function getItemClass() { -		//allow people to specify name with underscores and colons -		$name = str_replace('_', '-', $this->getName()); +		// allow people to specify name with underscores and colons +		$name = strtolower($this->getName()); +		$name = str_replace('_', '-', $name);  		$name = str_replace(':', '-', $name); +		$name = str_replace(' ', '-', $name); -		$class = implode(' ', $this->itemClass); +		$class = implode(' ', $this->data['itemClass']);  		if ($class) {  			return "elgg-menu-item-$name $class";  		} else { @@ -341,11 +411,10 @@ class ElggMenuItem {  	 * Set the priority of the menu item  	 *  	 * @param int $priority The smaller numbers mean higher priority (1 before 100) -	 *  	 * @return void  	 */  	public function setWeight($priority) { -		$this->priority = $priority; +		$this->data['priority'] = $priority;  	}  	/** @@ -354,18 +423,17 @@ class ElggMenuItem {  	 * @return int  	 */  	public function getWeight() { -		return $this->priority; +		return $this->data['priority'];  	}  	/**  	 * Set the section identifier  	 *  	 * @param string $section The identifier of the section -	 *  	 * @return void  	 */  	public function setSection($section) { -		$this->section = $section; +		$this->data['section'] = $section;  	}  	/** @@ -374,18 +442,17 @@ class ElggMenuItem {  	 * @return string  	 */  	public function getSection() { -		return $this->section; +		return $this->data['section'];  	}  	/**  	 * Set the parent identifier  	 * -	 * @param string $parent_name The identifier of the parent ElggMenuItem -	 * +	 * @param string $name The identifier of the parent ElggMenuItem  	 * @return void  	 */ -	public function setParentName($parent_name) { -		$this->parent_name = $parent_name; +	public function setParentName($name) { +		$this->data['parent_name'] = $name;  	}  	/** @@ -394,18 +461,17 @@ class ElggMenuItem {  	 * @return string  	 */  	public function getParentName() { -		return $this->parent_name; +		return $this->data['parent_name'];  	}  	/**  	 * Set the parent menu item  	 *  	 * @param ElggMenuItem $parent -	 *  	 * @return void  	 */  	public function setParent($parent) { -		$this->parent = $parent; +		$this->data['parent'] = $parent;  	}  	/** @@ -414,29 +480,27 @@ class ElggMenuItem {  	 * @return ElggMenuItem or null  	 */  	public function getParent() { -		return $this->parent; +		return $this->data['parent'];  	}  	/**  	 * Add a child menu item  	 *  	 * @param ElggMenuItem $item -	 *  	 * @return void  	 */  	public function addChild($item) { -		$this->children[] = $item; +		$this->data['children'][] = $item;  	}  	/**  	 * Set the menu item's children  	 *  	 * @param array $children Array of ElggMenuItems -	 *  	 * @return void  	 */  	public function setChildren($children) { -		$this->children = $children; +		$this->data['children'] = $children;  	}  	/** @@ -445,27 +509,25 @@ class ElggMenuItem {  	 * @return array  	 */  	public function getChildren() { -		return $this->children; +		return $this->data['children'];  	}  	/**  	 * Sort the children  	 * -	 * @param string $sort_function -	 * +	 * @param string $sortFunction A function that is passed to usort()  	 * @return void  	 */ -	public function sortChildren($sort_function) { -		usort($this->children, $sort_function); +	public function sortChildren($sortFunction) { +		usort($this->data['children'], $sortFunction);  	}  	/**  	 * Get the menu item content (usually a link)  	 *  	 * @params array $vars Options to pass to output/url if a link -	 *  	 * @return string -	 *  +	 *  	 * @todo View code in a model.  How do we feel about that?  	 */  	public function getContent(array $vars = array()) { @@ -474,26 +536,17 @@ class ElggMenuItem {  			return $this->text;  		} -		$vars['text'] = $this->text; -		 -		if ($this->href) { -			$vars['href'] = $this->href; -		} +		$defaults = get_object_vars($this); +		unset($defaults['data']); -		if ($this->linkClass) { -			$vars['class'] = $this->getLinkClass(); -		} -		 -		if ($this->rel) { -			$vars['rel'] = $this->rel; -		} -				 -		if ($this->title) { -			$vars['title'] = $this->title; -		} -		 -		if ($this->is_action) { -			$vars['is_action'] = $this->is_action; +		$vars += $defaults; + +		if ($this->data['linkClass']) { +			if (isset($vars['class'])) { +				$vars['class'] += $this->getLinkClass(); +			} else { +				$vars['class'] = $this->getLinkClass(); +			}  		}  		if ($this->confirm) { diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index 95a7362e2..d837431fc 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -315,9 +315,9 @@ class ElggPlugin extends ElggObject {  			return false;  		}  		// Hook to validate setting -		$value = elgg_trigger_plugin_hook('plugin:setting', 'plugin', array( -			'plugin' => $this->pluginID, -			'plugin_object' => $this, +		$value = elgg_trigger_plugin_hook('setting', 'plugin', array( +			'plugin_id' => $this->pluginID, +			'plugin' => $this,  			'name' => $name,  			'value' => $value  		), $value); @@ -454,10 +454,11 @@ class ElggPlugin extends ElggObject {  		}  		// Hook to validate setting -		// note this doesn't pass the namespaced name! -		$value = elgg_trigger_plugin_hook('plugin:usersetting', 'user', array( +		// note: this doesn't pass the namespaced name +		$value = elgg_trigger_plugin_hook('usersetting', 'plugin', array(  			'user' => $user, -			'plugin' => $this->getID(), +			'plugin' => $this, +			'plugin_id' => $this->getID(),  			'name' => $name,  			'value' => $value  		), $value); @@ -700,6 +701,11 @@ class ElggPlugin extends ElggObject {  //			return false;  //		} +		// include classes +		if ($flags & ELGG_PLUGIN_REGISTER_CLASSES) { +			$this->registerClasses(); +		} +		  		// include start file  		if ($flags & ELGG_PLUGIN_INCLUDE_START) {  			$this->includeFile('start.php'); @@ -715,11 +721,6 @@ class ElggPlugin extends ElggObject {  			$this->registerLanguages();  		} -		// include classes -		if ($flags & ELGG_PLUGIN_REGISTER_CLASSES) { -			$this->registerClasses(); -		} -  		return true;  	} diff --git a/engine/classes/ElggPriorityList.php b/engine/classes/ElggPriorityList.php new file mode 100644 index 000000000..aa33831ff --- /dev/null +++ b/engine/classes/ElggPriorityList.php @@ -0,0 +1,358 @@ +<?php +/** + * Iterate over elements in a specific priority. + * + * $pl = new ElggPriorityList(); + * $pl->add('Element 0'); + * $pl->add('Element 10', 10); + * $pl->add('Element -10', -10); + * + * foreach ($pl as $priority => $element) { + *	var_dump("$priority => $element"); + * } + * + * Yields: + * -10 => Element -10 + * 0 => Element 0 + * 10 => Element 10 + * + * Collisions on priority are handled by inserting the element at or as close to the + * requested priority as possible: + * + * $pl = new ElggPriorityList(); + * $pl->add('Element 5', 5); + * $pl->add('Colliding element 5', 5); + * $pl->add('Another colliding element 5', 5); + * + * foreach ($pl as $priority => $element) { + *	var_dump("$priority => $element"); + * } + * + * Yields: + *	5 => 'Element 5', + *	6 => 'Colliding element 5', + *	7 => 'Another colliding element 5' + * + * You can do priority lookups by element: + * + * $pl = new ElggPriorityList(); + * $pl->add('Element 0'); + * $pl->add('Element -5', -5); + * $pl->add('Element 10', 10); + * $pl->add('Element -10', -10); + * + * $priority = $pl->getPriority('Element -5'); + *  + * Or element lookups by priority. + * $element = $pl->getElement(-5); + * + * To remove elements, pass the element. + * $pl->remove('Element -10'); + * + * To check if an element exists: + * $pl->contains('Element -5'); + * + * To move an element: + * $pl->move('Element -5', -3); + * + * ElggPriorityList only tracks priority. No checking is done in ElggPriorityList for duplicates or + * updating. If you need to track this use objects and an external map: + * + * function elgg_register_something($id, $display_name, $location, $priority = 500) { + *	// $id => $element. + *	static $map = array(); + *	static $list; + * + *	if (!$list) { + *		$list = new ElggPriorityList(); + *	} + * + *	// update if already registered. + *	if (isset($map[$id])) { + *		$element = $map[$id]; + *		// move it first because we have to pass the original element. + *		if (!$list->move($element, $priority)) { + *			return false; + *		} + *		$element->display_name = $display_name; + *		$element->location = $location; + *	} else { + *		$element = new stdClass(); + *		$element->display_name = $display_name; + *		$element->location = $location; + *		if (!$list->add($element, $priority)) { + *			return false; + *		} + *		$map[$id] = $element; + *	} + * + *	return true; + * } + * + * @package Elgg.Core + * @subpackage Helpers + */ +class ElggPriorityList +	implements Iterator, Countable { + +	/** +	 * The list of elements +	 * +	 * @var array +	 */ +	private $elements = array(); + +	/** +	 * Create a new priority list. +	 * +	 * @param array $elements An optional array of priorities => element +	 */ +	public function __construct(array $elements = array()) { +		if ($elements) { +			foreach ($elements as $priority => $element) { +				$this->add($element, $priority); +			} +		} +	} + +	/** +	 * Adds an element to the list. +	 * +	 * @warning This returns the priority at which the element was added, which can be 0. Use +	 *          !== false to check for success. +	 * +	 * @param mixed $element  The element to add to the list. +	 * @param mixed $priority Priority to add the element. In priority collisions, the original element +	 *                        maintains its priority and the new element is to the next available +	 *                        slot, taking into consideration all previously registered elements. +	 *                        Negative elements are accepted. +	 * @return int            The priority of the added element. +	 */ +	public function add($element, $priority = null, $exact = false) { +		if ($priority !== null && !is_numeric($priority)) { +			return false; +		} else { +			$priority = $this->getNextPriority($priority); +		} + +		$this->elements[$priority] = $element; +		$this->sorted = false; +		return $priority; +	} + +	/** +	 * Removes an element from the list. +	 * +	 * @warning The element must have the same attributes / values. If using $strict, it must have +	 *          the same types. array(10) will fail in strict against array('10') (str vs int). +	 * +	 * @param type $element +	 * @return bool +	 */ +	public function remove($element, $strict = false) { +		$index = array_search($element, $this->elements, $strict); +		if ($index !== false) { +			unset($this->elements[$index]); +			return true; +		} else { +			return false; +		} +	} + +	/** +	 * Move an existing element to a new priority. +	 * +	 * @param mixed  $current_priority +	 * @param int    $new_priority +	 * +	 * @return int The new priority. +	 */ +	public function move($element, $new_priority, $strict = false) { +		$new_priority = (int) $new_priority; +		 +		$current_priority = $this->getPriority($element, $strict); +		if ($current_priority === false) { +			return false; +		} + +		if ($current_priority == $new_priority) { +			return true; +		} + +		// move the actual element so strict operations still work +		$element = $this->getElement($current_priority); +		unset($this->elements[$current_priority]); +		return $this->add($element, $new_priority); +	} + +	/** +	 * Returns the elements +	 * +	 * @return array +	 */ +	public function getElements() { +		$this->sortIfUnsorted(); +		return $this->elements; +	} + +	/** +	 * Sort the elements optionally by a callback function. +	 * +	 * If no user function is provided the elements are sorted by priority registered. +	 * +	 * The callback function should accept the array of elements as the first argument and should +	 * return a sorted array. +	 * +	 * This function can be called multiple times. +	 * +	 * @param type $callback +	 * @return bool +	 */ +	public function sort($callback = null) { +		if (!$callback) { +			ksort($this->elements, SORT_NUMERIC); +		} else { +			$sorted = call_user_func($callback, $this->elements); + +			if (!$sorted) { +				return false; +			} + +			$this->elements = $sorted; +		} +		 +		$this->sorted = true; +		return true; +	} + +	/** +	 * Sort the elements if they haven't been sorted yet. +	 * +	 * @return bool +	 */ +	private function sortIfUnsorted() { +		if (!$this->sorted) { +			return $this->sort(); +		} +	} + +	/** +	 * Returns the next priority available. +	 * +	 * @param int $near Make the priority as close to $near as possible. +	 * @return int +	 */ +	public function getNextPriority($near = 0) { +		$near = (int) $near; +		 +		while (array_key_exists($near, $this->elements)) { +			$near++; +		} + +		return $near; +	} + +	/** +	 * Returns the priority of an element if it exists in the list. +	 *  +	 * @warning This can return 0 if the element's priority is 0. +	 * +	 * @param mixed $element The element to check for. +	 * @param bool  $strict  Use strict checking? +	 * @return mixed False if the element doesn't exists, the priority if it does. +	 */ +	public function getPriority($element, $strict = false) { +		return array_search($element, $this->elements, $strict); +	} + +	/** +	 * Returns the element at $priority. +	 * +	 * @param int $priority +	 * @return mixed The element or false on fail. +	 */ +	public function getElement($priority) { +		return (isset($this->elements[$priority])) ? $this->elements[$priority] : false; +	} + +	/** +	 * Returns if the list contains $element. +	 * +	 * @param mixed $element The element to check. +	 * @param bool  $strict  Use strict checking? +	 * @return bool +	 */ +	public function contains($element, $strict = false) { +		return $this->getPriority($element, $strict) !== false; +	} + +	 +	/********************** +	 * Interface methods * +	 **********************/ + +	/** +	 * Iterator +	 */ + +	/** +	 * PHP Iterator Interface +	 * +	 * @see Iterator::rewind() +	 * @return void +	 */ +	public function rewind() { +		$this->sortIfUnsorted(); +		return rewind($this->elements); +	} + +	/** +	 * PHP Iterator Interface +	 * +	 * @see Iterator::current() +	 * @return mixed +	 */ +	public function current() { +		$this->sortIfUnsorted(); +		return current($this->elements); +	} + +	/** +	 * PHP Iterator Interface +	 * +	 * @see Iterator::key() +	 * @return int +	 */ +	public function key() { +		$this->sortIfUnsorted(); +		return key($this->elements); +	} + +	/** +	 * PHP Iterator Interface +	 * +	 * @see Iterator::next() +	 * @return mixed +	 */ +	public function next() { +		$this->sortIfUnsorted(); +		return next($this->elements); +	} + +	/** +	 * PHP Iterator Interface +	 * +	 * @see Iterator::valid() +	 * @return bool +	 */ +	public function valid() { +		$this->sortIfUnsorted(); +		$key = key($this->elements); +		return ($key !== NULL && $key !== FALSE); +	} + +	// Countable +	public function count() { +		return count($this->elements); +	} +}
\ No newline at end of file diff --git a/engine/classes/ElggSite.php b/engine/classes/ElggSite.php index e3b8b8f1a..40bfca060 100644 --- a/engine/classes/ElggSite.php +++ b/engine/classes/ElggSite.php @@ -410,8 +410,9 @@ class ElggSite extends ElggEntity {  			'register',  			'action/register',  			'forgotpassword', -			'action/user/requestnewpassword',  			'resetpassword', +			'action/user/requestnewpassword', +			'action/user/passwordreset',  			'upgrade\.php',  			'xml-rpc\.php',  			'mt/mt-xmlrpc\.cgi', diff --git a/engine/classes/ElggUser.php b/engine/classes/ElggUser.php index 1af4cdc3a..75ac008f6 100644 --- a/engine/classes/ElggUser.php +++ b/engine/classes/ElggUser.php @@ -484,7 +484,8 @@ class ElggUser extends ElggEntity  	 * @return array|false  	 */  	public function getCollections($subtype = "", $limit = 10, $offset = 0) { -		return get_user_collections($this->getGUID(), $subtype, $limit, $offset); +		elgg_deprecated_notice("ElggUser::getCollections() has been deprecated", 1.8); +		return false;  	}  	/** diff --git a/engine/lib/actions.php b/engine/lib/actions.php index ff598916f..4ccffd267 100644 --- a/engine/lib/actions.php +++ b/engine/lib/actions.php @@ -384,7 +384,8 @@ function actions_init() {   */  function elgg_is_xhr() {  	return isset($_SERVER['HTTP_X_REQUESTED_WITH']) -		&& strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; +		&& strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' || +		get_input('X-Requested-With') === 'XMLHttpRequest';  }  /** diff --git a/engine/lib/admin.php b/engine/lib/admin.php index 3bfb69102..c16da9295 100644 --- a/engine/lib/admin.php +++ b/engine/lib/admin.php @@ -348,7 +348,7 @@ function elgg_admin_add_plugin_settings_menu() {  				'parent_name' => 'settings',  				'context' => 'admin',  				'section' => 'configure', -		 )); +			));  		}  	}  } diff --git a/engine/lib/configuration.php b/engine/lib/configuration.php index cbc083bd0..b756d2e70 100644 --- a/engine/lib/configuration.php +++ b/engine/lib/configuration.php @@ -282,8 +282,8 @@ function datalist_set($name, $value) {  		return false;  	} -	$name = sanitise_string($name); -	$value = sanitise_string($value); +	$sanitised_name = sanitise_string($name); +	$sanitised_value = sanitise_string($value);  	// If memcache is available then invalidate the cached copy  	static $datalist_memcache; @@ -295,13 +295,16 @@ function datalist_set($name, $value) {  		$datalist_memcache->delete($name);  	} -	insert_data("INSERT into {$CONFIG->dbprefix}datalists" -		. " set name = '{$name}', value = '{$value}'" -		. " ON DUPLICATE KEY UPDATE value='{$value}'"); - -	$DATALIST_CACHE[$name] = $value; +	$success = insert_data("INSERT into {$CONFIG->dbprefix}datalists" +		. " set name = '{$sanitised_name}', value = '{$sanitised_value}'" +		. " ON DUPLICATE KEY UPDATE value='{$sanitised_value}'"); -	return true; +	if ($success) { +		$DATALIST_CACHE[$name] = $value; +		return true; +	} else { +		return false; +	}  }  /** diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php index d92257a09..ff4fa0756 100644 --- a/engine/lib/deprecated-1.8.php +++ b/engine/lib/deprecated-1.8.php @@ -20,7 +20,7 @@   * @param string $fromdir Optional directory to load upgrades from. default: engine/schema/upgrades/   * @param bool   $quiet   If true, suppress all error messages. Only use for the upgrade from <=1.6.   * - * @return bool + * @return int The number of upgrades run.   * @see upgrade.php   * @see version.php   * @deprecated 1.8 Use PHP upgrades for sql changes. @@ -35,6 +35,8 @@ function db_upgrade($version, $fromdir = "", $quiet = FALSE) {  	if (!$fromdir) {  		$fromdir = $CONFIG->path . 'engine/schema/upgrades/';  	} +	 +	$i = 0;  	if ($handle = opendir($fromdir)) {  		$sqlupgrades = array(); @@ -65,11 +67,12 @@ function db_upgrade($version, $fromdir = "", $quiet = FALSE) {  				} else {  					run_sql_script($fromdir . $sqlfile);  				} +				$i++;  			}  		}  	} -	return TRUE; +	return $i;  }  /** diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index df78515f2..198ffe60c 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -167,12 +167,12 @@ function forward($location = "", $reason = 'system') {   * @param string $name     An identifier for the JavaScript library   * @param string $url      URL of the JavaScript file   * @param string $location Page location: head or footer. (default: head) - * @param int    $priority Priority of the CSS file (lower numbers load earlier) + * @param int    $priority Priority of the JS file (lower numbers load earlier)   *   * @return bool   * @since 1.8.0   */ -function elgg_register_js($name, $url, $location = 'head', $priority = 500) { +function elgg_register_js($name, $url, $location = 'head', $priority = null) {  	return elgg_register_external_file('js', $name, $url, $location, $priority);  } @@ -225,7 +225,7 @@ function elgg_get_loaded_js($location = 'head') {   * @return bool   * @since 1.8.0   */ -function elgg_register_css($name, $url, $priority = 500) { +function elgg_register_css($name, $url, $priority = null) {  	return elgg_register_external_file('css', $name, $url, 'head', $priority);  } @@ -278,7 +278,7 @@ function elgg_get_loaded_css() {   * @return bool   * @since 1.8.0   */ -function elgg_register_external_file($type, $name, $url, $location, $priority = 500) { +function elgg_register_external_file($type, $name, $url, $location, $priority = null) {  	global $CONFIG;  	if (empty($name) || empty($url)) { @@ -288,32 +288,36 @@ function elgg_register_external_file($type, $name, $url, $location, $priority =  	$url = elgg_format_url($url);  	$url = elgg_normalize_url($url); -	if (!isset($CONFIG->externals)) { -		$CONFIG->externals = array(); -	} - -	if (!isset($CONFIG->externals[$type])) { -		$CONFIG->externals[$type] = array(); -	} +	elgg_bootstrap_externals_data_structure($type);  	$name = trim(strtolower($name)); - -	if (isset($CONFIG->externals[$type][$name])) { -		// update a registered item -		$item = $CONFIG->externals[$type][$name]; - +	$priority = max((int)$priority, 0); +	$item = elgg_extract($name, $CONFIG->externals_map[$type]); + +	if ($item) { +		// updating a registered item +		// don't update loaded because it could already be set +		$item->url = $url; +		$item->location = $location; + +		// if loaded before registered, that means it hasn't been added to the list yet +		if ($CONFIG->externals[$type]->contains($item)) { +			$priority = $CONFIG->externals[$type]->move($item, $priority); +		} else { +			$priority = $CONFIG->externals[$type]->add($item, $priority); +		}  	} else {  		$item = new stdClass();  		$item->loaded = false; -	} +		$item->url = $url; +		$item->location = $location; -	$item->url = $url; -	$item->priority = max((int)$priority, 0); -	$item->location = $location; +		$priority = $CONFIG->externals[$type]->add($item, $priority); +	} -	$CONFIG->externals[$type][$name] = $item; +	$CONFIG->externals_map[$type][$name] = $item; -	return true; +	return $priority !== false;  }  /** @@ -328,19 +332,14 @@ function elgg_register_external_file($type, $name, $url, $location, $priority =  function elgg_unregister_external_file($type, $name) {  	global $CONFIG; -	if (!isset($CONFIG->externals)) { -		return false; -	} - -	if (!isset($CONFIG->externals[$type])) { -		return false; -	} +	elgg_bootstrap_externals_data_structure($type);  	$name = trim(strtolower($name)); -	 -	if (array_key_exists($name, $CONFIG->externals[$type])) { -		unset($CONFIG->externals[$type][$name]); -		return true; +	$item = elgg_extract($name, $CONFIG->externals_map[$type]); + +	if ($item) { +		unset($CONFIG->externals_map[$type][$name]); +		return $CONFIG->externals[$type]->remove($item);  	}  	return false; @@ -358,27 +357,23 @@ function elgg_unregister_external_file($type, $name) {  function elgg_load_external_file($type, $name) {  	global $CONFIG; -	if (!isset($CONFIG->externals)) { -		$CONFIG->externals = array(); -	} - -	if (!isset($CONFIG->externals[$type])) { -		$CONFIG->externals[$type] = array(); -	} +	elgg_bootstrap_externals_data_structure($type);  	$name = trim(strtolower($name)); -	if (isset($CONFIG->externals[$type][$name])) { +	$item = elgg_extract($name, $CONFIG->externals_map[$type]); + +	if ($item) {  		// update a registered item -		$CONFIG->externals[$type][$name]->loaded = true; +		$item->loaded = true;  	} else {  		$item = new stdClass();  		$item->loaded = true;  		$item->url = '';  		$item->location = ''; -		$item->priority = 500; -		$CONFIG->externals[$type][$name] = $item; +		$priority = $CONFIG->externals[$type]->add($item); +		$CONFIG->externals_map[$type][$name] = $item;  	}  } @@ -394,13 +389,12 @@ function elgg_load_external_file($type, $name) {  function elgg_get_loaded_external_files($type, $location) {  	global $CONFIG; -	if (isset($CONFIG->externals) && isset($CONFIG->externals[$type])) { -		$items = array_values($CONFIG->externals[$type]); +	if (isset($CONFIG->externals) && $CONFIG->externals[$type] instanceof ElggPriorityList) { +		$items = $CONFIG->externals[$type]->getElements();  		$callback = "return \$v->loaded == true && \$v->location == '$location';";  		$items = array_filter($items, create_function('$v', $callback));  		if ($items) { -			usort($items, create_function('$a,$b','return $a->priority >= $b->priority;'));  			array_walk($items, create_function('&$v,$k', '$v = $v->url;'));  		}  		return $items; @@ -409,6 +403,31 @@ function elgg_get_loaded_external_files($type, $location) {  }  /** + * Bootstraps the externals data structure in $CONFIG. + * + * @param string $type The type of external, js or css. + */ +function elgg_bootstrap_externals_data_structure($type) { +	global $CONFIG; + +	if (!isset($CONFIG->externals)) { +		$CONFIG->externals = array(); +	} + +	if (!$CONFIG->externals[$type] instanceof ElggPriorityList) { +		$CONFIG->externals[$type] = new ElggPriorityList(); +	} + +	if (!isset($CONFIG->externals_map)) { +		$CONFIG->externals_map = array(); +	} + +	if (!isset($CONFIG->externals_map[$type])) { +		$CONFIG->externals_map[$type] = array(); +	} +} + +/**   * Returns a list of files in $directory.   *   * Only returns files.  Does not recurse into subdirs. @@ -1678,17 +1697,26 @@ function elgg_normalise_plural_options_array($options, $singulars) {   * useful.  Servers will hold pages until processing is done before sending   * them out to the browser.   * + * @see http://www.php.net/register-shutdown-function + *   * @return void   * @see register_shutdown_hook()   */  function _elgg_shutdown_hook() {  	global $START_MICROTIME; -	elgg_trigger_event('shutdown', 'system'); +	try { +		elgg_trigger_event('shutdown', 'system'); -	$time = (float)(microtime(TRUE) - $START_MICROTIME); -	// demoted to NOTICE from DEBUG so javascript is not corrupted -	elgg_log("Page {$_SERVER['REQUEST_URI']} generated in $time seconds", 'NOTICE'); +		$time = (float)(microtime(TRUE) - $START_MICROTIME); +		// demoted to NOTICE from DEBUG so javascript is not corrupted +		elgg_log("Page {$_SERVER['REQUEST_URI']} generated in $time seconds", 'NOTICE'); +	} catch (Exception $e) { +		$message = 'Error: ' . get_class($e) . ' thrown within the shutdown handler. '; +		$message .= "Message: '{$e->getMessage()}' in file {$e->getFile()} (line {$e->getLine()})"; +		error_log($message); +		error_log("Exception trace stack: {$e->getTraceAsString()}"); +	}  }  /** diff --git a/engine/lib/entities.php b/engine/lib/entities.php index cb197b569..10313fc8c 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -1190,10 +1190,10 @@ $time_created_lower = NULL, $time_updated_upper = NULL, $time_updated_lower = NU   * @internal This also provides the views for elgg_view_annotation().   *   * @param array $options Any options from $getter options plus: - * 	 full_view => BOOL Display full view entities - * 	 list_type_toggle => BOOL Display gallery / list switch - * 	 pagination => BOOL Display pagination links - *   gallery => BOOL display in gallery view + *	full_view => BOOL Display full view entities + *	list_type => STR 'list' or 'gallery' + *	list_type_toggle => BOOL Display gallery / list switch + *	pagination => BOOL Display pagination links   *   * @param mixed $getter  The entity getter function to use to fetch the entities   * @param mixed $viewer  The function to use to view the entity list. @@ -1216,7 +1216,6 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti  		'full_view' => TRUE,  		'list_type_toggle' => FALSE,  		'pagination' => TRUE, -		'gallery' => FALSE,  	);  	$options = array_merge($defaults, $options); diff --git a/engine/lib/group.php b/engine/lib/group.php index 755482b00..e7b70fd10 100644 --- a/engine/lib/group.php +++ b/engine/lib/group.php @@ -294,6 +294,7 @@ function group_gatekeeper($forward = true) {   * @param bool   $default_on True if this option should be active by default   *   * @return void + * @since 1.5.0   */  function add_group_tool_option($name, $label, $default_on = true) {  	global $CONFIG; @@ -319,6 +320,7 @@ function add_group_tool_option($name, $label, $default_on = true) {   * @param string $name Name of the group tool option   *   * @return void + * @since 1.7.5   */  function remove_group_tool_option($name) {  	global $CONFIG; diff --git a/engine/lib/input.php b/engine/lib/input.php index 84752bc7d..56ec214dc 100644 --- a/engine/lib/input.php +++ b/engine/lib/input.php @@ -10,8 +10,13 @@  /**   * Get some input from variables passed on the GET or POST line.   * + * If using any data obtained from get_input() in a web page, please be aware that + * it is a possible vector for a reflected XSS attack. If you are expecting an + * integer, cast it to an int. If it is a string, escape quotes. + *   * Note: this function does not handle nested arrays (ex: form input of param[m][n])   * because of the filtering done in htmlawed from the filter_tags call. + * @todo Is this ^ still?   *   * @param string $variable      The variable we want to return.   * @param mixed  $default       A default value for the variable if it is not found. diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php index b51c63b49..cefe40ecf 100644 --- a/engine/lib/navigation.php +++ b/engine/lib/navigation.php @@ -154,6 +154,44 @@ function elgg_is_menu_item_registered($menu_name, $item_name) {  }  /** + * Convenience function for registering a button to title menu + * + * The URL must be $handler/$name/$guid where $guid is the guid of the page owner. + * The label of the button is "$handler:$name" so that must be defined in a + * language file. + * + * This is used primarily to support adding an add content button + * + * @param string $handler The handler to use or null to autodetect from context + * @param string $name    Name of the button + * @return void + * @since 1.8.0 + */ +function elgg_register_title_button($handler = null, $name = 'add') { +	if (elgg_is_logged_in()) { + +		if (!$handler) { +			$handler = elgg_get_context(); +		} + +		$owner = elgg_get_page_owner_entity(); +		if (!$owner) { +			// no owns the page so this is probably an all site list page +			$owner = elgg_get_logged_in_user_entity(); +		} +		if ($owner && $owner->canWriteToContainer()) { +			$guid = $owner->getGUID(); +			elgg_register_menu_item('title', array( +				'name' => $name, +				'href' => "$handler/$name/$guid", +				'text' => elgg_echo("$handler:$name"), +				'link_class' => 'elgg-button elgg-button-action', +			)); +		} +	} +} + +/**   * Adds a breadcrumb to the breadcrumbs stack.   *   * @param string $title The title to display @@ -276,7 +314,7 @@ function elgg_river_menu_setup($hook, $type, $return, $params) {  					'href' => "#comments-add-$object->guid",  					'text' => elgg_view_icon('speech-bubble'),  					'title' => elgg_echo('comment:this'), -					'link_class' => "elgg-toggler", +					'rel' => 'toggle',  					'priority' => 50,  				);  				$return[] = ElggMenuItem::factory($options); @@ -335,12 +373,38 @@ function elgg_entity_menu_setup($hook, $type, $return, $params) {  }  /** + * Adds a delete link to "generic_comment" annotations + */ +function elgg_annotation_menu_setup($hook, $type, $return, $params) { +	$annotation = $params['annotation']; + +	if ($annotation->name == 'generic_comment' && $annotation->canEdit()) { +		$url = elgg_http_add_url_query_elements('action/comments/delete', array( +			'annotation_id' => $annotation->id, +		)); + +		$options = array( +			'name' => 'delete', +			'href' => $url, +			'text' => "<span class=\"elgg-icon elgg-icon-delete\"></span>", +			'confirm' => elgg_echo('deleteconfirm'), +			'text_encode' => false +		); +		$return[] = ElggMenuItem::factory($options); +	} + +	return $return; +} + + +/**   * Navigation initialization   */  function elgg_nav_init() {  	elgg_register_plugin_hook_handler('prepare', 'menu:site', 'elgg_site_menu_setup');  	elgg_register_plugin_hook_handler('register', 'menu:river', 'elgg_river_menu_setup');  	elgg_register_plugin_hook_handler('register', 'menu:entity', 'elgg_entity_menu_setup'); +	elgg_register_plugin_hook_handler('register', 'menu:annotation', 'elgg_annotation_menu_setup');  }  elgg_register_event_handler('init', 'system', 'elgg_nav_init'); diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php index 5224efaf1..9d5fd39b6 100644 --- a/engine/lib/relationships.php +++ b/engine/lib/relationships.php @@ -593,38 +593,31 @@ function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par  }  /** - * An event listener which will notify users based on certain events. + * Notify user that someone has friended them   * - * @param string $event       Event name - * @param string $object_type Object type - * @param mixed  $object      Object + * @param string $event  Event name + * @param string $type   Object type + * @param mixed  $object Object   *   * @return bool   */ -function relationship_notification_hook($event, $object_type, $object) { -	global $CONFIG; +function relationship_notification_hook($event, $type, $object) { -	if ( -		($object instanceof ElggRelationship) && -		($event == 'create') && -		($object_type == 'friend') -	) { -		$user_one = get_entity($object->guid_one); -		$user_two = get_entity($object->guid_two); +	$user_one = get_entity($object->guid_one); +	$user_two = get_entity($object->guid_two); -		// Notify target user -		return notify_user($object->guid_two, $object->guid_one, +	return notify_user($object->guid_two, +			$object->guid_one,  			elgg_echo('friend:newfriend:subject', array($user_one->name)),  			elgg_echo("friend:newfriend:body", array($user_one->name, $user_one->getURL())) -		); -	} +	);  } -/** Register the import hook */ +// Register the import hook  elgg_register_plugin_hook_handler("import", "all", "import_relationship_plugin_hook", 3); -/** Register the hook, ensuring entities are serialised first */ +// Register the hook, ensuring entities are serialised first  elgg_register_plugin_hook_handler("export", "all", "export_relationship_plugin_hook", 3); -/** Register event to listen to some events **/ +// Register event to listen to some events  elgg_register_event_handler('create', 'friend', 'relationship_notification_hook'); diff --git a/engine/lib/river.php b/engine/lib/river.php index 36dde7f05..64ddcfdc1 100644 --- a/engine/lib/river.php +++ b/engine/lib/river.php @@ -185,6 +185,9 @@ function elgg_delete_river(array $options = array()) {  	$query = "DELETE rv.* FROM {$CONFIG->dbprefix}river rv "; +	// remove identical join clauses +	$joins = array_unique($options['joins']); +	  	// add joins  	foreach ($joins as $j) {  		$query .= " $j "; @@ -469,7 +472,7 @@ function elgg_get_river_type_subtype_where_sql($table, $types, $subtypes, $pairs  		}  		if (is_array($wheres) && count($wheres)) { -			$wheres = array(implode(' AND ', $wheres)); +			$wheres = array(implode(' OR ', $wheres));  		}  	} else {  		// using type/subtype pairs @@ -589,10 +592,13 @@ function elgg_river_page_handler($page) {  	elgg_set_page_owner_guid(elgg_get_logged_in_user_guid()); +	// make a URL segment available in page handler script  	$page_type = elgg_extract(0, $page, 'all'); +	$page_type = preg_replace('[\W]', '', $page_type);  	if ($page_type == 'owner') {  		$page_type = 'mine';  	} +	set_input('page_type', $page_type);  	// content filter code here  	$entity_type = ''; diff --git a/engine/lib/statistics.php b/engine/lib/statistics.php index cd2b7a6a1..1232c6128 100644 --- a/engine/lib/statistics.php +++ b/engine/lib/statistics.php @@ -96,7 +96,7 @@ function get_number_users($show_deactivated = false) {    */  function get_online_users() {  	$offset = get_input('offset', 0); -	$count = count(find_active_users(600, 9999)); +	$count = find_active_users(600, 10, $offset, true);  	$objects = find_active_users(600, 10, $offset);  	if ($objects) { diff --git a/engine/lib/upgrade.php b/engine/lib/upgrade.php index 85810956b..dc3911062 100644 --- a/engine/lib/upgrade.php +++ b/engine/lib/upgrade.php @@ -20,15 +20,15 @@ function upgrade_code($version, $quiet = FALSE) {  	$version = (int) $version;  	$upgrade_path = elgg_get_config('path') . 'engine/lib/upgrades/'; -	$processed_upgrades = unserialize(datalist_get('processed_upgrades')); -	// the day we started the new upgrade names -	$upgrade_epoch = 2011021700; +	$processed_upgrades = elgg_get_processed_upgrades(); +	// upgrading from 1.7 to 1.8. Need to bootstrap.  	if (!$processed_upgrades) { -		$processed_upgrades = array(); -	} +		elgg_upgrade_bootstrap_17_to_18(); -	$upgrades = array(); +		// grab accurate processed upgrades +		$processed_upgrades = elgg_get_processed_upgrades(); +	}  	$upgrade_files = elgg_get_upgrade_files($upgrade_path); @@ -36,23 +36,7 @@ function upgrade_code($version, $quiet = FALSE) {  		return false;  	} -	// if before the new upgrade system, run through all upgrades and check -	// version number. After the upgrade epoch, pull run upgrades from db -	if ($version < $upgrade_epoch) { -		foreach ($upgrade_files as $upgrade_file) { -			$upgrade_version = elgg_get_upgrade_file_version($upgrade_file); - -			if ($version < $upgrade_version) { -				$upgrades[] = $upgrade_file; -			} else { -				// set this upgrade as processed so that we don't run it again -				$processed_upgrades[] = $upgrade_file; -			} -		} -	} else { -		// add any upgrades that haven't been run to the upgrades list -		$upgrades = elgg_get_unprocessed_upgrades($upgrade_files, $processed_upgrades); -	} +	$upgrades = elgg_get_unprocessed_upgrades($upgrade_files, $processed_upgrades);  	// Sort and execute  	sort($upgrades); @@ -67,7 +51,7 @@ function upgrade_code($version, $quiet = FALSE) {  			try {  				if (!@include("$upgrade_path/$upgrade")) {  					$success = false; -					error_log($e->getmessage()); +					error_log("Could not include $upgrade_path/$upgrade");  				}  			} catch (Exception $e) {  				$success = false; @@ -76,6 +60,7 @@ function upgrade_code($version, $quiet = FALSE) {  		} else {  			if (!include("$upgrade_path/$upgrade")) {  				$success = false; +				error_log("Could not include $upgrade_path/$upgrade");  			}  		} @@ -84,13 +69,12 @@ function upgrade_code($version, $quiet = FALSE) {  			$processed_upgrades[] = $upgrade;  			// don't set the version to a lower number in instances where an upgrade -			// has been merged from a lower version +			// has been merged from a lower version of Elgg  			if ($upgrade_version > $version) {  				datalist_set('version', $upgrade_version);  			} -			$processed_upgrades = array_unique($processed_upgrades); -			datalist_set('processed_upgrades', serialize($processed_upgrades)); +			elgg_set_processed_upgrades($processed_upgrades);  		} else {  			return false;  		} @@ -100,6 +84,29 @@ function upgrade_code($version, $quiet = FALSE) {  }  /** + * Saves the processed upgrades to a dataset. + * + * @param array $processed_upgrades An array of processed upgrade filenames + *                                  (not the path, just the file) + * @return bool + */ +function elgg_set_processed_upgrades(array $processed_upgrades) { +	$processed_upgrades = array_unique($processed_upgrades); +	return datalist_set('processed_upgrades', serialize($processed_upgrades)); +} + +/** + * Gets a list of processes upgrades + * + * @return mixed Array of processed upgrade filenames or false + */ +function elgg_get_processed_upgrades() { +	$upgrades = datalist_get('processed_upgrades'); +	$unserialized = unserialize($upgrades); +	return $unserialized; +} + +/**   * Returns the version of the upgrade filename.   *   * @param string $filename The upgrade filename. No full path. @@ -248,3 +255,46 @@ function version_upgrade() {  	return false;  } + +/** + * Boot straps into 1.8 upgrade system from 1.7 + * + * This runs all the 1.7 upgrades, then sets the processed_upgrades to all existing 1.7 upgrades. + * Control is then passed back to the main upgrade function which detects and runs the + * 1.8 upgrades, regardless of filename convention. + * + * @return bool + */ +function elgg_upgrade_bootstrap_17_to_18() { +	$db_version = (int) datalist_get('version'); + +	// the 1.8 upgrades before the upgrade system change that are interspersed with 1.7 upgrades. +	$upgrades_18 = array( +		'2010111501.php', +		'2010121601.php', +		'2010121602.php', +		'2010121701.php', +		'2010123101.php', +		'2011010101.php', +	); + +	$upgrades_17 = array(); +	$upgrade_files = elgg_get_upgrade_files(); +	$processed_upgrades = array(); + +	foreach ($upgrade_files as $upgrade_file) { +		// ignore if not in 1.7 format or if it's a 1.8 upgrade +		if (in_array($upgrade_file, $upgrades_18) || !preg_match("/[0-9]{10}\.php/", $upgrade_file)) { +			continue; +		} + +		$upgrade_version = elgg_get_upgrade_file_version($upgrade_file); + +		// this has already been run in a previous 1.7.X -> 1.7.X upgrade +		if ($upgrade_version < $db_version) { +			$processed_upgrades[] = $upgrade_file; +		} +	} + +	return elgg_set_processed_upgrades($processed_upgrades); +} diff --git a/engine/lib/upgrades/2011010101.php b/engine/lib/upgrades/2011010101.php index 9dbaff1e4..b063c249b 100644 --- a/engine/lib/upgrades/2011010101.php +++ b/engine/lib/upgrades/2011010101.php @@ -87,7 +87,10 @@ if ($upgrade_version > $version) {  	datalist_set('version', $upgrade_version);  } +// add ourselves to the processed_upgrades. +$processed_upgrades[] = '2011010101.php'; +  $processed_upgrades = array_unique($processed_upgrades); -datalist_set('processed_upgrades', serialize($processed_upgrades)); +elgg_set_processed_upgrades($processed_upgrades);  forward('upgrade.php'); diff --git a/engine/lib/users.php b/engine/lib/users.php index 59bfa1259..48f10f974 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -625,31 +625,37 @@ function get_user_by_email($email) {  /**   * A function that returns a maximum of $limit users who have done something within the last - * $seconds seconds. + * $seconds seconds or the total count of active users.   *   * @param int $seconds Number of seconds (default 600 = 10min)   * @param int $limit   Limit, default 10. - * @param int $offset  Offset, defualt 0. + * @param int $offset  Offset, default 0. + * @param bool $count  Count, default false.   *   * @return mixed   */ -function find_active_users($seconds = 600, $limit = 10, $offset = 0) { -	global $CONFIG; - +function find_active_users($seconds = 600, $limit = 10, $offset = 0, $count = false) {  	$seconds = (int)$seconds;  	$limit = (int)$limit;  	$offset = (int)$offset; +	$params = array('seconds' => $seconds, 'limit' => $limit, 'offset' => $offset, 'count' => $count); +	$data = elgg_trigger_plugin_hook('find_active_users', 'system', $params, NULL); +	if (!$data) { +		global $CONFIG; -	$time = time() - $seconds; - -	$access = get_access_sql_suffix("e"); +		$time = time() - $seconds; -	$query = "SELECT distinct e.* from {$CONFIG->dbprefix}entities e -		join {$CONFIG->dbprefix}users_entity u on e.guid = u.guid -		where u.last_action >= {$time} and $access -		order by u.last_action desc limit {$offset}, {$limit}"; - -	return get_data($query, "entity_row_to_elggstar"); +		$data = elgg_get_entities(array( +			'type' => 'user',  +			'limit' => $limit, +			'offset' => $offset, +			'count' => $count, +			'joins' => array("join {$CONFIG->dbprefix}users_entity u on e.guid = u.guid"), +			'wheres' => array("u.last_action >= {$time}"), +			'order_by' => "u.last_action desc" +		)); +	} +	return $data;  }  /** @@ -1377,7 +1383,10 @@ function elgg_profile_fields_setup() {  function elgg_avatar_page_handler($page) {  	global $CONFIG; -	set_input('username', $page[1]); +	$user = get_user_by_username($page[1]); +	if ($user) { +		elgg_set_page_owner_guid($user->getGUID()); +	}  	if ($page[0] == 'edit') {  		require_once("{$CONFIG->path}pages/avatar/edit.php"); diff --git a/engine/lib/views.php b/engine/lib/views.php index 45b2c35f8..fe3265347 100644 --- a/engine/lib/views.php +++ b/engine/lib/views.php @@ -309,6 +309,11 @@ function elgg_view_exists($view, $viewtype = '', $recurse = true) {  		}  	} +	// Now check if the default view exists if the view is registered as a fallback +	if ($viewtype != 'default' && elgg_does_viewtype_fallback($viewtype)) { +		return elgg_view_exists($view, 'default'); +	} +  	return false;  } @@ -950,9 +955,10 @@ function elgg_view_annotation(ElggAnnotation $annotation, array $vars = array(),   *		'offset'           The current indexing offset   *		'limit'            The number of entities to display per page   *		'full_view'        Display the full view of the entities? - *		'list_class'       CSS Class applied to the list + *		'list_class'       CSS class applied to the list + *		'item_class'       CSS class applied to the list items   *		'pagination'       Display pagination? - *		'gallery'          Display as gallery? + *		'list_type'        List type: 'list' (default), 'gallery'   *		'list_type_toggle' Display the list type toggle?   *   * @return string The rendered list of entities @@ -965,14 +971,21 @@ $list_type_toggle = true, $pagination = true) {  		$offset = (int)get_input('offset', 0);  	} +	// list type can be passed as request parameter +	$list_type = get_input('list_type', 'list'); +	if (get_input('listtype')) { +		elgg_deprecated_notice("'listtype' has been deprecated by 'list_type' for lists", 1.8); +		$list_type = get_input('listtype'); +	} +  	if (is_array($vars)) {  		// new function  		$defaults = array(  			'items' => $entities, -			'list_class' => 'elgg-entity-list', +			'list_class' => 'elgg-list-entity',  			'full_view' => true,  			'pagination' => true, -			'gallery' => false, +			'list_type' => $list_type,  			'list_type_toggle' => false,  			'offset' => $offset,  		); @@ -990,18 +1003,13 @@ $list_type_toggle = true, $pagination = true) {  			'limit' => (int) $limit,  			'full_view' => $full_view,  			'pagination' => $pagination, -			'gallery' => false, +			'list_type' => $list_type,  			'list_type_toggle' => $list_type_toggle, -			'list_class' => 'elgg-entity-list', +			'list_class' => 'elgg-list-entity',  		);  	} -	$listtype = get_input('listtype', 'list'); -	if ($listtype != 'list') { -		$vars['gallery'] = true; -	} - -	if ($vars['gallery']) { +	if ($vars['list_type'] != 'list') {  		return elgg_view('page/components/gallery', $vars);  	} else {  		return elgg_view('page/components/list', $vars); @@ -1073,21 +1081,24 @@ function elgg_view_entity_annotations(ElggEntity $entity, $full_view = true) {  }  /** - * Returns a rendered title. + * Renders a title.   *   * This is a shortcut for {@elgg_view page/elements/title}.   * - * @param string $title   The page title - * @param string $submenu Should a submenu be displayed? (deprecated) + * @param string $title The page title + * @param string $vars  View variables (was submenu be displayed? (deprecated))   *   * @return string The HTML (etc)   */ -function elgg_view_title($title, $submenu = false) { -	if ($submenu !== false) { +function elgg_view_title($title, $vars = array()) { +	if (!is_array($vars)) {  		elgg_deprecated_notice('setting $submenu in elgg_view_title() is deprecated', 1.8); +		$vars = array('submenu' => $vars);  	} -	return elgg_view('page/elements/title', array('title' => $title, 'submenu' => $submenu)); +	$vars['title'] = $title; + +	return elgg_view('page/elements/title', $vars);  }  /** @@ -1203,7 +1214,7 @@ function elgg_view_river_item($item, array $vars = array()) {  	$vars['item'] = $item; -	return elgg_view('river/item', $vars); +	return elgg_view($item->getView(), $vars);  }  /** @@ -1469,21 +1480,6 @@ function autoregister_views($view_base, $folder, $base_location_path, $viewtype)  }  /** - * Add the core Elgg head elements that could be cached - * - * @return void - */ -function elgg_views_register_core_head_elements() { -	$url = elgg_get_simplecache_url('js', 'elgg'); -	elgg_register_js('elgg', $url, 'head', 10); -	elgg_load_js('elgg'); - -	$url = elgg_get_simplecache_url('css', 'elgg'); -	elgg_register_css('elgg', $url, 10); -	elgg_load_css('elgg'); -} - -/**   * Add the rss link to the extras when if needed   *   * @return void @@ -1509,6 +1505,19 @@ function elgg_views_add_rss_link() {  }  /** + * Registers deprecated views to avoid making some pages from older plugins + * completely empty. + * + * @private + */ +function elgg_views_handle_deprecated_views() { +	$location = elgg_get_view_location('page_elements/contentwrapper'); +	if ($location === "/var/www/views/") { +		elgg_extend_view('page_elements/contentwrapper', 'page/elements/wrapper'); +	} +} + +/**   * Initialize viewtypes on system boot event   * This ensures simplecache is cleared during upgrades. See #2252   * @@ -1524,12 +1533,17 @@ function elgg_views_boot() {  	elgg_register_simplecache_view('css/ie6');  	elgg_register_simplecache_view('js/elgg'); -	elgg_register_js('jquery', '/vendors/jquery/jquery-1.5.min.js', 'head', 1); -	elgg_register_js('jquery-ui', '/vendors/jquery/jquery-ui-1.8.9.min.js', 'head', 2); +	elgg_register_js('jquery', '/vendors/jquery/jquery-1.6.2.min.js', 'head'); +	elgg_register_js('jquery-ui', '/vendors/jquery/jquery-ui-1.8.16.min.js', 'head');  	elgg_register_js('jquery.form', '/vendors/jquery/jquery.form.js'); +	 +	$elgg_js_url = elgg_get_simplecache_url('js', 'elgg'); +	elgg_register_js('elgg', $elgg_js_url, 'head'); +  	elgg_load_js('jquery');  	elgg_load_js('jquery-ui');  	elgg_load_js('jquery.form'); +	elgg_load_js('elgg');  	elgg_register_simplecache_view('js/lightbox');  	$lightbox_js_url = elgg_get_simplecache_url('js', 'lightbox'); @@ -1537,7 +1551,10 @@ function elgg_views_boot() {  	$lightbox_css_url = 'vendors/jquery/fancybox/jquery.fancybox-1.3.4.css';  	elgg_register_css('lightbox', $lightbox_css_url); -	elgg_register_event_handler('ready', 'system', 'elgg_views_register_core_head_elements'); +	$elgg_css_url = elgg_get_simplecache_url('css', 'elgg'); +	elgg_register_css('elgg', $elgg_css_url, 1); +	elgg_load_css('elgg'); +  	elgg_register_event_handler('pagesetup', 'system', 'elgg_views_add_rss_link');  	// discover the built-in view types @@ -1554,3 +1571,4 @@ function elgg_views_boot() {  }  elgg_register_event_handler('boot', 'system', 'elgg_views_boot', 1000); +elgg_register_event_handler('init', 'system', 'elgg_views_handle_deprecated_views'); diff --git a/engine/tests/api/helpers.php b/engine/tests/api/helpers.php index 461627547..ee2e64cfe 100644 --- a/engine/tests/api/helpers.php +++ b/engine/tests/api/helpers.php @@ -31,6 +31,7 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {  		global $CONFIG;  		unset($CONFIG->externals); +		unset($CONFIG->externals_map);  	}  	/** @@ -106,7 +107,16 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {  		// specify name  		$result = elgg_register_js('key', 'http://test1.com', 'footer');  		$this->assertTrue($result); -		$this->assertIdentical('http://test1.com', $CONFIG->externals['js']['key']->url); +		$this->assertTrue(isset($CONFIG->externals_map['js']['key'])); + +		$item = $CONFIG->externals_map['js']['key']; +		$this->assertTrue($CONFIG->externals['js']->contains($item)); + +		$priority = $CONFIG->externals['js']->getPriority($item); +		$this->assertTrue($priority !== false); + +		$item = $CONFIG->externals['js']->getElement($priority); +		$this->assertIdentical('http://test1.com', $item->url);  		// send a bad url  		$result = @elgg_register_js('bad'); @@ -118,11 +128,20 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {  	 */  	public function testElggRegisterCSS() {  		global $CONFIG; - +		  		// specify name  		$result = elgg_register_css('key', 'http://test1.com');  		$this->assertTrue($result); -		$this->assertIdentical('http://test1.com', $CONFIG->externals['css']['key']->url); +		$this->assertTrue(isset($CONFIG->externals_map['css']['key'])); + +		$item = $CONFIG->externals_map['css']['key']; +		$this->assertTrue($CONFIG->externals['css']->contains($item)); + +		$priority = $CONFIG->externals['css']->getPriority($item); +		$this->assertTrue($priority !== false); + +		$item = $CONFIG->externals['css']->getElement($priority); +		$this->assertIdentical('http://test1.com', $item->url);  	}  	/** @@ -134,21 +153,43 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {  		$base = trim(elgg_get_site_url(), "/");  		$urls = array('id1' => "$base/urla", 'id2' => "$base/urlb", 'id3' => "$base/urlc"); +		  		foreach ($urls as $id => $url) {  			elgg_register_js($id, $url);  		}  		$result = elgg_unregister_js('id1');  		$this->assertTrue($result); -		@$this->assertNULL($CONFIG->externals['js']['head']['id1']); + +		$js = $CONFIG->externals['js']; +		$elements = $js->getElements(); +		$this->assertFalse(isset($CONFIG->externals_map['js']['id1'])); +		 +		foreach ($elements as $element) { +			$this->assertFalse($element->name == 'id1'); +		}  		$result = elgg_unregister_js('id1');  		$this->assertFalse($result); +  		$result = elgg_unregister_js('', 'does_not_exist');  		$this->assertFalse($result);  		$result = elgg_unregister_js('id2'); -		$this->assertIdentical($urls['id3'], $CONFIG->externals['js']['id3']->url); +		$elements = $js->getElements(); + +		$this->assertFalse(isset($CONFIG->externals_map['js']['id2'])); +		foreach ($elements as $element) { +			$this->assertFalse($element->name == 'id2'); +		} + +		$this->assertTrue(isset($CONFIG->externals_map['js']['id3'])); + +		$priority = $CONFIG->externals['js']->getPriority($CONFIG->externals_map['js']['id3']); +		$this->assertTrue($priority !== false); + +		$item = $CONFIG->externals['js']->getElement($priority); +		$this->assertIdentical($urls['id3'], $item->url);  	}  	/** @@ -161,6 +202,7 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {  		elgg_load_js('key');  		$result = elgg_register_js('key', 'http://test1.com', 'footer');  		$this->assertTrue($result); +		  		$js_urls = elgg_get_loaded_js('footer');  		$this->assertIdentical(array('http://test1.com'), $js_urls);  	} @@ -173,7 +215,12 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {  		$base = trim(elgg_get_site_url(), "/"); -		$urls = array('id1' => "$base/urla", 'id2' => "$base/urlb", 'id3' => "$base/urlc"); +		$urls = array( +			'id1' => "$base/urla", +			'id2' => "$base/urlb", +			'id3' => "$base/urlc" +		); +		  		foreach ($urls as $id => $url) {  			elgg_register_js($id, $url);  			elgg_load_js($id); @@ -187,4 +234,275 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {  		$js_urls = elgg_get_loaded_js('footer');  		$this->assertIdentical(array(), $js_urls);  	} -} + +	// test ElggPriorityList +	public function testElggPriorityListAdd() { +		$pl = new ElggPriorityList(); +		$elements = array( +			'Test value', +			'Test value 2', +			'Test value 3' +		); + +		shuffle($elements); + +		foreach ($elements as $element) { +			$this->assertTrue($pl->add($element) !== false); +		} + +		$test_elements = $pl->getElements(); + +		$this->assertTrue(is_array($test_elements)); + +		foreach ($test_elements as $i => $element) { +			// should be in the array +			$this->assertTrue(in_array($element, $elements)); + +			// should be the only element, so priority 0 +			$this->assertEqual($i, array_search($element, $elements)); +		} +	} + +	public function testElggPriorityListAddWithPriority() { +		$pl = new ElggPriorityList(); + +		$elements = array( +			10 => 'Test Element 10', +			5 => 'Test Element 5', +			0 => 'Test Element 0', +			100 => 'Test Element 100', +			-1 => 'Test Element -1', +			-5 => 'Test Element -5' +		); + +		foreach ($elements as $priority => $element) { +			$pl->add($element, $priority); +		} + +		$test_elements = $pl->getElements(); + +		// should be sorted by priority +		$elements_sorted = array( +			-5 => 'Test Element -5', +			-1 => 'Test Element -1', +			0 => 'Test Element 0', +			5 => 'Test Element 5', +			10 => 'Test Element 10', +			100 => 'Test Element 100', +		); + +		$this->assertIdentical($elements_sorted, $test_elements); + +		foreach ($test_elements as $priority => $element) { +			$this->assertIdentical($elements[$priority], $element); +		} +	} + +	public function testElggPriorityListGetNextPriority() { +		$pl = new ElggPriorityList(); + +		$elements = array( +			2 => 'Test Element', +			0 => 'Test Element 2', +			-2 => 'Test Element 3', +		); + +		foreach ($elements as $priority => $element) { +			$pl->add($element, $priority); +		} + +		// we're not specifying a priority so it should be the next consecutive to 0. +		$this->assertEqual(1, $pl->getNextPriority()); + +		// add another one at priority 1 +		$pl->add('Test Element 1'); + +		// next consecutive to 0 is now 3. +		$this->assertEqual(3, $pl->getNextPriority()); +	} + +	public function testElggPriorityListRemove() { +		$pl = new ElggPriorityList(); + +		$elements = array(); +		for ($i=0; $i<3; $i++) { +			$element = new stdClass(); +			$element->name = "Test Element $i"; +			$element->someAttribute = rand(0, 9999); +			$elements[] = $element; +			$pl->add($element); +		} + +		$pl->remove($elements[1]); + +		$test_elements = $pl->getElements(); + +		// make sure it's gone. +		$this->assertTrue(2, count($test_elements)); +		$this->assertIdentical($elements[0], $test_elements[0]); +		$this->assertIdentical($elements[2], $test_elements[2]); +	} + +	public function testElggPriorityListMove() { +		$pl = new ElggPriorityList(); + +		$elements = array( +			-5 => 'Test Element -5', +			0 => 'Test Element 0', +			5 => 'Test Element 5', +		); + +		foreach ($elements as $priority => $element) { +			$pl->add($element, $priority); +		} + +		$this->assertTrue($pl->move($elements[-5], 10)); +		 +		// check it's at the new place +		$this->assertIdentical($elements[-5], $pl->getElement(10)); + +		// check it's not at the old +		$this->assertFalse($pl->getElement(-5)); +	} + +	public function testElggPriorityListConstructor() { +		$elements = array( +			10 => 'Test Element 10', +			5 => 'Test Element 5', +			0 => 'Test Element 0', +			100 => 'Test Element 100', +			-1 => 'Test Element -1', +			-5 => 'Test Element -5' +		); + +		$pl = new ElggPriorityList($elements); +		$test_elements = $pl->getElements(); + +		$elements_sorted = array( +			-5 => 'Test Element -5', +			-1 => 'Test Element -1', +			0 => 'Test Element 0', +			5 => 'Test Element 5', +			10 => 'Test Element 10', +			100 => 'Test Element 100', +		); + +		$this->assertIdentical($elements_sorted, $test_elements); +	} + +	public function testElggPriorityListGetPriority() { +		$pl = new ElggPriorityList(); + +		$elements = array( +			'Test element 0', +			'Test element 1', +			'Test element 2', +		); + +		foreach ($elements as $element) { +			$pl->add($element); +		} + +		$this->assertIdentical(0, $pl->getPriority($elements[0])); +		$this->assertIdentical(1, $pl->getPriority($elements[1])); +		$this->assertIdentical(2, $pl->getPriority($elements[2])); +	} + +	public function testElggPriorityListGetElement() { +		$pl = new ElggPriorityList(); +		$priorities = array(); + +		$elements = array( +			'Test element 0', +			'Test element 1', +			'Test element 2', +		); + +		foreach ($elements as $element) { +			$priorities[] = $pl->add($element); +		} + +		$this->assertIdentical($elements[0], $pl->getElement($priorities[0])); +		$this->assertIdentical($elements[1], $pl->getElement($priorities[1])); +		$this->assertIdentical($elements[2], $pl->getElement($priorities[2])); +	} + +	public function testElggPriorityListPriorityCollision() { +		$pl = new ElggPriorityList(); +		 +		$elements = array( +			5 => 'Test element 5', +			6 => 'Test element 6', +			0 => 'Test element 0', +		); + +		foreach ($elements as $priority => $element) { +			$pl->add($element, $priority); +		} + +		// add at a colliding priority +		$pl->add('Colliding element', 5); + +		// should float to the top closest to 5, so 7 +		$this->assertEqual(7, $pl->getPriority('Colliding element')); +	} + +	public function testElggPriorityListIterator() { +		$elements = array( +			-5 => 'Test element -5', +			0 => 'Test element 0', +			5 => 'Test element 5' +		); +		 +		$pl = new ElggPriorityList($elements); + +		foreach ($pl as $priority => $element) { +			$this->assertIdentical($elements[$priority], $element); +		} +	} + +	public function testElggPriorityListCountable() { +		$pl = new ElggPriorityList(); + +		$this->assertEqual(0, count($pl)); + +		$pl->add('Test element 0'); +		$this->assertEqual(1, count($pl)); + +		$pl->add('Test element 1'); +		$this->assertEqual(2, count($pl)); + +		$pl->add('Test element 2'); +		$this->assertEqual(3, count($pl)); +	} + +	public function testElggPriorityListUserSort() { +		$elements = array( +			'A', +			'B', +			'C', +			'D', +			'E', +		); + +		$elements_sorted_string = $elements; + +		shuffle($elements); +		$pl = new ElggPriorityList($elements); + +		// will sort by priority +		$test_elements = $pl->getElements(); +		$this->assertIdentical($elements, $test_elements); + +		function test_sort($elements) { +			sort($elements, SORT_LOCALE_STRING); +			return $elements; +		} + +		// force a new sort using our function +		$pl->sort('test_sort'); +		$test_elements = $pl->getElements(); + +		$this->assertIdentical($elements_sorted_string, $test_elements); +	} +}
\ No newline at end of file  | 
