]> git.mjollnir.org Git - moodle.git/commitdiff
listlib: MDL-17422 Paging in listlib was badly screwed
authortjhunt <tjhunt>
Thu, 27 Nov 2008 10:53:46 +0000 (10:53 +0000)
committertjhunt <tjhunt>
Thu, 27 Nov 2008 10:53:46 +0000 (10:53 +0000)
lib/listlib.php

index f97843340d0fafe87fb60840f2f739239a9fa2c2..47c596ddde724753e27382dcd3656104bdd3edcc 100644 (file)
@@ -7,7 +7,7 @@
 // Moodle - Modular Object-Oriented Dynamic Learning Environment         //
 //          http://moodle.com                                            //
 //                                                                       //
-// Copyright (C) 1999 onwards Martin Dougiamas  http://dougiamas.com       //
+// Copyright (C) 1999 onwards Martin Dougiamas  http://dougiamas.com     //
 //                                                                       //
 // This program is free software; you can redistribute it and/or modify  //
 // it under the terms of the GNU General Public License as published by  //
  *    Processing of editing actions on list.
  *
  * @author Jamie Pratt
- * @version  $Id$
  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
  * @package moodlecore
  */
 
-
-class moodle_list{
+/**
+ * Clues to reading this code:
+ *
+ * The functions that move things around the tree structure just update the
+ * database - they don't update the in-memory structure, instead they trigger a
+ * page reload so everything is rebuilt from scratch.
+ */
+class moodle_list {
     var $attributes;
     var $listitemclassname = 'list_item';
     /**
      * An array of $listitemclassname objects.
-     *
      * @var array
      */
     var $items = array();
     /**
      * ol / ul
-     *
      * @var string
      */
     var $type;
     /**
-     *
      * @var list_item or derived class
      */
     var $parentitem = null;
@@ -65,7 +67,6 @@ class moodle_list{
     var $sortby = 'parent, sortorder, name';
     /**
      * Records from db, only used in top level list.
-     *
      * @var array
      */
     var $records = array();
@@ -74,7 +75,6 @@ class moodle_list{
 
     /**
      * Key is child id, value is parent.
-     *
      * @var array
      */
     var $childparent;
@@ -103,20 +103,19 @@ class moodle_list{
      * @param integer $itemsperpage no of top level items.
      * @return moodle_list
      */
-    function moodle_list($type='ul', $attributes='', $editable = false, $pageurl=null, $page = 0, $pageparamname = 'page', $itemsperpage = 20){
+    function moodle_list($type='ul', $attributes='', $editable = false, $pageurl=null, $page = 0, $pageparamname = 'page', $itemsperpage = 20) {
         $this->editable = $editable;
         $this->attributes = $attributes;
         $this->type = $type;
         $this->page = $page;
         $this->pageparamname = $pageparamname;
         $this->itemsperpage = $itemsperpage;
-        if ($pageurl === null){
+        if ($pageurl === null) {
             $this->pageurl = new moodle_url();
             $this->pageurl->params(array($this->pageparamname => $this->page));
         } else {
             $this->pageurl = $pageurl;
         }
-
     }
 
     /**
@@ -124,20 +123,20 @@ class moodle_list{
      *
      * @param integer $indent depth of indentation.
      */
-    function to_html($indent=0, $extraargs=array()){
-        if (count($this->items)){
+    function to_html($indent=0, $extraargs=array()) {
+        if (count($this->items)) {
             $tabs = str_repeat("\t", $indent);
             $first = true;
             $itemiter = 1;
             $lastitem = '';
             $html = '';
 
-            foreach ($this->items as $item){
+            foreach ($this->items as $item) {
                 $last = (count($this->items) == $itemiter);
-                if ($this->editable){
+                if ($this->editable) {
                     $item->set_icon_html($first, $last, $lastitem);
                 }
-                if ($itemhtml = $item->to_html($indent+1, $extraargs)){
+                if ($itemhtml = $item->to_html($indent+1, $extraargs)) {
                     $html .= "$tabs\t<li".((!empty($item->attributes))?(' '.$item->attributes):'').">";
                     $html .= $itemhtml;
                     $html .= "</li>\n";
@@ -149,7 +148,7 @@ class moodle_list{
         } else {
             $html = '';
         }
-        if ($html){ //if there are list items to display then wrap them in ul / ol tag.
+        if ($html) { //if there are list items to display then wrap them in ul / ol tag.
             $tabs = str_repeat("\t", $indent);
             $html = $tabs.'<'.$this->type.((!empty($this->attributes))?(' '.$this->attributes):'').">\n".$html;
             $html .= $tabs."</".$this->type.">\n";
@@ -166,39 +165,36 @@ class moodle_list{
      * @param boolean $suppresserror error if not item found?
      * @return list_item *copy* or null if item is not found
      */
-    function find_item($id, $suppresserror = false){
-        if (isset($this->items)){
-            foreach ($this->items as $key => $child){
-                if ($child->id == $id){
+    function find_item($id, $suppresserror = false) {
+        if (isset($this->items)) {
+            foreach ($this->items as $key => $child) {
+                if ($child->id == $id) {
                     return $this->items[$key];
                 }
             }
-            foreach (array_keys($this->items) as $key){
+            foreach (array_keys($this->items) as $key) {
                 $thischild =& $this->items[$key];
                 $ref = $thischild->children->find_item($id, true);//error always reported at top level
-                if ($ref !== null){
+                if ($ref !== null) {
                     return $ref;
                 }
             }
         }
 
-        if (!$suppresserror){
+        if (!$suppresserror) {
             print_error('listnoitem');
         }
         return null;
     }
 
-
-
-    function add_item(&$item){
+    function add_item(&$item) {
         $this->items[] =& $item;
     }
 
-    function set_parent(&$parent){
+    function set_parent(&$parent) {
         $this->parentitem =& $parent;
     }
 
-
     /**
      * Produces a hierarchical tree of list items from a flat array of records.
      * 'parent' field is expected to point to a parent record.
@@ -207,10 +203,10 @@ class moodle_list{
      * a top level list
      *
      * @param integer $offset how many list toplevel items are there in lists before this one
-     * @return integer $offset + how many toplevel items where there in this list.
+     * @return array(boolean, integer) whether there is more than one page, $offset + how many toplevel items where there in this list.
      *
      */
-    function list_from_records($paged = false, $offset = 1){
+    function list_from_records($paged = false, $offset = 0) {
         $this->paged = $paged;
         $this->offset = $offset;
         $this->get_records();
@@ -223,26 +219,26 @@ class moodle_list{
         $itemiter = $offset;
         //make a simple array which is easier to search
         $this->childparent = array();
-        foreach ($records as $record){
+        foreach ($records as $record) {
             $this->childparent[$record->id] = $record->parent;
         }
         //create top level list items and they're responsible for creating their children
-        foreach ($records as $record){
-            if (!array_key_exists($record->parent, $this->childparent)){
+        foreach ($records as $record) {
+            if (!array_key_exists($record->parent, $this->childparent)) {
                 //if this record is not a child of another record then
 
                 $inpage = ($itemiter >= $this->firstitem && $itemiter <= $this->lastitem);
                 //make list item for top level for all items
                 //we need the info about the top level items for reordering peers.
-                if ($this->parentitem!==null){
+                if ($this->parentitem!==null) {
                     $newattributes = $this->parentitem->attributes;
                 } else {
                     $newattributes = '';
 
                 }
-                $newlistitem =& new $this->listitemclassname($record, $this, $newattributes, $inpage);
-                if ($inpage){
-                    $newlistitem->create_children($records, $this->childparent, $record->id);
+                $this->items[$itemiter] =& new $this->listitemclassname($record, $this, $newattributes, $inpage);
+                if ($inpage) {
+                    $this->items[$itemiter]->create_children($records, $this->childparent, $record->id);
                 } else {
                     //don't recurse down the tree for items that are not on this page
                     $this->paged = true;
@@ -267,7 +263,7 @@ class moodle_list{
         $html = '';
         $topcount = count($this->items);
         $this->pagecount = (integer) ceil(($topcount + $this->offset)/ QUESTION_PAGE_LENGTH );
-        if (!empty($this->page) && ($this->paged)){
+        if (!empty($this->page) && ($this->paged)) {
             $html = "<div class=\"paging\">".get_string('page').":\n";
             foreach (range(1,$this->pagecount) as $currentpage) {
                 if ($this->page == $currentpage) {
@@ -302,7 +298,7 @@ class moodle_list{
      */
     function get_child_ids() {
         $childids = array();
-        foreach ($this->items as $child){
+        foreach ($this->items as $child) {
            $childids[] = $child->id;
         }
         return $childids;
@@ -319,18 +315,17 @@ class moodle_list{
         $itemkey = array_search($id, $peers);
         switch ($direction) {
             case 'down' :
-                if (isset($peers[$itemkey+1])){
+                if (isset($peers[$itemkey+1])) {
                     $olditem = $peers[$itemkey+1];
                     $peers[$itemkey+1] = $id;
                     $peers[$itemkey] = $olditem;
                 } else {
                     print_error('listcantmoveup');
-
                 }
                 break;
 
             case 'up' :
-                if (isset($peers[$itemkey-1])){
+                if (isset($peers[$itemkey-1])) {
                     $olditem = $peers[$itemkey-1];
                     $peers[$itemkey-1] = $id;
                     $peers[$itemkey] = $olditem;
@@ -341,24 +336,29 @@ class moodle_list{
         }
         $this->reorder_peers($peers);
     }
-    function reorder_peers($peers){
-        global $DB;
 
+    function reorder_peers($peers) {
+        global $DB;
         foreach ($peers as $key => $peer) {
-            if (! $DB->set_field($this->table, "sortorder", $key, array("id"=>$peer))) {
+            if (!$DB->set_field($this->table, "sortorder", $key, array("id"=>$peer))) {
                 print_error('listupdatefail');
             }
         }
     }
+
+    /**
+     * @param integer $id an item index.
+     * @return object the item that used to be the parent of the item moved.
+     */
     function move_item_left($id) {
         global $DB;
 
         $item = $this->find_item($id);
-        if (!isset($item->parentlist->parentitem->parentlist)){
+        if (!isset($item->parentlist->parentitem->parentlist)) {
             print_error('listcantmoveleft');
         } else {
             $newpeers = $this->get_items_peers($item->parentlist->parentitem->id);
-            if (isset($item->parentlist->parentitem->parentlist->parentitem)){
+            if (isset($item->parentlist->parentitem->parentlist->parentitem)) {
                 $newparent = $item->parentlist->parentitem->parentlist->parentitem->id;
             } else {
                 $newparent = 0; // top level item
@@ -371,7 +371,9 @@ class moodle_list{
                 $this->reorder_peers($neworder);
             }
         }
+        return $item->parentlist->parentitem;
     }
+
     /**
      * Make item with id $id the child of the peer that is just above it in the sort order.
      *
@@ -382,17 +384,17 @@ class moodle_list{
 
         $peers = $this->get_items_peers($id);
         $itemkey = array_search($id, $peers);
-        if (!isset($peers[$itemkey-1])){
+        if (!isset($peers[$itemkey-1])) {
             print_error('listcantmoveright');
         } else {
             if (!$DB->set_field($this->table, "parent", $peers[$itemkey-1], array("id"=>$peers[$itemkey]))) {
                 print_error('listupdatefail');
             } else {
                 $newparent = $this->find_item($peers[$itemkey-1]);
-                if (isset($newparent->children)){
+                if (isset($newparent->children)) {
                     $newpeers = $newparent->children->get_child_ids();
                 }
-                if ($newpeers){
+                if ($newpeers) {
                     $newpeers[] = $peers[$itemkey];
                     $this->reorder_peers($newpeers);
                 }
@@ -409,24 +411,36 @@ class moodle_list{
      * @param integer $movedown id of item to move down
      * @return unknown
      */
-    function process_actions($left, $right, $moveup, $movedown){
+    function process_actions($left, $right, $moveup, $movedown) {
         //should this action be processed by this list object?
-        if (!(array_key_exists($left, $this->records) || array_key_exists($right, $this->records) || array_key_exists($moveup, $this->records) || array_key_exists($movedown, $this->records))){
+        if (!(array_key_exists($left, $this->records) || array_key_exists($right, $this->records) || array_key_exists($moveup, $this->records) || array_key_exists($movedown, $this->records))) {
             return false;
         }
         if (!empty($left)) {
-            $this->move_item_left($left);
+            $oldparentitem = $this->move_item_left($left);
+            if ($this->item_is_last_on_page($oldparentitem->id)) {
+                // Item has jumped onto the next page, change page when we redirect.
+                $this->page ++;
+                $this->pageurl->params(array($this->pageparamname => $this->page));
+            }
         } else if (!empty($right)) {
             $this->move_item_right($right);
+            if ($this->item_is_first_on_page($right)) {
+                // Item has jumped onto the previous page, change page when we redirect.
+                $this->page --;
+                $this->pageurl->params(array($this->pageparamname => $this->page));
+            }
         } else if (!empty($moveup)) {
             $this->move_item_up_down('up', $moveup);
-            if ($moveup == $this->items[$this->firstitem - 1]->id){//redirect to page that item has been moved to.
+            if ($this->item_is_first_on_page($moveup)) {
+                // Item has jumped onto the previous page, change page when we redirect.
                 $this->page --;
                 $this->pageurl->params(array($this->pageparamname => $this->page));
             }
         } else if (!empty($movedown)) {
             $this->move_item_up_down('down', $movedown);
-            if ($movedown == $this->items[$this->lastitem - 1]->id){//redirect to page that item has been moved to.
+            if ($this->item_is_last_on_page($movedown)) {
+                // Item has jumped onto the next page, change page when we redirect.
                 $this->page ++;
                 $this->pageurl->params(array($this->pageparamname => $this->page));
             }
@@ -436,24 +450,41 @@ class moodle_list{
 
         redirect($this->pageurl->out());
     }
+
+    /**
+     * @param integer $itemid an item id.
+     * @return boolean Is the item with the given id the first top-level item on
+     * the current page?
+     */
+    function item_is_first_on_page($itemid) {
+        return $this->page && isset($this->items[$this->firstitem]) &&
+                $itemid == $this->items[$this->firstitem]->id;
+    }
+
+    /**
+     * @param integer $itemid an item id.
+     * @return boolean Is the item with the given id the last top-level item on
+     * the current page?
+     */
+    function item_is_last_on_page($itemid) {
+        return $this->page && isset($this->items[$this->lastitem]) &&
+                $itemid == $this->items[$this->lastitem]->id;
+    }
 }
 
 class list_item {
     /**
      * id of record, used if list is editable
-     *
      * @var integer
      */
     var $id;
     /**
      * name of this item, used if list is editable
-     *
      * @var string
      */
     var $name;
     /**
      * The object or string representing this item.
-     *
      * @var mixed
      */
     var $item;
@@ -462,19 +493,17 @@ class list_item {
     var $display;
     var $icons = array();
     /**
-     *
      * @var moodle_list
      */
     var $parentlist;
     /**
      * Set if there are any children of this listitem.
-     *
      * @var moodle_list
      */
     var $children;
+
     /**
      * Constructor
-     *
      * @param mixed $item fragment of html for list item or record
      * @param object &$parent reference to parent of this item
      * @param string $attributes attributes for li tag
@@ -482,7 +511,7 @@ class list_item {
      *                              structure in memory to work with for actions but are not displayed.
      * @return list_item
      */
-    function list_item($item, &$parent, $attributes='', $display = true){
+    function list_item($item, &$parent, $attributes='', $display = true) {
         $this->item = $item;
         if (is_object($this->item)) {
             $this->id = $this->item->id;
@@ -495,12 +524,13 @@ class list_item {
         $this->children->set_parent($this);
         $this->display = $display;
     }
+
     /**
      * Output the html just for this item. Called by to_html which adds html for children.
      *
      */
-    function item_html($extraargs = array()){
-        if (is_string($this->item)){
+    function item_html($extraargs = array()) {
+        if (is_string($this->item)) {
             $html = $this->item;
         } elseif (is_object($this->item)) {
             //for debug purposes only. You should create a sub class to
@@ -509,6 +539,7 @@ class list_item {
         }
         return $html;
     }
+
     /**
      * Returns html
      *
@@ -517,13 +548,13 @@ class list_item {
      *                            may be used by sub class.
      * @return string html
      */
-    function to_html($indent=0, $extraargs = array()){
-        if (!$this->display){
+    function to_html($indent=0, $extraargs = array()) {
+        if (!$this->display) {
             return '';
         }
         $tabs = str_repeat("\t", $indent);
 
-        if (isset($this->children)){
+        if (isset($this->children)) {
             $childrenhtml = $this->children->to_html($indent+1, $extraargs);
         } else {
             $childrenhtml = '';
@@ -531,7 +562,7 @@ class list_item {
         return $this->item_html($extraargs).'&nbsp;'.(join($this->icons, '')).(($childrenhtml !='')?("\n".$childrenhtml):'');
     }
 
-    function set_icon_html($first, $last, &$lastitem){
+    function set_icon_html($first, $last, &$lastitem) {
         global $CFG;
         $strmoveup = get_string('moveup');
         $strmovedown = get_string('movedown');
@@ -540,7 +571,7 @@ class list_item {
 
         if (isset($this->parentlist->parentitem)) {
             $parentitem =& $this->parentlist->parentitem;
-            if (isset($parentitem->parentlist->parentitem)){
+            if (isset($parentitem->parentlist->parentitem)) {
                 $action = get_string('makechildof', 'question', $parentitem->parentlist->parentitem->name);
             } else {
                 $action = $strmoveleft;
@@ -568,19 +599,21 @@ class list_item {
         } else {
             $this->icons['right'] =  $this->image_spacer();
         }
-
     }
-    function image_icon($action, $url, $icon){
+
+    function image_icon($action, $url, $icon) {
         global $CFG;
         $pixpath = $CFG->pixpath;
         return '<a title="' . $action .'" href="'.$url.'">
                 <img src="' . $pixpath . '/t/'.$icon.'.gif" class="iconsmall" alt="' . $action. '" /></a> ';
     }
-    function image_spacer(){
+
+    function image_spacer() {
         global $CFG;
         $pixpath = $CFG->pixpath;
         return '<img src="' . $pixpath . '/spacer.gif" class="iconsmall" alt="" />';
     }
+
     /**
      * Recurse down tree creating list_items, called from moodle_list::list_from_records
      *
@@ -588,21 +621,21 @@ class list_item {
      * @param array $children
      * @param integer $thisrecordid
      */
-    function create_children(&$records, &$children, $thisrecordid){
+    function create_children(&$records, &$children, $thisrecordid) {
         //keys where value is $thisrecordid
         $thischildren = array_keys($children, $thisrecordid);
-        if (count($thischildren)){
-            foreach ($thischildren as $child){
+        if (count($thischildren)) {
+            foreach ($thischildren as $child) {
                 $thisclass = get_class($this);
                 $newlistitem =& new $thisclass($records[$child], $this->children, $this->attributes);
+                $this->children->add_item($newlistitem);
                 $newlistitem->create_children($records, $children, $records[$child]->id);
             }
         }
     }
-    function set_parent(&$parent){
+
+    function set_parent(&$parent) {
         $this->parentlist =& $parent;
-        $parent->add_item($this);
     }
-
 }
 ?>