Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Navigation API: Difference between revisions

From MoodleDocs
m (Protected "Navigation API": Developer Docs Migration ([Edit=Allow only administrators] (indefinite)))
 
(40 intermediate revisions by 16 users not shown)
Line 1: Line 1:
{{Template:Migrated|newDocId=/docs/apis/core/navigation/}}
==Overview==
==Overview==
The Navigation API allows for the manipulation of the navigation system used in Moodle.
The Navigation API allows for the manipulation of the navigation system used in Moodle.


==What the navigation is==
==What the navigation is==
It's very important to understand what the navigation is exactly within Moodle. One of the numerous goals for Moodle 2.0 was to standardise navigation throughout Moodle and try to bring order to the structure of a Moodle site.
It's very important to understand what the navigation is exactly within Moodle. One of the goals for Moodle 2.0 was to standardise navigation throughout Moodle and try to bring order to the structure of a Moodle site.  Navigation is available through the page object '''$PAGE''', against which you set the heading for the page, the title, any JavaScript requirements, etc.  The navigation structure uses the information $PAGE contains to generate a navigation structure for the site.  The navigation or settings [[blocks]] are interpretations of the navigation structure Moodle creates.


The solution to this was of course the navigation. Don't think about the navigation or settings blocks those are just interpretations of the navigation structure Moodle creates. The navigation structure is what you really need to understand.
This navigation structure is available through three variables:


For those of you who have already started looking into either writing code for Moodle or converting some existing code you will have undoubtedly come across the page object '''$PAGE''' against which you set the heading for the page, the title, any JavaScript requirements, and a whole lot of other stuff that is used by the page.
; $PAGE->navigation : This is the main navigation structure, it will contain items that will allow the user to browse to the other available pages.
; $PAGE->settingsnav : This is the settings navigation structure contains items that will allow the user to edit settings.
; $PAGE->navbar : The navbar is a special structure for page breadcrumbs.


The navigation structure within Moodle uses the information $PAGE contains to generate a navigation structure for the site that reflects the page that the user is viewing.
A conceptual view of the information architecture that sits behind the navigation tree is here:


This navigation structure is available through three variables that can be interacted with to alter the navigation, but more about that later.
[[File:Moodle-IA.png|200px|thumb|center|Information Architecture]]
The three variables are:


; $PAGE->navigation : This is the main navigation structure, it will contain items that will allow the user to browse to the other important pages that are available to them.
This diagram represents the major entities and how they are related to each other. Examples are given of the type of functions available on each kind of entity.
; $PAGE->settingsnav : This is the settings navigation structure, it will contain items that will allow the user to edit different things for whatever they are viewing.
; $PAGE->navbar : The navbar is a special structure, it is essentially the active item in the navigation preceded by all of its parents plus anything the page wants to add.


The above is a basic overview of the navigation structures which the following sections will attempt to explain in more detail. So onto what the navigation is not.
==What the navigation is not==
The navigation is '''NOT''' the navigation block or the settings block!  These two blocks were created to display the navigation structure. The navigation block looks at ''$PAGE->navigation'', and the settings block looks at ''$PAGE->settingsnav''.  Both blocks interpret their data into an HTML structure and render it.


==What the navigation isn't==
Now it is equally important to look at what the navigation is not and why that is.
The navigation is '''NOT''' the navigation block or the settings block!
These two blocks were in fact created at the same time as the navigation in order to display it, and that is all they do. Each block looks at a part of the navigation structure, the navigation block looks at ''$PAGE->navigation'', and the settings block looks at ''$PAGE->settingsnav'' and then both blocks interpret it into an HTML structure that allows you to see what is going on.
There are two things that this should lead you to understand:
# The navigation is a back-end structure that is built behind the scenes and has no immediate method of display.
# The navigation is a back-end structure that is built behind the scenes and has no immediate method of display.
# The navigation and settings blocks display the back-end navigation structure but add nothing to it at all.
# The navigation and settings blocks display the back-end navigation structure but add nothing to it at all.
::In the [[model-view-controller pattern]], $PAGE->navigation, $PAGE->settingsnav, and $PAGE->navbar are the models, and the blocks are views.


The navbar also needs to be looked at. The navbar as mentioned above is just the path to the active navigation or settings item. In the case of the navbar however it is not displayed by a block, instead it is displayed by the core renderer. This is of course because the navbar is not normally displayed as a block but added into the theme's layout files. Other than that difference it is just like the rest of the navigation, a back-end structure that is displayed by a separate something.
The navbar is just the path to the active navigation or settings item. The navbar is not displayed by a block; instead it is added into the theme's layout files and displayed by the core renderer.  
 
::If you are familiar with model-view-controller pattern, then $PAGE->navigation, $PAGE->settingsnav and $PAGE->navbar are the model, and the navigation and settings blocks are views.


==How the navigation works==
==How the navigation works==
Alright so now that we know what the navigation is, and what it isn't lets look into it in greater detail starting with the main navigation structure.


First up the main navigation structure can be accessed through '''$PAGE->navigation''', and worth mentioning is that $PAGE is essential to the navigation, without it the navigation wouldn't be able to generate a structure for the Moodle site or work out what the user is looking at and correlate that with the structure, which is what we shall look at now.
The main navigation structure can be accessed through '''$PAGE->navigation'''.  The navigation and settings are contextual in that they will relate to the page that the user is viewing. This is determined by other $PAGE object properties:
* '''$PAGE->context''' is a Moodle context that immediately outlines the nature of the page the user is viewing.
* '''$PAGE->course''' is the course the user is viewing.  This is essential if the context is CONTEXT_COURSE or anything within it.  However, it is also useful in other contexts such as CONTEXT_USER.
* '''$PAGE->cm''' is the course module instance.  This is essential if the context is CONTEXT_MODULE.
* '''$PAGE->url''' is used to match the active navigation item.  


As mentioned the $PAGE object is referenced by the navigation when it is generating its structure, this is is because the navigation, and settings are contextual in that as well as being a general navigation structure they will relate to the page that the user is viewing. This is of course determined by the things that the $PAGE object has been made aware of. The most import parts are listed below.
Nearly every page sets $PAGE->url through a call to '''$PAGE->set_url''' however not many explicitly set the context, course, or cm. When you call '''require_login''' with a course or cm it automatically calls the following:
* '''$PAGE->context''' is the most essential, it is of course a Moodle context that immediately outlines the nature of the page the user is viewing.
<syntaxhighlight lang="php">
* '''$PAGE->course''' is used to get the course the user is viewing, this of course is essential if the context is CONTEXT_COURSE or greater, however it may also be useful in other areas such as CONTEXT_USER.
* '''$PAGE->cm''' if the user is within a page promoting CONTEXT_MODULE or greater this will be the course module instance the page is using.
* '''$PAGE->url''' is critical to the navigation, it is used to match the active navigation item. If you have a page on the navigation but it is not being found to be the active page check here first.
 
So at this point we know how the navigation finds out about what the user is viewing, now we need to quickly look at how navigation gets that information. As you've probably realised by looking at code nearly every page sets $PAGE->url through a call to '''$PAGE->set_url''' however not many explicitly set the context, course, of cm.
 
This is because require_login does it for you if it is called with a course, and or cm (other than the front-page course). When you call require_login with the a course and/or cm it automatically calls the following:
<code php>
if ($cm) {
if ($cm) {
     $PAGE->set_cm($cm, $course); // set's up global $COURSE
     $PAGE->set_cm($cm, $course); // sets up global $COURSE
} else if ($cm) {
} else {
     $PAGE->set_course($course);
     $PAGE->set_course($course);// sets up global $COURSE
</code>
</syntaxhighlight>
Then within each of those two functions it sets the context for the item it was just given. This is how $PAGE finds out about courses and course modules.


Providing require_login is being called correctly a page will only be required to explicitly set a context, course, or cm if one of the following conditions are met:
A page will only be required to explicitly set a context, course, or cm under one of these conditions:
 
# '''require_login''' is NOT being called correctly
# '''$PAGE->set_context''' The page is using CONTEXT_SYSTEM, CONTEXT_COURSECAT, or CONTEXT_USER.
# The page is using CONTEXT_SYSTEM, CONTEXT_COURSECAT, or CONTEXT_USER (call '''$PAGE->set_context''' ).
# '''$PAGE->set_course''' or '''$PAGE->set_cm''' The page is using a course or cm but it is also using one of the above contexts.
# The page is using a course or cm but it is also using one of the above contexts (call '''$PAGE->set_course''' or '''$PAGE->set_cm''' ).
 
So by looking at the $PAGE object the navigation is able to generate a structure that reflects the Moodle site as well as the page the user is viewing.
 
Before we move on to look at how to interact with the navigation however there is one more thing that you need to know and that is when the navigation structure is generated.
 
Of course we can't generate the navigation structure immediately as we need to give the code a chance to set the $PAGE object correctly. Because of this the navigation structure is only generated when it is first used. This may be either when something tries to access the structure or when code tries to add to it.
Also the navigation is initialised in a specific order:


The navigation structure cannot be generated before the $PAGE object is configured. It is only generated when it is first used, either when something tries to access the structure or when code tries to add to it.  The navigation is initialised in a specific order:
# Main navigation structure
# Main navigation structure
# Settings navigation
# Settings navigation
# Navbar
# Navbar (does not need to be generated because of its simple contents and rendering)
 
The navbar however is a special case, as it is just the path up to and including the active item it doesn't need generation, it will of course be determined when interacted with by which time everything else will have been generated.


==Extending the navigation==
==Extending the navigation==
Before we tear into this we need to look at the different ways in which you can extend or manipulate the navigation.
# '''Code extension''' : This method of extending is when the code arbitrarily extends the navigation during its execution. Extending the navigation through this means allows you to extend the navigation anywhere easily, however it will only be shown on pages where your extending code gets called (you should probably put it in a function within lib.php).
## '''Navigation''' : This is extending the main navigation structure.
## '''Settings navigation''' : This is extending the settings navigation.
## '''The navbar''' : This is adding to the navbar.
# '''Plugin call-backs''' : These are specific functions that the navigation looks for and calls if they exist for the plugin, presently only three plugin types can extend the navigation through these call-backs.
## '''Modules''' : Modules have two call-back methods, first to extend the navigation, and second to extend the settings. These call-backs get called when ever the user is viewing a page within the module and should only extend the navigation for the module.
## '''Course formats''' : Course formats are able to completely redefine the way in which navigation is generated for a course, as well as this they also have several methods to ensure the navigation is generated correctly.
## '''Course reports''' : By default reports don't add themselves or anything else to the navigation however there is a call-back that can be implemented to allow them to do so.


===Code extension===
===Code extension===
These examples are taken from the breadcrumb discussion in the forums, linked in further reading.
This method of extending is when the code arbitrarily extends the navigation during its execution. Extending the navigation through this means allows you to extend the navigation anywhere easily, however it will only be shown on pages where your extending code gets called (you should probably put it in a function within lib.php).
 
Navigation extensions that apply all the time (even when not on pages including your code) can be made by putting them in your plugin's settings.php file.
 
These examples are taken from the [http://moodle.org/mod/forum/discuss.php?d=152391 General Developer Forum: Moodle 2 - how to set up breadcrumbs for a module page]. It has further information that is well worth reading.
====Navigation====
====Navigation====
<code php>
This is extending the main navigation structure.
<syntaxhighlight lang="php">
$previewnode = $PAGE->navigation->add(get_string('preview'), new moodle_url('/a/link/if/you/want/one.php'), navigation_node::TYPE_CONTAINER);
$previewnode = $PAGE->navigation->add(get_string('preview'), new moodle_url('/a/link/if/you/want/one.php'), navigation_node::TYPE_CONTAINER);
$thingnode = $previewnode->add(get_string('name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
$thingnode = $previewnode->add(get_string('name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
$thingnode->make_active();
$thingnode->make_active();
</code>
</syntaxhighlight>
The above lines of code adds a preview node to the bottom of the navigation and then adds a thingnode to the previewnode (adding a leaf to our tree).
The above lines of code adds a preview node to the bottom of the navigation and then adds a thingnode to the previewnode (adding a leaf to our tree).
The final line of code makes the thingnode active so that the navbar finds it however if the URL you give it is the same as the url you set for the page it will automatically be marked active and you won't need this call.
The final line of code makes the thingnode active so that the navbar finds it however if the URL you give it is the same as the url you set for the page it will automatically be marked active and you won't need this call.
Line 99: Line 73:
Next extending the navigation for the course.
Next extending the navigation for the course.
For this you will need to know the course id and have called require_login($courseorid); so that the navigation is loaded for the course.
For this you will need to know the course id and have called require_login($courseorid); so that the navigation is loaded for the course.
<code php>
<syntaxhighlight lang="php">
$coursenode = $PAGE->navigation->find($courseid, navigation_node::TYPE_COURSE);
$coursenode = $PAGE->navigation->find($courseid, navigation_node::TYPE_COURSE);
$thingnode = $coursenode->add(get_string('Name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
$thingnode = $coursenode->add(get_string('Name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
$thingnode->make_active();
$thingnode->make_active();
</code>
</syntaxhighlight>
The new bit of code here really is the first line which simply finds the course node, to do this we give it the course id and the node type in this case TYPE_COURSE.
The new bit of code here really is the first line which simply finds the course node, to do this we give it the course id and the node type in this case TYPE_COURSE.
What we are doing here is relying on the navigation to generate the navigation up to the course and then just adding to the course.
What we are doing here is relying on the navigation to generate the navigation up to the course and then just adding to the course.
Note that Moodle loads plugins in alphabetical order. Therefore plugin_b can find a node added by plugin_a but not the other way around.
====Settings navigation====
====Settings navigation====
Adding to the settings navigation is very similar to navigation
Adding to the settings navigation is very similar to navigation
<code php>
<syntaxhighlight lang="php">
$settingnode = $PAGE->settingsnav->add(get_string('setting'), new moodle_url('/a/link/if/you/want/one.php'), navigation_node::TYPE_CONTAINER);
$settingnode = $PAGE->settingsnav->add(get_string('setting'), new moodle_url('/a/link/if/you/want/one.php'), navigation_node::TYPE_CONTAINER);
$thingnode = $settingnode->add(get_string('Name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
$thingnode = $settingnode->add(get_string('Name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
$thingnode->make_active();
$thingnode->make_active();
</code>
</syntaxhighlight>
 
====Add Settings folder to navigation====
 
An example of a settings folder in a module can be see by navigating to Site administration / Plugins / Activity modules / Assignment.
 
[[File:assignmentmenu.png]]
 
An example of adding a navigation folder to a settings.php for a block with a link to the settings page and a external page is bellow.
 
<syntaxhighlight lang="php">
<?php
 
defined('MOODLE_INTERNAL') || die;
 
// Create folder / submenu in block menu, modsettings for activity modules, localplugins for Local plugins.
// The default folders are defined in admin/settings/plugins.php.
$ADMIN->add('blocksettings', new admin_category('blocksamplefolder',
        get_string('pluginname', 'mod_sample')));
 
// Create settings block.
$settings = new admin_settingpage($section, get_string('settings', 'block_sample'));
if ($ADMIN->fulltree) {
    $settings->add(new admin_setting_configcheckbox('block_sample_checkbox', get_string('checkbox', 'block_sample'),
        get_string('checkboxdescription', 'block_kronoshtml'), 0));
}
 
// This adds the settings link to the folder/submenu.
$ADMIN->add('blocksamplefolder', $settings);
// This adds a link to an external page.
$ADMIN->add('blocksamplefolder', new admin_externalpage('block_sample_page', get_string('externalpage', 'block_sample'),
        $CFG->wwwroot.'/blocks/sample/sample.php'));
// Prevent Moodle from adding settings block in standard location.
$settings = null;
</syntaxhighlight>
 
====Navbar====
====Navbar====
<code php>
This is extending the settings navigation.
<syntaxhighlight lang="php">
$PAGE->navbar->ignore_active();
$PAGE->navbar->ignore_active();
$PAGE->navbar->add(get_string('preview'), new moodle_url('/a/link/if/you/want/one.php'));
$PAGE->navbar->add(get_string('preview'), new moodle_url('/a/link/if/you/want/one.php'));
$PAGE->navbar->add(get_string('name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
$PAGE->navbar->add(get_string('name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
</code>
</syntaxhighlight>
The above code tells the navbar to ignore what ever the active page was and just use what you add, at which point we add two items as shown.
The above code tells the navbar to ignore what ever the active page was and just use what you add, at which point we add two items as shown.


==Manipulating the navigation==
===Plugin Callbacks===
Why would you want to, its perfect just as it is.
These are specific functions that the navigation looks for and calls if they exist for the plugin, presently only three plugin types can extend the navigation through these call-backs.
 
Ideally all entries in "Administration / Site administration" tree should be done via settings.php files but sometimes it may be easier to directly modify the navigation structure created from the admin settings tree (such as when adding links to external pages).
 
====Module====
Modules have two call-back methods, first to extend the navigation, and second to extend the settings. These call-backs get called when ever the user is viewing a page within the module and should only extend the navigation for the module.
<syntaxhighlight lang="php">
function {modulename}_extend_navigation(${modulename}node, $course, $module, $cm)
function {modulename}_extend_settings_navigation($settings, ${modulename}node)
</syntaxhighlight>
 
You may be required to add a node in a specified order within the menu navigation menus. To do this you need to examine the node object as given in the respective parameters in the functions above, then find the key of the childnode you wish to place the link before. For example, applying the code below will put a direct link to grade report.
<syntaxhighlight lang="php">
function my_plugin_extend_settings_navigation($settingsnav, $context){
    if(($context->contextlevel === 50) &&
        has_capability('gradereport/grader:view', $context)){
        $id = $context->instanceid;
        $urltext = get_string('gradereportlink', 'myplugin');
        $url = new moodle_url($CFG->wwwroot . '/grade/report/grader/index.php', array('id'=>$id));
        $coursesettingsnode = $settingsnav->find('courseadmin', null);  // 'courseadmin' is the menu key
        $node = $coursesettingsnode->create($urltext, $url, navigation_node::NODETYPE_LEAF, null, 'gradebook',  new pix_icon('i/report', 'grades'));
        $coursesettingsnode->add_node($node,  'gradebooksetup'); //'gradebooksetup' is an where you put the link before
    }
...
</syntaxhighlight>
 
 
====Course Formats====
Course formats are able to completely redefine the way in which navigation is generated for a course, as well as this they also have several methods to ensure the navigation is generated correctly.
 
====Course Reports====
By default reports don't add themselves or anything else to the navigation however there is a call-back that can be implemented to allow them to do so.
 
====Local Plugins====
{{Moodle 2.9}}Local plugins have two call-back methods, first to extend the navigation, and second to extend the settings.
<syntaxhighlight lang="php">
function local_{pluginname}_extend_navigation(global_navigation $nav)
function local_{pluginname}_extend_settings_navigation(settings_navigation $nav, context $context)
</syntaxhighlight>
 
In versions before Moodle 2.9, the supported callbacks have <tt>_extends_</tt> (not imperative mood) in their names. This was a consistency bug fixed in MDL-49643.
 
<syntaxhighlight lang="php">
function local_{pluginname}_extends_navigation(global_navigation $nav)
function local_{pluginname}_extends_settings_navigation(settings_navigation $nav, context $context)
</syntaxhighlight>
 
====Course settings====
{{Moodle 3.0}}
Any plugin implementing the following callback in lib.php can extend the course settings navigation. Prior to 3.0 only ''reports'' and ''admin tools'' could extend the course settings navigation.
 
<syntaxhighlight lang="php">
function <component>_extend_navigation_course(navigation_node $parentnode, stdClass $course, context_course $context);
</syntaxhighlight>
 
====User settings====
{{Moodle 3.0}}
Any plugin implementing the following callback in lib.php can extend the user settings navigation. Prior to 3.0 only ''admin tools'' could extend the user settings navigation.
 
<syntaxhighlight lang="php">
function <component>_extend_navigation_user_settings(navigation_node $parentnode, stdClass $user, context_user $context, stdClass $course, context_course $coursecontext);
</syntaxhighlight>
 
====Category settings====
{{Moodle 3.0}}
Any plugin implementing the following callback in lib.php can extend the category settings navigation.
 
<syntaxhighlight lang="php">
function <component>_extend_navigation_category_settings(navigation_node $parentnode, context_coursecat $context);
</syntaxhighlight>
 
====Frontpage settings====
{{Moodle 3.0}}
Any plugin implementing the following callback in lib.php can extend the frontpage settings navigation. Prior to 3.0 only ''admin tools'' could extend the frontpage settings navigation.
 
<syntaxhighlight lang="php">
function <component>_extend_navigation_frontpage(navigation_node $parentnode, stdClass $course, context_course $context);
</syntaxhighlight>
 
====User profile====
{{Moodle 3.1}}
Any plugin implementing the following callback in lib.php can extend the user profile navigation.
<syntaxhighlight lang="php">
function <component>_extend_navigation_user(navigation_node $parentnode, stdClass $user, context_user $context, stdClass $course, context_course $coursecontext);
</syntaxhighlight>
 
=== Boost ===
The navigation API is specifically about allowing the manipulation of nodes in an in-memory tree structure that is used as the basis of building navigation components in the page. The navigation and settings blocks are 2 examples of such components and the flat navigation and settings menus in the Boost theme are some more. The navigation component itself can decide to show all or only part of the navigation tree in order to not overwhelm the user viewing the page. Whether a node is actually displayed depends on where in the tree the node was added, what is the current page in the navigation tree, and the specific navigation components that are used to provide navigation functionality in the current theme.
 
If you are testing in the Boost theme - all nodes that are added to settings tree are displayed in the course/activity settings menu which is shown as a cog on the front page of the course / activity. Only the most important pre-selected nodes are displayed in the flat-navigation drawer in order to provide consistency and avoid overwhelming the user with too many links. It is possible, but definitely NOT RECOMMENDED for plugins to add nodes to the flat navigation (see [#FAQ.27s_and_troubleshooting] for more information).


==FAQ's and troubleshooting==
==FAQ's and troubleshooting==
Line 128: Line 229:


The first thing to do here is check the URL you are setting for the page. It should match the URL your page has within the navigation. If it doesn't you have two options, first change your '''$PAGE->set_url''' call, or second override the URL the navigation is using to find the active node as shown below:
The first thing to do here is check the URL you are setting for the page. It should match the URL your page has within the navigation. If it doesn't you have two options, first change your '''$PAGE->set_url''' call, or second override the URL the navigation is using to find the active node as shown below:
<code php>
<syntaxhighlight lang="php">
navigation_node::override_active_url(new moodle_url('/your/url/here.php', array('param'=>'value')));
navigation_node::override_active_url(new moodle_url('/your/url/here.php', array('param'=>'value')));
</code>
</syntaxhighlight>
 
'''Q.''' How do I add a node to the "flat" navigation in the Boost theme for Moodle 3.2?
 
After creating a node and adding it to the navigation tree - you can set the property 'showinflatnavigation' to true in order for this node to be displayed in the flat navigation.
 
<syntaxhighlight lang="php">
$node = navigation_node::create(...);
$node->showinflatnavigation = true;
$navigation->add_node($node);
</syntaxhighlight>
 
This is highly discouraged because the number of nodes in this flat navigation has been deliberately restricted to a very small number of the most important links that are applicable to all user roles. Adding more links to this menu will make it harder to use, inconsistent for different users and inconsistent for different sites. Consider carefully if you really need to fill an additional 230x44 pixels of every single page in Moodle for every single user with a link to your thing. There are many other places to include links to your thing and most are automatically built from the navigation tree without forcing nodes to display in the flat navigation. E.g. settings menu of a course, profile page, preferences page, reports etc.
 
==See also==
 
* [[Core APIs]]
* [https://moodle.org/mod/forum/discuss.php?d=170325&parent=753095 Forum discussion - adding navigation to local plugins]


==More information==
[[Category:API]]
* [http://moodle.org/mod/forum/discuss.php?d=152391 General Developer Forum: Moodle 2 - how to set up breadcrumbs for a module page]

Latest revision as of 02:29, 24 June 2022

Important:

This content of this page has been updated and migrated to the new Moodle Developer Resources. The information contained on the page should no longer be seen up-to-date.

Why not view this page on the new site and help us to migrate more content to the new site!

Overview

The Navigation API allows for the manipulation of the navigation system used in Moodle.

What the navigation is

It's very important to understand what the navigation is exactly within Moodle. One of the goals for Moodle 2.0 was to standardise navigation throughout Moodle and try to bring order to the structure of a Moodle site. Navigation is available through the page object $PAGE, against which you set the heading for the page, the title, any JavaScript requirements, etc. The navigation structure uses the information $PAGE contains to generate a navigation structure for the site. The navigation or settings blocks are interpretations of the navigation structure Moodle creates.

This navigation structure is available through three variables:

$PAGE->navigation
This is the main navigation structure, it will contain items that will allow the user to browse to the other available pages.
$PAGE->settingsnav
This is the settings navigation structure contains items that will allow the user to edit settings.
$PAGE->navbar
The navbar is a special structure for page breadcrumbs.

A conceptual view of the information architecture that sits behind the navigation tree is here:

Information Architecture

This diagram represents the major entities and how they are related to each other. Examples are given of the type of functions available on each kind of entity.

What the navigation is not

The navigation is NOT the navigation block or the settings block! These two blocks were created to display the navigation structure. The navigation block looks at $PAGE->navigation, and the settings block looks at $PAGE->settingsnav. Both blocks interpret their data into an HTML structure and render it.

  1. The navigation is a back-end structure that is built behind the scenes and has no immediate method of display.
  2. The navigation and settings blocks display the back-end navigation structure but add nothing to it at all.
In the model-view-controller pattern, $PAGE->navigation, $PAGE->settingsnav, and $PAGE->navbar are the models, and the blocks are views.

The navbar is just the path to the active navigation or settings item. The navbar is not displayed by a block; instead it is added into the theme's layout files and displayed by the core renderer.

How the navigation works

The main navigation structure can be accessed through $PAGE->navigation. The navigation and settings are contextual in that they will relate to the page that the user is viewing. This is determined by other $PAGE object properties:

  • $PAGE->context is a Moodle context that immediately outlines the nature of the page the user is viewing.
  • $PAGE->course is the course the user is viewing. This is essential if the context is CONTEXT_COURSE or anything within it. However, it is also useful in other contexts such as CONTEXT_USER.
  • $PAGE->cm is the course module instance. This is essential if the context is CONTEXT_MODULE.
  • $PAGE->url is used to match the active navigation item.

Nearly every page sets $PAGE->url through a call to $PAGE->set_url however not many explicitly set the context, course, or cm. When you call require_login with a course or cm it automatically calls the following:

if ($cm) {
    $PAGE->set_cm($cm, $course); // sets up global $COURSE
} else {
    $PAGE->set_course($course);// sets up global $COURSE

A page will only be required to explicitly set a context, course, or cm under one of these conditions:

  1. require_login is NOT being called correctly
  2. The page is using CONTEXT_SYSTEM, CONTEXT_COURSECAT, or CONTEXT_USER (call $PAGE->set_context ).
  3. The page is using a course or cm but it is also using one of the above contexts (call $PAGE->set_course or $PAGE->set_cm ).

The navigation structure cannot be generated before the $PAGE object is configured. It is only generated when it is first used, either when something tries to access the structure or when code tries to add to it. The navigation is initialised in a specific order:

  1. Main navigation structure
  2. Settings navigation
  3. Navbar (does not need to be generated because of its simple contents and rendering)

Extending the navigation

Code extension

This method of extending is when the code arbitrarily extends the navigation during its execution. Extending the navigation through this means allows you to extend the navigation anywhere easily, however it will only be shown on pages where your extending code gets called (you should probably put it in a function within lib.php).

Navigation extensions that apply all the time (even when not on pages including your code) can be made by putting them in your plugin's settings.php file.

These examples are taken from the General Developer Forum: Moodle 2 - how to set up breadcrumbs for a module page. It has further information that is well worth reading.

Navigation

This is extending the main navigation structure.

$previewnode = $PAGE->navigation->add(get_string('preview'), new moodle_url('/a/link/if/you/want/one.php'), navigation_node::TYPE_CONTAINER);
$thingnode = $previewnode->add(get_string('name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
$thingnode->make_active();

The above lines of code adds a preview node to the bottom of the navigation and then adds a thingnode to the previewnode (adding a leaf to our tree). The final line of code makes the thingnode active so that the navbar finds it however if the URL you give it is the same as the url you set for the page it will automatically be marked active and you won't need this call.

Next extending the navigation for the course. For this you will need to know the course id and have called require_login($courseorid); so that the navigation is loaded for the course.

$coursenode = $PAGE->navigation->find($courseid, navigation_node::TYPE_COURSE);
$thingnode = $coursenode->add(get_string('Name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
$thingnode->make_active();

The new bit of code here really is the first line which simply finds the course node, to do this we give it the course id and the node type in this case TYPE_COURSE. What we are doing here is relying on the navigation to generate the navigation up to the course and then just adding to the course.

Note that Moodle loads plugins in alphabetical order. Therefore plugin_b can find a node added by plugin_a but not the other way around.

Settings navigation

Adding to the settings navigation is very similar to navigation

$settingnode = $PAGE->settingsnav->add(get_string('setting'), new moodle_url('/a/link/if/you/want/one.php'), navigation_node::TYPE_CONTAINER);
$thingnode = $settingnode->add(get_string('Name of thing'), new moodle_url('/a/link/if/you/want/one.php'));
$thingnode->make_active();

Add Settings folder to navigation

An example of a settings folder in a module can be see by navigating to Site administration / Plugins / Activity modules / Assignment.

assignmentmenu.png

An example of adding a navigation folder to a settings.php for a block with a link to the settings page and a external page is bellow.

<?php

defined('MOODLE_INTERNAL') || die;

// Create folder / submenu in block menu, modsettings for activity modules, localplugins for Local plugins. 
// The default folders are defined in admin/settings/plugins.php.
$ADMIN->add('blocksettings', new admin_category('blocksamplefolder',
        get_string('pluginname', 'mod_sample')));

// Create settings block.
$settings = new admin_settingpage($section, get_string('settings', 'block_sample'));
if ($ADMIN->fulltree) {
    $settings->add(new admin_setting_configcheckbox('block_sample_checkbox', get_string('checkbox', 'block_sample'),
        get_string('checkboxdescription', 'block_kronoshtml'), 0));
}

// This adds the settings link to the folder/submenu.
$ADMIN->add('blocksamplefolder', $settings);
// This adds a link to an external page.
$ADMIN->add('blocksamplefolder', new admin_externalpage('block_sample_page', get_string('externalpage', 'block_sample'),
        $CFG->wwwroot.'/blocks/sample/sample.php'));
// Prevent Moodle from adding settings block in standard location.
$settings = null;

Navbar

This is extending the settings navigation.

$PAGE->navbar->ignore_active();
$PAGE->navbar->add(get_string('preview'), new moodle_url('/a/link/if/you/want/one.php'));
$PAGE->navbar->add(get_string('name of thing'), new moodle_url('/a/link/if/you/want/one.php'));

The above code tells the navbar to ignore what ever the active page was and just use what you add, at which point we add two items as shown.

Plugin Callbacks

These are specific functions that the navigation looks for and calls if they exist for the plugin, presently only three plugin types can extend the navigation through these call-backs.

Ideally all entries in "Administration / Site administration" tree should be done via settings.php files but sometimes it may be easier to directly modify the navigation structure created from the admin settings tree (such as when adding links to external pages).

Module

Modules have two call-back methods, first to extend the navigation, and second to extend the settings. These call-backs get called when ever the user is viewing a page within the module and should only extend the navigation for the module.

function {modulename}_extend_navigation(${modulename}node, $course, $module, $cm)
function {modulename}_extend_settings_navigation($settings, ${modulename}node)

You may be required to add a node in a specified order within the menu navigation menus. To do this you need to examine the node object as given in the respective parameters in the functions above, then find the key of the childnode you wish to place the link before. For example, applying the code below will put a direct link to grade report.

function my_plugin_extend_settings_navigation($settingsnav, $context){
    if(($context->contextlevel === 50) &&
        has_capability('gradereport/grader:view', $context)){
        $id = $context->instanceid;
        $urltext = get_string('gradereportlink', 'myplugin');
        $url = new moodle_url($CFG->wwwroot . '/grade/report/grader/index.php', array('id'=>$id));
        $coursesettingsnode = $settingsnav->find('courseadmin', null);   // 'courseadmin' is the menu key
        $node = $coursesettingsnode->create($urltext, $url, navigation_node::NODETYPE_LEAF, null, 'gradebook',  new pix_icon('i/report', 'grades'));
        $coursesettingsnode->add_node($node,  'gradebooksetup'); //'gradebooksetup' is an where you put the link before
    }
...


Course Formats

Course formats are able to completely redefine the way in which navigation is generated for a course, as well as this they also have several methods to ensure the navigation is generated correctly.

Course Reports

By default reports don't add themselves or anything else to the navigation however there is a call-back that can be implemented to allow them to do so.

Local Plugins

Moodle 2.9 Local plugins have two call-back methods, first to extend the navigation, and second to extend the settings.

function local_{pluginname}_extend_navigation(global_navigation $nav)
function local_{pluginname}_extend_settings_navigation(settings_navigation $nav, context $context)

In versions before Moodle 2.9, the supported callbacks have _extends_ (not imperative mood) in their names. This was a consistency bug fixed in MDL-49643.

function local_{pluginname}_extends_navigation(global_navigation $nav)
function local_{pluginname}_extends_settings_navigation(settings_navigation $nav, context $context)

Course settings

Moodle 3.0

Any plugin implementing the following callback in lib.php can extend the course settings navigation. Prior to 3.0 only reports and admin tools could extend the course settings navigation.

function <component>_extend_navigation_course(navigation_node $parentnode, stdClass $course, context_course $context);

User settings

Moodle 3.0

Any plugin implementing the following callback in lib.php can extend the user settings navigation. Prior to 3.0 only admin tools could extend the user settings navigation.

function <component>_extend_navigation_user_settings(navigation_node $parentnode, stdClass $user, context_user $context, stdClass $course, context_course $coursecontext);

Category settings

Moodle 3.0

Any plugin implementing the following callback in lib.php can extend the category settings navigation.

function <component>_extend_navigation_category_settings(navigation_node $parentnode, context_coursecat $context);

Frontpage settings

Moodle 3.0

Any plugin implementing the following callback in lib.php can extend the frontpage settings navigation. Prior to 3.0 only admin tools could extend the frontpage settings navigation.

function <component>_extend_navigation_frontpage(navigation_node $parentnode, stdClass $course, context_course $context);

User profile

Moodle 3.1

Any plugin implementing the following callback in lib.php can extend the user profile navigation.

function <component>_extend_navigation_user(navigation_node $parentnode, stdClass $user, context_user $context, stdClass $course, context_course $coursecontext);

Boost

The navigation API is specifically about allowing the manipulation of nodes in an in-memory tree structure that is used as the basis of building navigation components in the page. The navigation and settings blocks are 2 examples of such components and the flat navigation and settings menus in the Boost theme are some more. The navigation component itself can decide to show all or only part of the navigation tree in order to not overwhelm the user viewing the page. Whether a node is actually displayed depends on where in the tree the node was added, what is the current page in the navigation tree, and the specific navigation components that are used to provide navigation functionality in the current theme.

If you are testing in the Boost theme - all nodes that are added to settings tree are displayed in the course/activity settings menu which is shown as a cog on the front page of the course / activity. Only the most important pre-selected nodes are displayed in the flat-navigation drawer in order to provide consistency and avoid overwhelming the user with too many links. It is possible, but definitely NOT RECOMMENDED for plugins to add nodes to the flat navigation (see [#FAQ.27s_and_troubleshooting] for more information).

FAQ's and troubleshooting

Q. My page is on the navigation but it doesn't find it?

The first thing to do here is check the URL you are setting for the page. It should match the URL your page has within the navigation. If it doesn't you have two options, first change your $PAGE->set_url call, or second override the URL the navigation is using to find the active node as shown below:

navigation_node::override_active_url(new moodle_url('/your/url/here.php', array('param'=>'value')));

Q. How do I add a node to the "flat" navigation in the Boost theme for Moodle 3.2?

After creating a node and adding it to the navigation tree - you can set the property 'showinflatnavigation' to true in order for this node to be displayed in the flat navigation.

$node = navigation_node::create(...);
$node->showinflatnavigation = true;
$navigation->add_node($node);

This is highly discouraged because the number of nodes in this flat navigation has been deliberately restricted to a very small number of the most important links that are applicable to all user roles. Adding more links to this menu will make it harder to use, inconsistent for different users and inconsistent for different sites. Consider carefully if you really need to fill an additional 230x44 pixels of every single page in Moodle for every single user with a link to your thing. There are many other places to include links to your thing and most are automatically built from the navigation tree without forcing nodes to display in the flat navigation. E.g. settings menu of a course, profile page, preferences page, reports etc.

See also