MDL-38309 move HTML generation from print_tabs() to renderer
authorMarina Glancy <marina@moodle.com>
Thu, 11 Apr 2013 09:06:52 +0000 (19:06 +1000)
committerMarina Glancy <marina@moodle.com>
Mon, 15 Apr 2013 02:13:03 +0000 (12:13 +1000)
- created renderable object tabtree that represents the tree of tabs, extends tabobject
- created core_renderer::tabtree(), and supporting core_renderer::render_tabtree(), core_renderer::render_tabobject()
- change print_tabs() to use renderer function, deprecate supporting not needed functions

lib/deprecatedlib.php
lib/outputcomponents.php
lib/outputrenderers.php
lib/weblib.php

index 8022c44..1fc0087 100644 (file)
@@ -4515,3 +4515,110 @@ function get_courses_wmanagers($categoryid=0, $sort="c.sortorder ASC", $fields=a
 
     return $courses;
 }
+
+/**
+ * Converts a nested array tree into HTML ul:li [recursive]
+ *
+ * @deprecated since 2.5
+ *
+ * @param array $tree A tree array to convert
+ * @param int $row Used in identifying the iteration level and in ul classes
+ * @return string HTML structure
+ */
+function convert_tree_to_html($tree, $row=0) {
+    debugging('Function convert_tree_to_html() is deprecated since Moodle 2.5. Consider using class tabtree and core_renderer::render_tabtree()', DEBUG_DEVELOPER);
+
+    $str = "\n".'<ul class="tabrow'.$row.'">'."\n";
+
+    $first = true;
+    $count = count($tree);
+
+    foreach ($tree as $tab) {
+        $count--;   // countdown to zero
+
+        $liclass = '';
+
+        if ($first && ($count == 0)) {   // Just one in the row
+            $liclass = 'first last';
+            $first = false;
+        } else if ($first) {
+            $liclass = 'first';
+            $first = false;
+        } else if ($count == 0) {
+            $liclass = 'last';
+        }
+
+        if ((empty($tab->subtree)) && (!empty($tab->selected))) {
+            $liclass .= (empty($liclass)) ? 'onerow' : ' onerow';
+        }
+
+        if ($tab->inactive || $tab->active || $tab->selected) {
+            if ($tab->selected) {
+                $liclass .= (empty($liclass)) ? 'here selected' : ' here selected';
+            } else if ($tab->active) {
+                $liclass .= (empty($liclass)) ? 'here active' : ' here active';
+            }
+        }
+
+        $str .= (!empty($liclass)) ? '<li class="'.$liclass.'">' : '<li>';
+
+        if ($tab->inactive || $tab->active || ($tab->selected && !$tab->linkedwhenselected)) {
+            // The a tag is used for styling
+            $str .= '<a class="nolink"><span>'.$tab->text.'</span></a>';
+        } else {
+            $str .= '<a href="'.$tab->link.'" title="'.$tab->title.'"><span>'.$tab->text.'</span></a>';
+        }
+
+        if (!empty($tab->subtree)) {
+            $str .= convert_tree_to_html($tab->subtree, $row+1);
+        } else if ($tab->selected) {
+            $str .= '<div class="tabrow'.($row+1).' empty">&nbsp;</div>'."\n";
+        }
+
+        $str .= ' </li>'."\n";
+    }
+    $str .= '</ul>'."\n";
+
+    return $str;
+}
+
+/**
+ * Convert nested tabrows to a nested array
+ *
+ * @deprecated since 2.5
+ *
+ * @param array $tabrows A [nested] array of tab row objects
+ * @param string $selected The tabrow to select (by id)
+ * @param array $inactive An array of tabrow id's to make inactive
+ * @param array $activated An array of tabrow id's to make active
+ * @return array The nested array
+ */
+function convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated) {
+
+    debugging('Function convert_tabrows_to_tree() is deprecated since Moodle 2.5. Consider using class tabtree', DEBUG_DEVELOPER);
+/// Work backwards through the rows (bottom to top) collecting the tree as we go.
+
+    $tabrows = array_reverse($tabrows);
+
+    $subtree = array();
+
+    foreach ($tabrows as $row) {
+        $tree = array();
+
+        foreach ($row as $tab) {
+            $tab->inactive = in_array((string)$tab->id, $inactive);
+            $tab->active = in_array((string)$tab->id, $activated);
+            $tab->selected = (string)$tab->id == $selected;
+
+            if ($tab->active || $tab->selected) {
+                if ($subtree) {
+                    $tab->subtree = $subtree;
+                }
+            }
+            $tree[] = $tab;
+        }
+        $subtree = $tree;
+    }
+
+    return $subtree;
+}
index 7ad3df7..e3080b1 100644 (file)
@@ -2857,3 +2857,155 @@ class custom_menu extends custom_menu_item {
         return ($itema > $itemb) ? +1 : -1;
     }
 }
