diff --git a/lib/listlib.php b/lib/listlib.php index 708db97210aff..c1fd788d6b915 100644 --- a/lib/listlib.php +++ b/lib/listlib.php @@ -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 // @@ -34,29 +34,31 @@ * 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 @@ function moodle_list($type='ul', $attributes='', $editable = false, $pageurl=nul * * @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\tattributes))?(' '.$item->attributes):'').">"; $html .= $itemhtml; $html .= "\n"; @@ -149,7 +148,7 @@ function to_html($indent=0, $extraargs=array()){ } 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."type.">\n"; @@ -166,39 +165,36 @@ function to_html($indent=0, $extraargs=array()){ * @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,42 +203,42 @@ function set_parent(&$parent){ * 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(); $records = $this->records; $page = $this->page; if (!empty($page)) { - $this->firstitem = ($page-1) * $this->itemsperpage + 1; + $this->firstitem = ($page - 1) * $this->itemsperpage; $this->lastitem = $this->firstitem + $this->itemsperpage - 1; } $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 @@ function display_page_numbers() { $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 = "
".get_string('page').":\n"; foreach (range(1,$this->pagecount) as $currentpage) { if ($this->page == $currentpage) { @@ -302,7 +298,7 @@ function get_items_peers($itemid) { */ 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 @@ function move_item_up_down($direction, $id) { $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,20 +336,26 @@ function move_item_up_down($direction, $id) { } $this->reorder_peers($peers); } - function reorder_peers($peers){ + + function reorder_peers($peers) { foreach ($peers as $key => $peer) { - if (! set_field("{$this->table}", "sortorder", $key, "id", $peer)) { + if (!set_field("{$this->table}", "sortorder", $key, "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) { $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 @@ -367,7 +368,9 @@ function move_item_left($id) { $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. * @@ -376,17 +379,17 @@ function move_item_left($id) { function move_item_right($id) { $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 (!set_field("{$this->table}", "parent", $peers[$itemkey-1], "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); } @@ -403,24 +406,36 @@ function move_item_right($id) { * @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)); } @@ -430,24 +445,41 @@ function process_actions($left, $right, $moveup, $movedown){ 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{ +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; @@ -456,19 +488,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 @@ -476,7 +506,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; @@ -489,12 +519,13 @@ function list_item($item, &$parent, $attributes='', $display = true){ $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 @@ -503,6 +534,7 @@ function item_html($extraargs = array()){ } return $html; } + /** * Returns html * @@ -511,13 +543,13 @@ function item_html($extraargs = array()){ * 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 = ''; @@ -525,7 +557,7 @@ function to_html($indent=0, $extraargs = array()){ return $this->item_html($extraargs).' '.(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'); @@ -534,7 +566,7 @@ function set_icon_html($first, $last, &$lastitem){ 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; @@ -562,19 +594,21 @@ function set_icon_html($first, $last, &$lastitem){ } 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 ' ' . $action. ' '; } - function image_spacer(){ + + function image_spacer() { global $CFG; $pixpath = $CFG->pixpath; return ''; } + /** * Recurse down tree creating list_items, called from moodle_list::list_from_records * @@ -582,21 +616,21 @@ function image_spacer(){ * @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); } - } ?>