diff options
-rw-r--r-- | engine/classes/ElggPriorityList.php | 96 | ||||
-rw-r--r-- | engine/lib/elgglib.php | 112 | ||||
-rw-r--r-- | engine/tests/api/helpers.php | 75 |
3 files changed, 241 insertions, 42 deletions
diff --git a/engine/classes/ElggPriorityList.php b/engine/classes/ElggPriorityList.php index 39fe698da..931138106 100644 --- a/engine/classes/ElggPriorityList.php +++ b/engine/classes/ElggPriorityList.php @@ -26,6 +26,7 @@ * * * // Array + * * $pl = new ElggPriorityList(); * $pl[] = 'Element 0'; * $pl[-5] = 'Element -5'; @@ -45,24 +46,45 @@ * 10 => Element 10 * * - * Collisions with priority are handled by inserting the element as close to the requested priority - * as possible. + * Collisions with priority are handled by default differently in the OOP and the array interfaces. + * + * If using the OOP interface, the default is to insert the element as close to the requested + * priority as possible. * * $pl = new ElggPriorityList(); - * $pl[5] = 'Element 5'; - * $pl[5] = 'Colliding element 5'; - * $pl[5] = 'Another colliding element 5'; + * $pl->add('Element 5', 5); + * $pl->add('Colliding element 5', 5); + * $pl->add('Another colliding element 5', 5); * * var_dump($pl->getElements()); * * Yields: - * * array( * 5 => 'Element 5', * 6 => 'Colliding element 5', * 7 => 'Another colliding element 5' * ) * + * If using the array interface, elements are added at exactly the priority, displacing other + * elements if necessary. This behavior is also available by passing true as the 3rd argument to + * ->add(): + * + * $pl = new ElggPriorityList(); + * $pl[5] = 'Element 5'; + * $pl[6] = 'Element 6'; + * $pl[5] = 'Colliding element 5'; // shifts the previous two up by one + * $pl->add('Another colliding element 5', 5, true); // shifts the previous three up by one + * + * var_dump($pl->getElements()); + * + * Yields: + * array( + * 5 => 'Another colliding element 5', + * 6 => 'Colliding element 5', + * 7 => 'Element 5', + * 8 => 'Element 6' + * ) + * * @package Elgg.Core * @subpackage Helpers */ @@ -101,16 +123,21 @@ class ElggPriorityList * 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 the element was added at. + * @param bool $exact If true, will put the element at exactly the priority specified, displacing + * other elements. + * @return int The priority of the added element. */ - public function add($element, $priority = null) { + public function add($element, $priority = null, $exact = false) { if ($priority !== null && !is_numeric($priority)) { return false; + } elseif ($exact) { + $this->shiftElementsSegment($priority); } else { $priority = $this->getNextPriority($priority); } $this->elements[$priority] = $element; + $this->sorted = false; return $priority; } @@ -134,6 +161,32 @@ class ElggPriorityList } /** + * Move an existing element to a new priority. + * + * @param int $current_priority + * @param int $new_priority + * @param bool $exact + * @return bool + */ + public function move($current_priority, $new_priority, $exact = false) { + $current_priority = (int) $current_priority; + $new_priority = (int) $new_priority; + + if (!isset($this->elements[$current_priority])) { + return false; + } + + if ($current_priority == $new_priority) { + return true; + } + + $element = $this->elements[$current_priority]; + unset($this->elements[$current_priority]); + + return $this->add($element, $new_priority, $exact); + } + + /** * Returns the elements * * @param type $elements @@ -186,6 +239,29 @@ class ElggPriorityList } /** + * Shift a segment of elements starting at $index up by one until the end of the array or + * there's a gap in the indexes. This produces a space at $index to insert a new element. + * + * @param type $index The index to start + * @return array + */ + private function shiftElementsSegment($index) { + $index = (int) $index; + // @todo probably a better way. + $replace_elements = array(); + while (isset($this->elements[$index])) { + $replace_elements[$index + 1] = $this->elements[$index]; + unset($this->elements[$index]); + $index++; + } + + // insert old ones + foreach ($replace_elements as $index => $element) { + $this->elements[$index] = $element; + } + } + + /** * Returns the next priority available. * * @param int $near Make the priority as close to $near as possible. @@ -295,7 +371,9 @@ class ElggPriorityList } public function offsetSet($offset, $value) { - return $this->add($value, $offset); + // for $pl[] = 'New element' + $exact = ($offset !== null); + return $this->add($value, $offset, $exact); } public function offsetUnset($offset) { diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index cb736f418..b6b603e79 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -172,7 +172,7 @@ function forward($location = "", $reason = 'system') { * @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)) { @@ -292,26 +292,35 @@ function elgg_register_external_file($type, $name, $url, $location, $priority = $CONFIG->externals = array(); } - if (!isset($CONFIG->externals[$type])) { - $CONFIG->externals[$type] = array(); + if (!$CONFIG->externals[$type] instanceof ElggPriorityList) { + $CONFIG->externals[$type] = new ElggPriorityList(); } $name = trim(strtolower($name)); + $priority = max((int)$priority, 0); - if (isset($CONFIG->externals[$type][$name])) { - // update a registered item - $item = $CONFIG->externals[$type][$name]; + $index = elgg_get_external_file_priority($name, $type); + + if ($index !== false) { + // updating a registered item + $item = $CONFIG->externals[$type][$index]; + $item->url = $url; + $item->location = $location; + // remove old saved priority + elgg_remove_external_file_priority($name, $type); + $priority = $CONFIG->externals[$type]->move($index, $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; + // save priority map so we can update if added again + elgg_save_external_file_priority($priority, $name, $type); return true; } @@ -332,14 +341,17 @@ function elgg_unregister_external_file($type, $name) { return false; } - if (!isset($CONFIG->externals[$type])) { + if (!$CONFIG->externals[$type] instanceof ElggPriorityList) { return false; } $name = trim(strtolower($name)); - if (array_key_exists($name, $CONFIG->externals[$type])) { - unset($CONFIG->externals[$type][$name]); + $priority = elgg_get_external_file_priority($name, $type); + + if ($priority !== false) { + elgg_remove_external_file_priority($name, $type); + unset($CONFIG->externals[$type][$priority]); return true; } @@ -362,24 +374,75 @@ function elgg_load_external_file($type, $name) { $CONFIG->externals = array(); } - if (!isset($CONFIG->externals[$type])) { - $CONFIG->externals[$type] = array(); + if (!$CONFIG->externals[$type] instanceof ElggPriorityList) { + $CONFIG->externals[$type] = new ElggPriorityList(); } $name = trim(strtolower($name)); - if (isset($CONFIG->externals[$type][$name])) { + $priority = elgg_get_external_file_priority($name, $type); + + if ($priority !== false) { // update a registered item - $CONFIG->externals[$type][$name]->loaded = true; + $CONFIG->externals[$type][$priority]->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); + elgg_save_external_file_priority($priority, $name, $type); + } +} + +/** + * Gets the priority of an external by name and type. + * + * @param type $name + * @param type $type + * @return type + */ +function elgg_get_external_file_priority($name, $type) { + global $CONFIG; + + if (!isset($CONFIG->externals_priorities[$type][$name])) { + return false; + } + + return $CONFIG->externals_priorities[$type][$name]; +} + +function elgg_save_external_file_priority($priority, $name, $type) { + global $CONFIG; + + if (!isset($CONFIG->externals_priorities)) { + $CONFIG->externals_priorities = array(); + } + + if (!isset($CONFIG->externals_priorities[$type])) { + $CONFIG->externals_priorities[$type] = array(); + } + + $CONFIG->externals_priorities[$type][$name] = $priority; + + return true; +} + +function elgg_remove_external_file_priority($name, $type) { + global $CONFIG; + + if (!isset($CONFIG->externals_priorities)) { + $CONFIG->externals_priorities = array(); } + + if (!isset($CONFIG->externals_priorities[$type])) { + $CONFIG->externals_priorities[$type] = array(); + } + + unset($CONFIG->externals_priorities[$type][$name]); + + return true; } /** @@ -394,13 +457,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; diff --git a/engine/tests/api/helpers.php b/engine/tests/api/helpers.php index 033970359..cceb762be 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_priorities); } /** @@ -106,7 +107,9 @@ 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_priorities['js']['key'])); + $index = $CONFIG->externals_priorities['js']['key']; + $this->assertIdentical('http://test1.com', $CONFIG->externals['js'][$index]->url); // send a bad url $result = @elgg_register_js('bad'); @@ -122,7 +125,9 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { // 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_priorities['css']['key'])); + $index = elgg_get_external_file_priority('css', 'key'); + $this->assertIdentical('http://test1.com', $CONFIG->externals['css'][$index]->url); } /** @@ -140,7 +145,13 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { $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_priorities['js']['id1'])); + foreach ($elements as $element) { + $this->assertFalse($element->name == 'id1'); + } $result = elgg_unregister_js('id1'); $this->assertFalse($result); @@ -148,7 +159,15 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { $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_priorities['js']['id2'])); + foreach ($elements as $element) { + $this->assertFalse($element->name == 'id2'); + } + + $this->assertTrue(isset($CONFIG->externals_priorities['js']['id3'])); + $priority = $CONFIG->externals_priorities['js']['id3']; + $this->assertIdentical($urls['id3'], $CONFIG->externals['js'][$priority]->url); } /** @@ -361,18 +380,19 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { public function testElggPriorityListArrayAccess() { $pl = new ElggPriorityList(); + $pl[] = 'Test element 0'; $pl[-10] = 'Test element -10'; $pl[-1] = 'Test element -1'; $pl[] = 'Test element 1'; $pl[5] = 'Test element 5'; - $pl[0] = 'Test element collision with 0 (should be 2)'; + $pl[0] = 'Test element collision with 0'; $elements = array( -1 => 'Test element -1', - 0 => 'Test element 0', - 1 => 'Test element 1', - 2 => 'Test element collision with 0 (should be 2)', + 0 => 'Test element collision with 0', + 1 => 'Test element 0', + 2 => 'Test element 1', 5 => 'Test element 5', ); @@ -441,4 +461,43 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { $this->assertIdentical($elements_sorted_string, $test_elements); } + + function testElggPriorityListShiftElementsSegment() { + $elements = array( + 0 => 'Element 0', + 1 => 'Element 1', + 2 => 'Element 2', + 4 => 'Element 4', + ); + + $pl = new ElggPriorityList($elements); + + // add a new element directly at 1. + $pl->add('New Element', 1, true); + + $elements_sorted = array( + 0 => 'Element 0', + 1 => 'New Element', + 2 => 'Element 1', + 3 => 'Element 2', + 4 => 'Element 4', + ); + + $test_elements = $pl->getElements(); + $this->assertIdentical($elements_sorted, $test_elements); + + $pl->add('New Element 10', 10, true); + + $elements_sorted = array( + 0 => 'Element 0', + 1 => 'New Element', + 2 => 'Element 1', + 3 => 'Element 2', + 4 => 'Element 4', + 10 => 'New Element 10' + ); + + $test_elements = $pl->getElements(); + $this->assertIdentical($elements_sorted, $test_elements); + } }
\ No newline at end of file |