Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace core\navigation\output;
  18  
  19  use renderable;
  20  use renderer_base;
  21  use templatable;
  22  use custom_menu;
  23  
  24  /**
  25   * Primary navigation renderable
  26   *
  27   * This file combines primary nav, custom menu, lang menu and
  28   * usermenu into a standardized format for the frontend
  29   *
  30   * @package     core
  31   * @category    navigation
  32   * @copyright   2021 onwards Peter Dias
  33   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  class primary implements renderable, templatable {
  36      /** @var moodle_page $page the moodle page that the navigation belongs to */
  37      private $page = null;
  38  
  39      /**
  40       * primary constructor.
  41       * @param \moodle_page $page
  42       */
  43      public function __construct($page) {
  44          $this->page = $page;
  45      }
  46  
  47      /**
  48       * Combine the various menus into a standardized output.
  49       *
  50       * @param renderer_base|null $output
  51       * @return array
  52       */
  53      public function export_for_template(?renderer_base $output = null): array {
  54          if (!$output) {
  55              $output = $this->page->get_renderer('core');
  56          }
  57  
  58          $menudata = (object) array_merge($this->get_primary_nav(), $this->get_custom_menu($output));
  59          $moremenu = new \core\navigation\output\more_menu($menudata, 'navbar-nav', false);
  60          $mobileprimarynav = array_merge($this->get_primary_nav(), $this->get_custom_menu($output));
  61  
  62          $languagemenu = new \core\output\language_menu($this->page);
  63  
  64          return [
  65              'mobileprimarynav' => $mobileprimarynav,
  66              'moremenu' => $moremenu->export_for_template($output),
  67              'lang' => !isloggedin() || isguestuser() ? $languagemenu->export_for_template($output) : [],
  68              'user' => $this->get_user_menu($output),
  69          ];
  70      }
  71  
  72      /**
  73       * Get the primary nav object and standardize the output
  74       *
  75       * @return array
  76       */
  77      protected function get_primary_nav(): array {
  78          $nodes = [];
  79          foreach ($this->page->primarynav->children as $node) {
  80              $nodes[] = [
  81                  'title' => $node->get_title(),
  82                  'url' => $node->action(),
  83                  'text' => $node->text,
  84                  'icon' => $node->icon,
  85                  'isactive' => $node->isactive,
  86                  'key' => $node->key,
  87              ];
  88          }
  89  
  90          return $nodes;
  91      }
  92  
  93      /**
  94       * Custom menu items reside on the same level as the original nodes.
  95       * Fetch and convert the nodes to a standardised array.
  96       *
  97       * @param renderer_base $output
  98       * @return array
  99       */
 100      protected function get_custom_menu(renderer_base $output): array {
 101          global $CFG;
 102  
 103          // Early return if a custom menu does not exists.
 104          if (empty($CFG->custommenuitems)) {
 105              return [];
 106          }
 107  
 108          $custommenuitems = $CFG->custommenuitems;
 109          $currentlang = current_language();
 110          $custommenunodes = custom_menu::convert_text_to_menu_nodes($custommenuitems, $currentlang);
 111          $nodes = [];
 112          foreach ($custommenunodes as $node) {
 113              $nodes[] = $node->export_for_template($output);
 114          }
 115  
 116          return $nodes;
 117      }
 118  
 119      /**
 120       * Get/Generate the user menu.
 121       *
 122       * This is leveraging the data from user_get_user_navigation_info and the logic in $OUTPUT->user_menu()
 123       *
 124       * @param renderer_base $output
 125       * @return array
 126       */
 127      public function get_user_menu(renderer_base $output): array {
 128          global $CFG, $USER, $PAGE;
 129          require_once($CFG->dirroot . '/user/lib.php');
 130  
 131          $usermenudata = [];
 132          $submenusdata = [];
 133          $info = user_get_user_navigation_info($USER, $PAGE);
 134          if (isset($info->unauthenticateduser)) {
 135              $info->unauthenticateduser['content'] = get_string($info->unauthenticateduser['content']);
 136              $info->unauthenticateduser['url'] = get_login_url();
 137              return (array) $info;
 138          }
 139          // Gather all the avatar data to be displayed in the user menu.
 140          $usermenudata['avatardata'][] = [
 141              'content' => $info->metadata['useravatar'],
 142              'classes' => 'current'
 143          ];
 144          $usermenudata['userfullname'] = $info->metadata['realuserfullname'] ?? $info->metadata['userfullname'];
 145  
 146          // Logged in as someone else.
 147          if ($info->metadata['asotheruser']) {
 148              $usermenudata['avatardata'][] = [
 149                  'content' => $info->metadata['realuseravatar'],
 150                  'classes' => 'realuser'
 151              ];
 152              $usermenudata['metadata'][] = [
 153                  'content' => get_string('loggedinas', 'moodle', $info->metadata['userfullname']),
 154                  'classes' => 'viewingas'
 155              ];
 156          }
 157  
 158          // Gather all the meta data to be displayed in the user menu.
 159          $metadata = [
 160              'asotherrole' => [
 161                  'value' => 'rolename',
 162                  'class' => 'role role-##GENERATEDCLASS##',
 163              ],
 164              'userloginfail' => [
 165                  'value' => 'userloginfail',
 166                  'class' => 'loginfailures',
 167              ],
 168              'asmnetuser' => [
 169                  'value' => 'mnetidprovidername',
 170                  'class' => 'mnet mnet-##GENERATEDCLASS##',
 171              ],
 172          ];
 173          foreach ($metadata as $key => $value) {
 174              if (!empty($info->metadata[$key])) {
 175                  $content = $info->metadata[$value['value']] ?? '';
 176                  $generatedclass = strtolower(preg_replace('#[ ]+#', '-', trim($content)));
 177                  $customclass = str_replace('##GENERATEDCLASS##', $generatedclass, ($value['class'] ?? ''));
 178                  $usermenudata['metadata'][] = [
 179                      'content' => $content,
 180                      'classes' => $customclass
 181                  ];
 182              }
 183          }
 184  
 185          $modifiedarray = array_map(function($value) {
 186              $value->divider = $value->itemtype == 'divider';
 187              $value->link = $value->itemtype == 'link';
 188              if (isset($value->pix) && !empty($value->pix)) {
 189                  $value->pixicon = $value->pix;
 190                  unset($value->pix);
 191              }
 192              return $value;
 193          }, $info->navitems);
 194  
 195          // Include the language menu as a submenu within the user menu.
 196          $languagemenu = new \core\output\language_menu($this->page);
 197          $langmenu = $languagemenu->export_for_template($output);
 198          if (!empty($langmenu)) {
 199              $languageitems = $langmenu['items'];
 200              // If there are available languages, generate the data for the the language selector submenu.
 201              if (!empty($languageitems)) {
 202                  $langsubmenuid = uniqid();
 203                  // Generate the data for the link to language selector submenu.
 204                  $language = (object) [
 205                      'itemtype' => 'submenu-link',
 206                      'submenuid' => $langsubmenuid,
 207                      'title' => get_string('language'),
 208                      'divider' => false,
 209                      'submenulink' => true,
 210                  ];
 211  
 212                  // Place the link before the 'Log out' menu item which is either the last item in the menu or
 213                  // second to last when 'Switch roles' is available.
 214                  $menuposition = count($modifiedarray) - 1;
 215                  if (has_capability('moodle/role:switchroles', $PAGE->context)) {
 216                      $menuposition = count($modifiedarray) - 2;
 217                  }
 218                  array_splice($modifiedarray, $menuposition, 0, [$language]);
 219  
 220                  // Generate the data for the language selector submenu.
 221                  $submenusdata[] = (object)[
 222                      'id' => $langsubmenuid,
 223                      'title' => get_string('languageselector'),
 224                      'items' => $languageitems,
 225                  ];
 226              }
 227          }
 228  
 229          // Add divider before the last item.
 230          $modifiedarray[count($modifiedarray) - 2]->divider = true;
 231          $usermenudata['items'] = $modifiedarray;
 232          $usermenudata['submenus'] = array_values($submenusdata);
 233  
 234          return $usermenudata;
 235      }
 236  }