Adding courses and categories to the custom menu

Jump to: navigation, search
Warning: This page is no longer in use. The information contained on the page should NOT be seen as relevant or reliable.


Hello this is going to be a very brief tutorial on how to add a courses and categories tree to the custom menu.
The reason for writing this tutorial is simply that this seems to be a request that is being made over and over again.

Before we get started

Please please please make sure you are familiar with the other Themes tutorials before attempting this tutorial.

In particular I would recommend being familiar with the following documents and tutorials as I'm going to move through this tutorial at a fast pace.

Providing you are familiar with the above you should manage just fine with this tutorial and if you ever get stuck don't forget to ask in the theme's forums, there is always someone around who can help.

Two words of warning

First Just a quick warning here, this tutorial adds all categories and courses to the navigation, on a site with a large number of categories and/or courses this will be a performance hit. I would strongly suggest that if you are looking to implement something such as this on a large site that you talk to your system admin and/or resident developer about setting up a shared cache and caching the result of the get_course_category_tree method... it is a technical task but doing so will improve the performance of this extension on a large site.

Second If you have categories nested 2 or more deep then there is a chance that you will run into display problems with the custom menu on some themes... not too much you can do about it but if you encounter it with a core theme and you fix it perhaps you would be kind enough to create an issue in our bug tracker and share the fix. That'd being said this is only a hunch I have all of the theme's may be fine :)

Laying the ground work

Alright I don't want this tutorial to drag out so get ready. For this tutorial I am just going to extend the standard theme, I'm going to make absolutely no changes to the design and layout of the theme or the custom menu I am ONLY going to add a list of courses and categories to it.

So to begin with within your Moodle themes directory create a new directory coursecategorymenu in which I want you to create three files, the first config.php which is obviously required, the second renderers.php as we are going to achieve this by overriding a renderer, and the third the English language file for this theme.

You should end up with:

  • moodle/theme/coursecategorymenu
  • moodle/theme/coursecategorymenu/config.php
  • moodle/theme/coursecategorymenu/renderers.php
  • moodle/theme/coursecategorymenu/lang/en/theme_coursecategorymenu.php

config.php

Obviously because all I want to base this theme off the standard theme and only override a renderer the config.php file is going to be VERY basic, so much so that I'm not going to go into details about it, as you've read the recommended tutorials you will already be completely familiar with everything it is doing.

<?php
 
$THEME->name = 'coursecategorymenu';
$THEME->parents = array('standard', 'base');
$THEME->rendererfactory = 'theme_overridden_renderer_factory';

renderers.php

This is of course where the magic is going to happen.

In my case I want to add a branch to the end of the custom menu titled Courses and then I want to add the category and course structure to that branch.

So like the extending the custom menu tutorial I will be overriding the core renderer and I will also be overriding the render_custom_menu_method.

So the code for this:

class theme_coursecategorymenu_core_renderer extends core_renderer {
 
    protected function render_custom_menu(custom_menu $menu) {
        // Our code will go here shortly
    }
 
}

You'll notice that is identical to the starting code for the extending custom menu tutorial.

The difference comes in the code that we are going to populate it with:

require_once($CFG->dirroot.'/course/lib.php');
 
$branch = $menu->add(get_string('courses', 'theme_coursecategorymenu'), null, null, 10000);
 
$categorytree = get_course_category_tree();
foreach ($categorytree as $category) {
    $this->add_category_to_custommenu($branch, $category);
}
 
return parent::render_custom_menu($menu);

So lets work through this code line by line:

require_once($CFG->dirroot.'/course/lib.php');

This we need to do because we are going to use part of the course API, in particular a function called get_course_category_tree() that we'll look at shortly. The function we want to use exists within the course lib file and in order to be sure its always available we need to include this file.

$branch = $menu->add(get_string('courses', 'theme_coursecategorymenu'), null, null, 10000);

This line adds the Courses branch to the custom menu into which we will add the category and course structure.

$categorytree = get_course_category_tree();

This line is the main one to note, here we are calling a Moodle function that will return us a category and course tree, because all courses need to be within a category the initial return is an array of categories, each category in the array will have the properties of the category as well as two additional properties, the first property categories is an array containing all of this categories sub categories, and the second property courses is also an array containing all of the courses within this category.

$categorytree = get_course_category_tree();
foreach ($categorytree as $category) {
    $this->add_category_to_custommenu($branch, $category);
}

Now these lines of code deal with the initial array of categories returned by the get_course_category_tree function.

In this case we iterate of the initial categories and for each one we call a method of the renderer add_category_to_custommenu now those of you who are clued in will realise that this method doesn't exist yet... and you are correct - we need to write this method which is what we will look at next.
As a heads up the reason that we need to write another method is because we need a method that we can call recursivily as there are potentially infinite category branches all contains sub categories and courses.

return parent::render_custom_menu($menu);

The final line of code simply calls the original render_custom_menu method now that we have extended the custom menu as we want.

Now that we have looked at the render_custom_menu lets write the function we just talked about add_category_to_custommenu.
This function as discussed above is special in that we intend to call it recursively, that means we want to call it once for EVERY category that exists in the tree. We will need to give it the categories parent menu item as well as the category object we want added.

Lets look at the code for this method:

protected function add_category_to_custommenu(custom_menu_item $parent, stdClass $category) {
    $branch = $parent->add($category->name, new moodle_url('/course/category.php', array('id' =>  $category->id)));
    if (!empty($category->categories)) {
        foreach ($category->categories as $subcategory) {
            $this->add_category_to_custommenu($branch, $subcategory);
        }
    }
    if (!empty($category->courses)) {
        foreach ($category->courses as $course) {
            $branch->add($course->shortname, new moodle_url('/course/view.php', array('id' => $course->id)), $course->fullname);
        }
    }
}

