MDL-38309 move HTML generation from print_tabs() to renderer

- 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
This commit is contained in:
Marina Glancy
2013-04-15 12:13:03 +10:00
parent b3661ab272
commit c269b9d16f
4 changed files with 386 additions and 156 deletions
+107
View 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;
}
+152
View 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);
}
}
+101
View 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;
}
}
/**
+26 -156
View 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);
}
}
/**