]> gitweb.fluxo.info Git - lorea/elgg.git/commitdiff
Finished ElggPriorityList. Migrated external files to use it.
authorBrett Profitt <brett.profitt@gmail.com>
Thu, 18 Aug 2011 01:22:13 +0000 (18:22 -0700)
committerBrett Profitt <brett.profitt@gmail.com>
Thu, 18 Aug 2011 01:22:13 +0000 (18:22 -0700)
engine/classes/ElggPriorityList.php
engine/lib/elgglib.php
engine/tests/api/helpers.php

index 39fe698da2cbc05587974e7e2d963badca3e7fec..93113810691a7bf42e83fc14802cf8821673dc9a 100644 (file)
@@ -26,6 +26,7 @@
  *
  *
  * // Array
+ * 
  * $pl = new ElggPriorityList();
  * $pl[] = 'Element 0';
  * $pl[-5] = 'Element -5';
  * 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;
        }
 
@@ -133,6 +160,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
         *
@@ -185,6 +238,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.
         *
@@ -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) {
index cb736f41882aca5820d51a06a21b566db3bcf8d5..b6b603e79b357de1dc22b21b1a7c40d916b275a8 100644 (file)
@@ -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;
index 033970359c5330ccf2ddcaed8150e311971fc197..cceb762be7d562319b21c3f8d1ae0ada18bc866d 100644 (file)
@@ -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