Ok so that doesn't look too bad, as you've already read the extending custom menu tutorial you will be able to spot some of the things it is doing, none the less lets walk through the code.

protected function add_category_to_custommenu(custom_menu_item $parent, stdClass $category) {
    .....
}

First up the function definition, this function is going to be private because only we need it and it is going to take two arguments, the first $parent has to be a custom_menu_item, the second $category has to be the category object.

The objective of this method is to add a category node to the custom menu, in regards to our arguments we want to add $category as a child of $parent.

$branch = $parent->add($category->name, new moodle_url('/course/category.php', array('id' =>  $category->id)));

This line looks very familiar, here we are adding a node for the category to the $parent, we are also collecting the newly added node as $branch.

if (!empty($category->categories)) {
    foreach ($category->categories as $subcategory) {
        $this->add_category_to_custommenu($branch, $subcategory);
    }
}

Now this code is responsible for add the sub categories of this category to the menu as well, first we need to check that the categories property isn't empty (if it is there is nothing to add). Assuming there are sub categories we need go through each of them and call this method on them so that they get added to the custom menu as well.
This is called recursion.

if (!empty($category->courses)) {
    foreach ($category->courses as $course) {
        $branch->add($course->shortname, new moodle_url('/course/view.php', array('id' => $course->id)), $course->fullname);
    }
}

This code is very similar to the above code except that we are looking at the courses within this category.
Also note that this bit of code doesn't call add_category_to_custommenu recursivily, it doesn't need to as courses are the last bit we want to display on the menu within the category.

And that is it, time to tidy up and we're done.

Finishing up

All of the code is written now there is only last thing to do in order to tidy up however and that is to add the courses string that we used earlier... did you spot it?

This is of course very easy, open up moodle/theme/coursecategorymenu/lang/en/theme_coursecategorymenu.php and add the following:

<?php
 
$string['courses'] = 'Courses';

And we're done. If you now browse to your site and change to the new theme you should see the Courses branch at the end of your custom menu that contains all of the categories, sub categories and courses for your site.

Full source

config.php

<?php
 
$THEME->name = 'coursecategorymenu';
$THEME->parents = array('standard', 'base');
$THEME->rendererfactory = 'theme_overridden_renderer_factory';

renderers.php

<?php
 
class theme_coursecategorymenu_core_renderer extends core_renderer {
 
    protected function render_custom_menu(custom_menu $menu) {
        global $CFG;
 
        require_once($CFG->dirroot.'/course/lib.php');
 
        $branch = $menu->add(get_string('courses', 'theme_coursecategorymenu'), null, null, 10000);
 
        $categorytree = get_course_category_tree();
        foreach ($categorytree as $category) {
            $this->add_category_to_custommenu($branch, $category);
        }
 
        return parent::render_custom_menu($menu);
    }
 
    protected function add_category_to_custommenu(custom_menu_item $parent, stdClass $category) {
        $branch = $parent->add($category->name, new moodle_url('/course/category.php', array('id' =>  $category->id)));
        if (!empty($category->categories)) {
            foreach ($category->categories as $subcategory) {
                $this->add_category_to_custommenu($branch, $subcategory);
            }
        }
        if (!empty($category->courses)) {
            foreach ($category->courses as $course) {
                $branch->add($course->shortname, new moodle_url('/course/view.php', array('id' => $course->id)), $course->fullname);
            }
        }
    }
 
}

lang/en/theme_coursecategorymenu.php

<?php
 
$string['courses'] = 'Courses';

How to add "My Courses" to the Custom Menu Bar for Moodle 2.4

This adds a nice drop down for logged in users so they can easily see their relevant courses. Note the code below has not been tested on Moodle 2.0-2.3, but you can find working code and instructions on how to add "My Courses" to the custom menu bar for those versions at: Themes_2.0_extending_the_custom_menu

Below is the Moodle 2.4 custom menu code to add to theme/themename/renderers.php

// Add the code below to Moodle 2.4   /theme/themname/renderers.php
<?php>
 
class theme_themename_core_renderer extends core_renderer {
 
    protected function render_custom_menu(custom_menu $menu) {
 
        global $CFG;
        require_once($CFG->dirroot.'/course/lib.php');
 
	// Moodle 2.4 doesn't appear to support $mycourses = $this->page->navigation->get('mycourses'); so 
 
	if (isloggedin() && !isguestuser() && $mycourses = enrol_get_my_courses(NULL, 'visible DESC, fullname ASC')) {  //which does work
 
            $branchlabel = get_string('mycourses') ;
            $branchurl   = new moodle_url('/course/index.php');
            $branchtitle = $branchlabel;
            $branchsort  = 8000 ; // lower numbers = higher priority e.g. move this item to the left on the Custom Menu	
            $branch = $menu->add($branchlabel, $branchurl, $branchtitle, $branchsort);
 
            foreach ($mycourses as $mycourse) {
                $branch->add($mycourse->shortname, new moodle_url('/course/view.php', array('id' => $mycourse->id)), $mycourse->fullname);
            }
        }
    }
 
//Note to integrate this into the code for adding course and categories at the top of this document page, 
//remove this class definition (you only need it once, and change the function name here to:
//
//     protected function render_mycourses_custom_menu(custom_menu_item $menu, $position) {
//
//then add a line in the original render_custom_menu function towards the end of it and invoke this function by
//
//     $this->render_mycourses_custom_menu($menu, 12000) ;  
//
//
//Finally, don't forget to add the language definition for mycourses if that is not already set.  Bon chance!!

Please note, in the comments I did also highlight the bit that doesn't work in Moodle 2.4.

See also