diff --git a/lib/outputcomponents.php b/lib/outputcomponents.php index 79f01e0db2c..2b18213bcc8 100644 --- a/lib/outputcomponents.php +++ b/lib/outputcomponents.php @@ -2323,15 +2323,22 @@ class custom_menu_item implements renderable { * @since Moodle 2.0 */ class custom_menu extends custom_menu_item { + + /** @var string the language we should render for, null disables multilang support */ + protected $currentlanguage = null; + /** * Creates the custom menu - * @param string $text Sets the text for this custom menu, never gets used and is optional + * + * @param string $definition the menu items definition in syntax required by {@link convert_text_to_menu_nodes()} + * @param string $language the current language code, null disables multilang support */ - public function __construct($text='base') { - global $CFG; - parent::__construct($text); - if (!empty($CFG->custommenuitems)) { - $this->override_children(self::convert_text_to_menu_nodes($CFG->custommenuitems)); + public function __construct($definition = '', $currentlanguage = null) { + + $this->currentlanguage = $currentlanguage; + parent::__construct('root'); // create virtual root element of the menu + if (!empty($definition)) { + $this->override_children(self::convert_text_to_menu_nodes($definition, $currentlanguage)); } } @@ -2353,8 +2360,9 @@ class custom_menu extends custom_menu_item { * then be added to a custom menu. * * Structure: - * text|url|title - * The number of hyphens at the start determines the depth of the item + * text|url|title|langs + * The number of hyphens at the start determines the depth of the item. The + * languages are optional, comma separated list of languages the line is for. * * Example structure: * First level first item|http://www.moodle.com/ @@ -2364,16 +2372,16 @@ class custom_menu extends custom_menu_item { * -Second level third item|http://www.moodle.com/development/ * First level second item|http://www.moodle.com/feedback/ * First level third item + * English only|http://moodle.com|English only item|en + * German only|http://moodle.de|Deutsch|de,de_du,de_kids + * * * @static - * @param string $text + * @param string $text the menu items definition + * @param string $language the language code, null disables multilang support * @return array */ - public static function convert_text_to_menu_nodes($text) { - $text = format_text($text, FORMAT_MOODLE, array( - 'para' => false, - 'newlines' => false, - 'context' => get_system_context())); + public static function convert_text_to_menu_nodes($text, $language = null) { $lines = explode("\n", $text); $children = array(); $lastchild = null; @@ -2381,27 +2389,38 @@ class custom_menu extends custom_menu_item { $lastsort = 0; foreach ($lines as $line) { $line = trim($line); - $bits = explode('|', $line ,4); // name|url|title|sort - if (!array_key_exists(0, $bits) || empty($bits[0])) { + $bits = explode('|', $line, 4); // name|url|title|langs + if (!array_key_exists(0, $bits) or empty($bits[0])) { // Every item must have a name to be valid continue; } else { $bits[0] = ltrim($bits[0],'-'); } - if (!array_key_exists(1, $bits)) { + if (!array_key_exists(1, $bits) or empty($bits[1])) { // Set the url to null $bits[1] = null; } else { // Make sure the url is a moodle url $bits[1] = new moodle_url(trim($bits[1])); } - if (!array_key_exists(2, $bits)) { + if (!array_key_exists(2, $bits) or empty($bits[2])) { // Set the title to null seeing as there isn't one $bits[2] = $bits[0]; } + if (!array_key_exists(3, $bits) or empty($bits[3])) { + // The item is valid for all languages + $itemlangs = null; + } else { + $itemlangs = array_map('trim', explode(',', $bits[3])); + } + if (!empty($language) and !empty($itemlangs)) { + // check that the item is intended for the current language + if (!in_array($language, $itemlangs)) { + continue; + } + } // Set an incremental sort order to keep it simple. - $bits[3] = $lastsort; - $lastsort = $bits[3]+1; + $lastsort++; if (preg_match('/^(\-*)/', $line, $match) && $lastchild != null && $lastdepth !== null) { $depth = strlen($match[1]); if ($depth < $lastdepth) { @@ -2411,26 +2430,26 @@ class custom_menu extends custom_menu_item { for ($i =0; $i < $difference; $i++) { $tempchild = $tempchild->get_parent(); } - $lastchild = $tempchild->add($bits[0], $bits[1], $bits[2], $bits[3]); + $lastchild = $tempchild->add($bits[0], $bits[1], $bits[2], $lastsort); } else { $depth = 0; - $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $bits[3]); + $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $lastsort); $children[] = $lastchild; } } else if ($depth > $lastdepth) { $depth = $lastdepth + 1; - $lastchild = $lastchild->add($bits[0], $bits[1], $bits[2], $bits[3]); + $lastchild = $lastchild->add($bits[0], $bits[1], $bits[2], $lastsort); } else { if ($depth == 0) { - $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $bits[3]); + $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $lastsort); $children[] = $lastchild; } else { - $lastchild = $lastchild->get_parent()->add($bits[0], $bits[1], $bits[2], $bits[3]); + $lastchild = $lastchild->get_parent()->add($bits[0], $bits[1], $bits[2], $lastsort); } } } else { $depth = 0; - $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $bits[3]); + $lastchild = new custom_menu_item($bits[0], $bits[1], $bits[2], $lastsort); $children[] = $lastchild; } $lastdepth = $depth; diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index 9f2bb835d78..1f00572bdc6 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -2410,7 +2410,7 @@ EOD; if (empty($CFG->custommenuitems)) { return ''; } - $custommenu = new custom_menu(); + $custommenu = new custom_menu($CFG->custommenuitems, current_language()); return $this->render_custom_menu($custommenu); } diff --git a/lib/simpletest/testoutputcomponents.php b/lib/simpletest/testoutputcomponents.php index 61f67086d35..f8aa788b2ef 100644 --- a/lib/simpletest/testoutputcomponents.php +++ b/lib/simpletest/testoutputcomponents.php @@ -112,3 +112,111 @@ class user_picture_test extends UnitTestCase { $this->assertEqual($returned->custom1, 'Value of custom1'); } } + + +/** + * Unit tests for the custom_menu class + */ +class custom_menu_test extends UnitTestCase { + + public function test_empty_menu() { + $emptymenu = new custom_menu(); + $this->assertTrue($emptymenu instanceof custom_menu); + $this->assertFalse($emptymenu->has_children()); + } + + public function test_basic_syntax() { + $definition = <<assertTrue($menu instanceof custom_menu); + $this->assertTrue($menu->has_children()); + $firstlevel = $menu->get_children(); + $this->assertIsA($firstlevel, 'array'); + $this->assertEqual(2, count($firstlevel)); + + $item = array_shift($firstlevel); + $this->assertTrue($item instanceof custom_menu_item); + $this->assertTrue($item->has_children()); + $this->assertEqual(3, count($item->get_children())); + $this->assertEqual('Moodle community', $item->get_text()); + $itemurl = $item->get_url(); + $this->assertTrue($itemurl instanceof moodle_url); + $this->assertEqual('http://moodle.org', $itemurl->out()); + $this->assertEqual($item->get_text(), $item->get_title()); // implicit title + + $item = array_shift($firstlevel); + $this->assertTrue($item->has_children()); + $this->assertEqual(2, count($item->get_children())); + $this->assertEqual('Moodle company', $item->get_text()); + $this->assertTrue(is_null($item->get_url())); + + $subitem = array_shift($item->get_children()); + $this->assertFalse($subitem->has_children()); + $this->assertEqual('Hosting', $subitem->get_text()); + $this->assertEqual('Commercial hosting', $subitem->get_title()); + } + + public function test_multilang_support() { + $definition = <<assertTrue($menu->has_children()); + $this->assertEqual(2, count($menu->get_children())); + + $infomenu = array_pop($menu->get_children()); + $this->assertTrue($infomenu->has_children()); + $this->assertEqual(2, count($infomenu->get_children())); + + $langspecinfo = array_shift($infomenu->get_children()); + $this->assertEqual('Information in English', $langspecinfo->get_title()); + + // same menu for English language selected + $menu = new custom_menu($definition, 'en'); + $this->assertTrue($menu->has_children()); + $this->assertEqual(2, count($menu->get_children())); + + $infomenu = array_pop($menu->get_children()); + $this->assertTrue($infomenu->has_children()); + $this->assertEqual(1, count($infomenu->get_children())); + + $langspecinfo = array_shift($infomenu->get_children()); + $this->assertEqual('Information in English', $langspecinfo->get_title()); + + // same menu for German (de_du) language selected + $menu = new custom_menu($definition, 'de_du'); + $this->assertTrue($menu->has_children()); + $this->assertEqual(2, count($menu->get_children())); + + $infomenu = array_pop($menu->get_children()); + $this->assertTrue($infomenu->has_children()); + $this->assertEqual(1, count($infomenu->get_children())); + + $langspecinfo = array_shift($infomenu->get_children()); + $this->assertEqual('Informationen in deutscher Sprache', $langspecinfo->get_title()); + + // same menu for Czech language selected + $menu = new custom_menu($definition, 'cs'); + $this->assertTrue($menu->has_children()); + $this->assertEqual(2, count($menu->get_children())); + + $infomenu = array_pop($menu->get_children()); + $this->assertFalse($infomenu->has_children()); + } +}