+
+/**
+ * Stores one tab
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package core
+ */
+class tabobject implements renderable {
+    /** @var string unique id of the tab in this tree, it is used to find selected and/or inactive tabs */
+    var $id;
+    /** @var moodle_url|string link */
+    var $link;
+    /** @var string text on the tab */
+    var $text;
+    /** @var string title under the link, by defaul equals to text */
+    var $title;
+    /** @var bool whether to display a link under the tab name when it's selected */
+    var $linkedwhenselected = false;
+    /** @var bool whether the tab is inactive */
+    var $inactive = false;
+    /** @var bool indicates that this tab's child is selected */
+    var $activated = false;
+    /** @var bool indicates that this tab is selected */
+    var $selected = false;
+    /** @var array stores children tabobjects */
+    var $subtree = array();
+    /** @var int level of tab in the tree, 0 for root (instance of tabtree), 1 for the first row of tabs */
+    var $level = 1;
+
+    /**
+     * Constructor
+     *
+     * @param string $id unique id of the tab in this tree, it is used to find selected and/or inactive tabs
+     * @param string|moodle_url $link
+     * @param string $text text on the tab
+     * @param string $title title under the link, by defaul equals to text
+     * @param bool $linkedwhenselected whether to display a link under the tab name when it's selected
+     */
+    public function __construct($id, $link = null, $text = '', $title = '', $linkedwhenselected = false) {
+        $this->id = $id;
+        $this->link = $link;
+        $this->text = $text;
+        $this->title = $title ? $title : $text;
+        $this->linkedwhenselected = $linkedwhenselected;
+    }
+
+    /**
+     * Travels through tree and finds the tab to mark as selected, all parents are automatically marked as activated
+     *
+     * @param string $selected the id of the selected tab (whatever row it's on),
+     *    if null marks all tabs as unselected
+     * @return bool whether this tab is selected or contains selected tab in its subtree
+     */
+    protected function set_selected($selected) {
+        if ((string)$selected === (string)$this->id) {
+            $this->selected = true;
+            // This tab is selected. No need to travel through subtree.
+            return true;
+        }
+        foreach ($this->subtree as $subitem) {
+            if ($subitem->set_selected($selected)) {
+                // This tab has child that is selected. Mark it as activated. No need to check other children.
+                $this->activated = true;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Travels through tree and finds a tab with specified id
+     *
+     * @param string $id
+     * @return tabtree|null
+     */
+    public function find($id) {
+        if ((string)$this->id === (string)$id) {
+            return $this;
+        }
+        foreach ($this->subtree as $tab) {
+            if ($obj = $tab->find($id)) {
+                return $obj;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Allows to mark each tab's level in the tree before rendering.
+     *
+     * @param int $level
+     */
+    protected function set_level($level) {
+        $this->level = $level;
+        foreach ($this->subtree as $tab) {
+            $tab->set_level($level + 1);
+        }
+    }
+}
+
+/**
+ * Stores tabs list
+ *
+ * Example how to print a single line tabs:
+ * $rows = array(
+ *    new tabobject(...),
+ *    new tabobject(...)
+ * );
+ * echo $OUTPUT->tabtree($rows, $selectedid);
+ *
+ * Multiple row tabs may not look good on some devices but if you want to use them
+ * you can specify ->subtree for the active tabobject.
+ *
+ * @copyright 2013 Marina Glancy
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.5
+ * @package core
+ * @category output
+ */
+class tabtree extends tabobject {
+    /**
+     * Constuctor
+     *
+     * It is highly recommended to call constructor when list of tabs is already
+     * populated, this way you ensure that selected and inactive tabs are located
+     * and attribute level is set correctly.
+     *
+     * @param array $tabs array of tabs, each of them may have it's own ->subtree
+     * @param string|null $selected which tab to mark as selected, all parent tabs will
+     *     automatically be marked as activated
+     * @param array|string|null $inactive list of ids of inactive tabs, regardless of
+     *     their level. Note that you can as weel specify tabobject::$inactive for separate instances
+     */
+    public function __construct($tabs, $selected = null, $inactive = null) {
+        $this->subtree = $tabs;
+        if ($selected !== null) {
+            $this->set_selected($selected);
+        }
+        if ($inactive !== null) {
+            if (is_array($inactive)) {
+                foreach ($inactive as $id) {
+                    if ($tab = $this->find($id)) {
+                        $tab->inactive = true;
+                    }
+                }
+            } else if ($tab = $this->find($inactive)) {
+                $tab->inactive = true;
+            }
+        }
+        $this->set_level(0);
+    }
+}
index 04fed8f..2298120 100644 (file)
@@ -2882,6 +2882,107 @@ EOD;
 
         return $content;
     }
+
+    /**
+     * Renders tabs
+     *
+     * This function replaces print_tabs() used before Moodle 2.5 but with slightly different arguments
+     *
+     * @param array $tabs array of tabs, each of them may have it's own ->subtree
+     * @param string|null $selected which tab to mark as selected, all parent tabs will
+     *     automatically be marked as activated
+     * @param array|string|null $inactive list of ids of inactive tabs, regardless of
+     *     their level. Note that you can as weel specify tabobject::$inactive for separate instances
+     * @return string
+     */
+    public function tabtree($tabs, $selected = null, $inactive = null) {
+        return $this->render(new tabtree($tabs, $selected, $inactive));
+    }
+
+    /**
+     * Renders tabtree
+     *
+     * @param tabtree $tabtree
+     * @return string
+     */
+    protected function render_tabtree(tabtree $tabtree) {
+        if (empty($tabtree->subtree)) {
+            return '';
+        }
+        $str = '';
+        $str .= html_writer::start_tag('div', array('class' => 'tabtree'));
+        $str .= $this->render_tabobject($tabtree);
+        $str .= html_writer::end_tag('div').
+                html_writer::tag('div', ' ', array('class' => 'clearer'));
+        return $str;        
+    }
+
+    /**
+     * Renders tabobject (part of tabtree)
+     *
+     * This function is called from {@link core_renderer::render_tabtree()}
+     * and also it calls itself when printing the $tabobject subtree recursively.
+     *
+     * Property $tabobject->level indicates the number of row of tabs.
+     *
+     * @param tabobject $tabobject
+     * @return string HTML fragment
+     */
+    protected function render_tabobject(tabobject $tabobject) {
+        $str = '';
+
+        // Print name of the current tab.
+        if ($tabobject instanceof tabtree) {
+            // No name for tabtree root.
+        } else if ($tabobject->inactive || $tabobject->activated || ($tabobject->selected && !$tabobject->linkedwhenselected)) {
+            // Tab name without a link. The <a> tag is used for styling.
+            $str .= html_writer::tag('a', html_writer::span($tabobject->text), array('class' => 'nolink'));
+        } else {
+            // Tab name with a link.
+            if (!($tabobject->link instanceof moodle_url)) {
+                // backward compartibility when link was passed as quoted string
+                $str .= "<a href=\"$tabobject->link\" title=\"$tabobject->title\"><span>$tabobject->text</span></a>";
+            } else {
+                $str .= html_writer::link($tabobject->link, html_writer::span($tabobject->text), array('title' => $tabobject->title));
+            }
+        }
+
+        if (empty($tabobject->subtree)) {
+            if ($tabobject->selected) {
+                $str .= html_writer::tag('div', '&nbsp;', array('class' => 'tabrow'. ($tabobject->level + 1). ' empty'));
+            }
+            return $str;
+        }
+
+        // Print subtree
+        $str .= html_writer::start_tag('ul', array('class' => 'tabrow'. $tabobject->level));
+        $cnt = 0;
+        foreach ($tabobject->subtree as $tab) {
+            $liclass = '';
+            if (!$cnt) {
+                $liclass .= ' first';
+            }
+            if ($cnt == count($tabobject->subtree) - 1) {
+                $liclass .= ' last';
+            }
+            if ((empty($tab->subtree)) && (!empty($tab->selected))) {
+                $liclass .= ' onerow';
+            }
+
+            if ($tab->selected) {
+                $liclass .= ' here selected';
+            } else if ($tab->activated) {
+                $liclass .= ' here active';
+            }
+
+            // This will recursively call function render_tabobject() for each item in subtree
+            $str .= html_writer::tag('li', $this->render($tab), array('class' => trim($liclass)));
+            $cnt++;
+        }
+        $str .= html_writer::end_tag('ul');
+
+        return $str;
+    }
 }
 
 /**
index 767d78f..c77b8f7 100644 (file)
@@ -2670,185 +2670,55 @@ function print_maintenance_message() {
     die;
 }
 
-/**
- * A class for tabs, Some code to print tabs
- *
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @package moodlecore
- */
-class tabobject {
-    /**
-     * @var string
-     */
-    var $id;
-    var $link;
-    var $text;
-    /**
-     * @var bool
-     */
-    var $linkedwhenselected;
-
-    /**
-     * A constructor just because I like constructors
-     *
-     * @param string $id
-     * @param string $link
-     * @param string $text
-     * @param string $title
-     * @param bool $linkedwhenselected
-     */
-    function tabobject ($id, $link='', $text='', $title='', $linkedwhenselected=false) {
-        $this->id   = $id;
-        $this->link = $link;
-        $this->text = $text;
-        $this->title = $title ? $title : $text;
-        $this->linkedwhenselected = $linkedwhenselected;
-    }
-}
-
-
-
 /**
  * Returns a string containing a nested list, suitable for formatting into tabs with CSS.
  *
- * @global object
+ * It is not recommended to use this function in Moodle 2.5 but it is left for backward
+ * compartibility.
+ *
+ * Example how to print a single line tabs:
+ * $rows = array(
+ *    new tabobject(...),
+ *    new tabobject(...)
+ * );
+ * echo $OUTPUT->tabtree($rows, $selectedid);
+ *
+ * Multiple row tabs may not look good on some devices but if you want to use them
+ * you can specify ->subtree for the active tabobject.
+ *
  * @param array $tabrows An array of rows where each row is an array of tab objects
  * @param string $selected  The id of the selected tab (whatever row it's on)
  * @param array  $inactive  An array of ids of inactive tabs that are not selectable.
  * @param array  $activated An array of ids of other tabs that are currently activated
  * @param bool $return If true output is returned rather then echo'd
  **/
-function print_tabs($tabrows, $selected=NULL, $inactive=NULL, $activated=NULL, $return=false) {
-    global $CFG;
-
-/// $inactive must be an array
-    if (!is_array($inactive)) {
-        $inactive = array();
-    }
-
-/// $activated must be an array
-    if (!is_array($activated)) {
-        $activated = array();
-    }
-
-/// Convert the tab rows into a tree that's easier to process
-    if (!$tree = convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated)) {
-        return false;
-    }
-
-/// Print out the current tree of tabs (this function is recursive)
-
-    $output = convert_tree_to_html($tree);
-
-    $output = "\n\n".'<div class="tabtree">'.$output.'</div><div class="clearer"> </div>'."\n\n";
-
-/// We're done!
-
-    if ($return) {
-        return $output;
-    }
-    echo $output;
-}
-
-/**
- * Converts a nested array tree into HTML ul:li [recursive]
- *
- * @param array $tree A tree array to convert
- * @param int $row Used in identifying the iteration level and in ul classes
- * @return string HTML structure
- */
-function convert_tree_to_html($tree, $row=0) {
-
-    $str = "\n".'<ul class="tabrow'.$row.'">'."\n";
-
-    $first = true;
-    $count = count($tree);
-
-    foreach ($tree as $tab) {
-        $count--;   // countdown to zero
-
-        $liclass = '';
-
-        if ($first && ($count == 0)) {   // Just one in the row
-            $liclass = 'first last';
-            $first = false;
-        } else if ($first) {
-            $liclass = 'first';
-            $first = false;
-        } else if ($count == 0) {
-            $liclass = 'last';
-        }
-
-        if ((empty($tab->subtree)) && (!empty($tab->selected))) {
-            $liclass .= (empty($liclass)) ? 'onerow' : ' onerow';
-        }
-
-        if ($tab->inactive || $tab->active || $tab->selected) {
-            if ($tab->selected) {
-                $liclass .= (empty($liclass)) ? 'here selected' : ' here selected';
-            } else if ($tab->active) {
-                $liclass .= (empty($liclass)) ? 'here active' : ' here active';
-            }
-        }
-
-        $str .= (!empty($liclass)) ? '<li class="'.$liclass.'">' : '<li>';
-
-        if ($tab->inactive || $tab->active || ($tab->selected && !$tab->linkedwhenselected)) {
-            // The a tag is used for styling
-            $str .= '<a class="nolink"><span>'.$tab->text.'</span></a>';
-        } else {
-            $str .= '<a href="'.$tab->link.'" title="'.$tab->title.'"><span>'.$tab->text.'</span></a>';
-        }
-
-        if (!empty($tab->subtree)) {
-            $str .= convert_tree_to_html($tab->subtree, $row+1);
-        } else if ($tab->selected) {
-            $str .= '<div class="tabrow'.($row+1).' empty">&nbsp;</div>'."\n";
-        }
-
-        $str .= ' </li>'."\n";
-    }
-    $str .= '</ul>'."\n";
-
-    return $str;
-}
-
-/**
- * Convert nested tabrows to a nested array
- *
- * @param array $tabrows A [nested] array of tab row objects
- * @param string $selected The tabrow to select (by id)
- * @param array $inactive An array of tabrow id's to make inactive
- * @param array $activated An array of tabrow id's to make active
- * @return array The nested array
- */
-function convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated) {
-
-/// Work backwards through the rows (bottom to top) collecting the tree as we go.
+function print_tabs($tabrows, $selected = null, $inactive = null, $activated = null, $return = false) {
+    global $OUTPUT;
 
     $tabrows = array_reverse($tabrows);
-
     $subtree = array();
-
     foreach ($tabrows as $row) {
         $tree = array();
 
         foreach ($row as $tab) {
-            $tab->inactive = in_array((string)$tab->id, $inactive);
-            $tab->active = in_array((string)$tab->id, $activated);
+            $tab->inactive = is_array($inactive) && in_array((string)$tab->id, $inactive);
+            $tab->activated = is_array($activated) && in_array((string)$tab->id, $activated);
             $tab->selected = (string)$tab->id == $selected;
 
-            if ($tab->active || $tab->selected) {
-                if ($subtree) {
-                    $tab->subtree = $subtree;
-                }
+            if ($tab->activated || $tab->selected) {
+                $tab->subtree = $subtree;
             }
             $tree[] = $tab;
         }
         $subtree = $tree;
     }
-
-    return $subtree;
+    $output = $OUTPUT->tabtree($subtree);
+    if ($return) {
+        return $output;
+    } else {
+        print $output;
+        return !empty($output);
+    }
 }
 
 /**