From 8954245a1bdf5815404e532e39c82452f3d2a209 Mon Sep 17 00:00:00 2001 From: tjhunt Date: Tue, 23 Jun 2009 10:41:22 +0000 Subject: [PATCH] themes: MDL-19077 start implementing moodle_core_renderer Only a couple of methods implemented. Please review the API and tell me if you are not happy with the direction I am taking. --- lib/deprecatedlib.php | 11 + lib/outputlib.php | 315 ++++++++++++++++++++++++++- lib/simpletest/testoutputlib.php | 41 ++++ lib/simpletest/testsimpletestlib.php | 42 ++++ lib/simpletestlib.php | 68 ++++++ lib/weblib.php | 12 - 6 files changed, 476 insertions(+), 13 deletions(-) diff --git a/lib/deprecatedlib.php b/lib/deprecatedlib.php index ab8ce96779..e314c3e111 100644 --- a/lib/deprecatedlib.php +++ b/lib/deprecatedlib.php @@ -1735,3 +1735,14 @@ function make_mod_upload_directory($courseid) { return make_upload_directory($courseid .'/'. $CFG->moddata); } +/** + * Prints some red text using echo + * + * @deprecated + * @param string $error The text to be displayed in red + */ +function formerr($error) { + global $OUTPUT; + echo $OUTPUT->error_text($error); +} + diff --git a/lib/outputlib.php b/lib/outputlib.php index 579e19c5bc..adb781e5c6 100644 --- a/lib/outputlib.php +++ b/lib/outputlib.php @@ -335,6 +335,8 @@ class template_renderer_factory extends renderer_factory_base { * * Tracks the xhtml_container_stack to use, which is passed in in the constructor. * + * Also has methods to facilitate generating HTML output. + * * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.0 @@ -350,6 +352,36 @@ class moodle_renderer_base { public function __construct($containerstack) { $this->containerstack = $containerstack; } + + protected function output_tag($tagname, $attributes, $contents) { + return $this->output_start_tag($tagname, $attributes) . $contents . + $this->output_end_tag($tagname); + } + protected function output_start_tag($tagname, $attributes) { + return '<' . $tagname . $this->output_attributes($attributes) . '>'; + } + protected function output_end_tag($tagname) { + return ''; + } + protected function output_empty_tag($tagname, $attributes) { + return '<' . $tagname . $this->output_attributes($attributes) . ' />'; + } + + protected function output_attribute($name, $value) { + if ($value || is_numeric($value)) { // We want 0 to be output. + return ' ' . $name . '="' . $value . '"'; + } + } + protected function output_attributes($attributes) { + $output = ''; + foreach ($attributes as $name => $value) { + $output .= $this->output_attribute($name, $value); + } + return $output; + } + protected function output_class_attribute($classes) { + return $this->output_attribute('class', implode(' ', $classes)); + } } @@ -626,8 +658,289 @@ class xhtml_container_stack { * @since Moodle 2.0 */ class moodle_core_renderer extends moodle_renderer_base { + public function link_to_popup_window() { + + } - // TODO + public function button_to_popup_window() { + + } + + public function close_window_button($buttontext = null, $reloadopener = false) { + if (empty($buttontext)) { + $buttontext = get_string('closewindow'); + } + // TODO + } + + public function close_window($delay = 0, $reloadopener = false) { + // TODO + } + + /** + * Output a + */ + public function select_menu($selectmenu) { + $selectmenu = clone($selectmenu); + $selectmenu->prepare(); + + if ($selectmenu->nothinglabel) { + $selectmenu->options = array($selectmenu->nothingvalue => $selectmenu->nothinglabel) + + $selectmenu->options; + } + + if (empty($selectmenu->id)) { + $selectmenu->id = 'menu' . str_replace(array('[', ']'), '', $selectmenu->name); + } + + $attributes = array( + 'name' => $selectmenu->name, + 'id' => $selectmenu->id, + 'class' => $selectmenu->get_classes_string(), + 'onchange' => $selectmenu->script, + ); + if ($selectmenu->disabled) { + $attributes['disabled'] = 'disabled'; + } + if ($selectmenu->tabindex) { + $attributes['tabindex'] = $tabindex; + } + + if ($selectmenu->listbox) { + if (is_integer($selectmenu->listbox)) { + $size = $selectmenu->listbox; + } else { + $size = min($selectmenu->maxautosize, count($selectmenu->options)); + } + $attributes['size'] = $size; + if ($selectmenu->multiple) { + $attributes['multiple'] = 'multiple'; + } + } + + $html = $this->output_start_tag('select', $attributes) . "\n"; + foreach ($selectmenu->options as $value => $label) { + $attributes = array('value' => $value); + if ((string)$value == (string)$selectmenu->selectedvalue || + (is_array($selectmenu->selectedvalue) && in_array($value, $selectmenu->selectedvalue))) { + $attributes['selected'] = 'selected'; + } + $html .= ' ' . $this->output_tag('option', $attributes, s($label)) . "\n"; + } + $html .= $this->output_end_tag('select') . "\n"; + + return $html; + } + + // TODO choose_from_menu_nested + + // TODO choose_from_radio + + /** + * Output an error message. By default wraps the error message in . + * If the error message is blank, nothing is output. + * @param $message the error message. + * @return string the HTML to output. + */ + public function error_text($message) { + if (empty($message)) { + return ''; + } + return $this->output_tag('span', array('class' => 'error'), $message); + } +} + + +/** + * Base class for classes representing HTML elements, like moodle_select_menu. + * + * Handles the id and class attribues. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 2.0 + */ +class moodle_html_component { + /** + * @var string value to use for the id attribute of this HTML tag. + */ + public $id = ''; + /** + * @var array class names to add to this HTML element. + */ + public $classes = array(); + + /** + * Ensure some class names are an array. + * @param mixed $classes either an array of class names or a space-separated + * string containing class names. + * @return array the class names as an array. + */ + public static function clean_clases($classes) { + if (is_array($classes)) { + return $classes; + } else { + return explode(' '. trim($classes)); + } + } + + /** + * Set the class name array. + * @param mixed $classes either an array of class names or a space-separated + * string containing class names. + */ + public function set_classes($classes) { + $this->classes = self::clean_clases($classes); + } + + /** + * Add a class name to the class names array. + * @param string $class the new class name to add. + */ + public function add_class($class) { + $this->classes[] = $class; + } + + /** + * Add a whole lot of class names to the class names array. + * @param mixed $classes either an array of class names or a space-separated + * string containing class names. + */ + public function add_classes($classes) { + $this->classes += self::clean_clases($classes); + } + + /** + * Get the class names as a string. + * @return string the class names as a space-separated string. Ready to be put in the class="" attribute. + */ + public function get_classes_string() { + return implode(' ', $this->classes); + } + + /** + * Perform any cleanup or final processing that should be done before an + * instance of this class is output. + */ + public function prepare() { + $this->classes = array_unique(self::clean_clases($this->classes)); + } +} + + +/** + * This class hold all the information required to describe a element. Default 0. + */ + public $tabindex = 0; + /** + * @var mixed Defaults to false, which means display the select as a dropdown menu. + * If true, display this select as a list box whose size is chosen automatically. + * If an integer, display as list box of that size. + */ + public $listbox = false; + /** + * @var integer if you are using $listbox === true to get an automatically + * sized list box, the size of the list box will be the number of options, + * or this number, whichever is smaller. + */ + public $maxautosize = 10; + /** + * @var boolean if true, allow multiple selection. Only used if $listbox is true. + */ + public $multiple = false; + /** + * @deprecated + * @var string JavaScript to add as an onchange attribute. Do not use this. + * Use the YUI even library instead. + */ + public $script = ''; + + /* @see lib/moodle_html_component#prepare() */ + public function prepare() { + if (empty($this->id)) { + $this->id = 'menu' . str_replace(array('[', ']'), '', $this->name); + } + if (empty($this->classes)) { + $this->set_classes(array('menu' . str_replace(array('[', ']'), '', $this->name))); + } + $this->add_class('select'); + parent::prepare(); + } + + /** + * This is a shortcut for making a simple select menu. It lets you specify + * the options, name and selected option in one line of code. + * @param array $options used to initialise {@link $options}. + * @param string $name used to initialise {@link $name}. + * @param string $selected used to initialise {@link $selected}. + * @return moodle_select_menu A moodle_select_menu object with the three common fields initialised. + */ + public static function make($options, $name, $selected = '') { + $menu = new moodle_select_menu(); + $menu->options = $options; + $menu->name = $name; + $menu->selectedvalue = $selected; + return $menu; + } + + /** + * This is a shortcut for making a yes/no select menu. + * @param string $name used to initialise {@link $name}. + * @param string $selected used to initialise {@link $selected}. + * @return moodle_select_menu A menu initialised with yes/no options. + */ + public static function make_yes_no($name, $selected) { + return self::make(array(0 => get_string('no'), 1 => get_string('yes')), $name, $selected); + } } diff --git a/lib/simpletest/testoutputlib.php b/lib/simpletest/testoutputlib.php index 39ac921253..9aa3f49f31 100644 --- a/lib/simpletest/testoutputlib.php +++ b/lib/simpletest/testoutputlib.php @@ -773,3 +773,44 @@ class template_renderer_test extends UnitTestCase { $this->assertEqual('', $html); } } + + +/** + * Unit tests for the moodle_core_renderer class. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class moodle_core_renderer_test extends UnitTestCase { + protected $containerstack; + protected $renderer; + + public function setUp() { + parent::setUp(); + $this->containerstack = new xhtml_container_stack(); + $this->renderer = new moodle_core_renderer($this->containerstack); + } + + public function test_select_menu_simple() { + $selectmenu = moodle_select_menu::make(array(10 => 'ten', 'c2' => 'two'), 'mymenu'); + $html = $this->renderer->select_menu($selectmenu); + $this->assert(new ContainsTagWithAttribute('select', 'class', 'menumymenu select'), $html); + $this->assert(new ContainsTagWithAttribute('select', 'name', 'mymenu'), $html); + $this->assert(new ContainsTagWithAttribute('select', 'id', 'menumymenu'), $html); + $this->assert(new ContainsTagWithContents('option', 'ten'), $html); + $this->assert(new ContainsTagWithAttribute('option', 'value', '10'), $html); + $this->assert(new ContainsTagWithContents('option', 'two'), $html); + $this->assert(new ContainsTagWithAttribute('option', 'value', 'c2'), $html); + } + + public function test_error_text() { + $html = $this->renderer->error_text('message'); + $this->assert(new ContainsTagWithContents('span', 'message'), $html); + $this->assert(new ContainsTagWithAttribute('span', 'class', 'error'), $html); + } + + public function test_error_text_blank() { + $html = $this->renderer->error_text(''); + $this->assertEqual('', $html); + } +} diff --git a/lib/simpletest/testsimpletestlib.php b/lib/simpletest/testsimpletestlib.php index bce4214486..be95ac60f6 100644 --- a/lib/simpletest/testsimpletestlib.php +++ b/lib/simpletest/testsimpletestlib.php @@ -30,4 +30,46 @@ class simpletestlib_test extends FakeDBUnitTestCase { } } + +/** + * Unit tests for the ContainsTagWithAttribute_test class. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class ContainsTagWithAttribute_test extends UnitTestCase { + function test_simple() { + $expectation = new ContainsTagWithAttribute('span', 'class', 'error'); + $this->assertTrue($expectation->test('message')); + } + + function test_other_attrs() { + $expectation = new ContainsTagWithAttribute('span', 'class', 'error'); + $this->assertTrue($expectation->test('message')); + } + + function test_fails() { + $expectation = new ContainsTagWithAttribute('span', 'class', 'error'); + $this->assertFalse($expectation->test('message')); + } +} + +/** + * Unit tests for the ContainsTagWithAttribute_test class. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class ContainsTagWithContents_test extends UnitTestCase { + function test_simple() { + $expectation = new ContainsTagWithContents('span', 'message'); + $this->assertTrue($expectation->test('message')); + } + + function test_no_end() { + $expectation = new ContainsTagWithContents('span', 'message'); + $this->assertFalse($expectation->test('message')); + } +} + ?> diff --git a/lib/simpletestlib.php b/lib/simpletestlib.php index 3c447ae8d5..47956b50fa 100644 --- a/lib/simpletestlib.php +++ b/lib/simpletestlib.php @@ -135,6 +135,7 @@ class ArraysHaveSameValuesExpectation extends SimpleExpectation { } } + /** * An Expectation that compares to objects, and ensures that for every field in the * expected object, there is a key of the same name in the actual object, with @@ -186,6 +187,72 @@ class CheckSpecifiedFieldsExpectation extends SimpleExpectation { } } + +/** + * An Expectation that looks to see whether some HMTL contains a tag with a certain attribute. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class ContainsTagWithAttribute extends SimpleExpectation { + const ATTRSREGEX = '(?:\s+\w+\s*=\s*["\'][^"\'>]*["\'])*'; + + protected $tag; + protected $attribute; + protected $value; + + function __construct($tag, $attribute, $value, $message = '%s') { + $this->SimpleExpectation($message); + $this->tag = $tag; + $this->attribute = $attribute; + $this->value = $value; + } + + function test($html) { + $regex = '/<' . preg_quote($this->tag) . self::ATTRSREGEX . + '(?:\s+' . preg_quote($this->attribute) . '\s*=\s*["\']' . preg_quote($this->value) . '["\'])' . + self::ATTRSREGEX . '\s*>/'; + return preg_match($regex, $html); + } + + function testMessage($html) { + return 'Content [' . $html . '] does not contain the tag [' . + $this->tag . '] with attribute [' . $this->attribute . '="' . $this->value . '"].'; + } +} + + +/** + * An Expectation that looks to see whether some HMTL contains a tag with a certain text inside it. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class ContainsTagWithContents extends SimpleExpectation { + const ATTRSREGEX = '(?:\s+\w+\s*=\s*["\'][^"\'>]*["\'])*'; + + protected $tag; + protected $content; + + function __construct($tag, $content, $message = '%s') { + $this->SimpleExpectation($message); + $this->tag = $tag; + $this->content = $content; + } + + function test($html) { + $regex = '/<' . preg_quote($this->tag) . self::ATTRSREGEX . '\s*>' . preg_quote($this->content) . + '<\/' . preg_quote($this->tag) . '>/'; + return preg_match($regex, $html); + } + + function testMessage($html) { + return 'Content [' . $html . '] does not contain the tag [' . + $this->tag . '] with contents [' . $this->content . '].'; + } +} + + /** * This class lets you write unit tests that access a separate set of test * tables with a different prefix. Only those tables you explicitly ask to @@ -419,6 +486,7 @@ class UnitTestCaseUsingDatabase extends UnitTestCase { } } + /** * @package moodlecore * @subpackage simpletestex diff --git a/lib/weblib.php b/lib/weblib.php index 11f8ef9301..d0074b7bf2 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -1279,18 +1279,6 @@ function popup_form($baseurl, $options, $formid, $selected='', $nothing='choose' } -/** - * Prints some red text using echo - * - * @param string $error The text to be displayed in red - */ -function formerr($error) { - - if (!empty($error)) { - echo ''. $error .''; - } -} - /** * Validates an email to make sure it makes sense. * -- 2.39.5