diff options
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 |