Course Construction

A Moodle course consists of sections (topics) and modules (activities or resources).

Information about the course structure is stored in a cache (which is read into memory from mdl_course.modinfo). This information can be accessed as an object of class course_modinfo by calling get_fast_modinfo($course_object).

If you need all sections in a course, call get_section_info_all on the course_info object, which will return an array of section_info objects. Example:

$course_modinfo = get_fast_modinfo($COURSE); 
$sections = $course_modinfo->get_section_info_all();

The useful properties of section_info are all private, so normally there wouldn't be any way to access them. However, section_info implements php's IteratorAggregate interface. So, it has a getIterator function which in this case allows you to loop over all properties, after all. It looks as though this was done to make it easier to convert the section_info object into a 'clean' array: only properties prefixed with a '_' are exposed through the iterator.

You can also access all private properties by using the same variable name without the prefix, because section_info maps these names using the 'magic' __get($name) function. So, $section→name actually retrieves $section→_name

Fetch Only Sections Which Contain Modules

Instead of calling course_modinfo#get_section_info_all, you can also call course_modinfo#get_sections. This method retrieves just the sections which actually contain content. The return value is different though:

exit(print_object($course_modinfo->get_sections())); // yields:

Array
(
    [0] => Array
        (
            [0] => 188
            [1] => 192
            [2] => 244
        )

    [1] => Array
        (
            [0] => 190
        )

    [2] => Array
        (
            [0] => 191
        )

    [3] => Array
        (
            [0] => 193
        )

)

Whereas calling course_modinf$course_modinfo→get_section_info_all()o#get_section_info_all returns:

exit(print_object($course_modinfo->get_section_info_all())); // yields:
Array
(
    [0] => section_info Object
        (
            [_id:section_info:private] => 246
            [_course:section_info:private] => 32
            [_section:section_info:private] => 0
            [_name:section_info:private] => 
            [_visible:section_info:private] => 1
            [_summary:section_info:private] => 
            [_summaryformat:section_info:private] => 1
            [_showavailability:section_info:private] => 0
            [_availablefrom:section_info:private] => 0
            [_availableuntil:section_info:private] => 0
            [_groupingid:section_info:private] => 0
            [_conditionscompletion:section_info:private] => Array
                (
                )

            [_conditionsgrade:section_info:private] => Array
                (
                )

            [_conditionsfield:section_info:private] => Array
                (
                )

            [_available:section_info:private] => 1
            [_availableinfo:section_info:private] => 
            [_uservisible:section_info:private] => 1
            [cachedformatoptions:section_info:private] => Array
                (
                )

            [_sequence] => 188,192,244
        )

    [1] => section_info Object
        (
            [_id:section_info:private] => 247
            [_course:section_info:private] => 32
            [_section:section_info:private] => 1
            [_name:section_info:private] => My Section Name for Topic 1
// And so on...

Please keep in mind that even though the properties are all private, you can still access them if you leave out the _ prefix (see previous section).

As an example, if you've got a specific section object, say $current_section, for which you want to retrieve all course_module ids, then you can do something like this:

$sections = $course_modinfo->get_section_info_all(); // array of course_module ids for each section
$current_course_module_ids = $sections[$current_section->section];

So, even though _section is actually a private property, you can still access it by omitting the underscore.

Current Section Number

To get the current section number:

$section_number = ($n = optional_param('section', 0, PARAM_INT)) ? $n : $PAGE->cm->sectionnum;

Printing Section Names

First, retrieve all (relevant) sections, then call get_section_name($course_or_id, $section):

$modinfo = get_fast_modinfo($COURSE);
$sections = $modinfo->get_sections();
foreach($sections as $section) {
    echo "<h2>" . get_section_name($COURSE, $section) . "</h2>";
}

Retrieve All Activities

There is a very straightforward function call to make if you want to retrieve all activities in a course:

$activities = get_array_of_activities($COURSE->id);

The course/lib.php function print_section does not use this function however. Here, something like the following is done:

$sections = $course_modinfo->get_section_info_all();
foreach ($sections as $section) {
    if (empty($course_modinfo->sections[$section->section])) continue;
    // approximation of the code used in ''print_section'':    
    echo "<h2>" . get_section_name($COURSE, $section->section) . "</h2>";
    foreach ($course_modinfo->sections[$section->section] as $modnumber) {
        $mod = $course_modinfo->cms[$modnumber];
        if (!$mod->uservisible) continue;

        list($content, $instancename) = get_print_section_cm_text($course_modinfo->cms[$modnumber], $COURSE);
        echo "$instancename<br/>";
    }
}

(This is not the actual code, but a compact, working version of it. Looping over the sections actually takes place elsewhere: for each section, print_section is called.)

Get A Specific Section

To get the first section:

        $first_section = $course_modinfo->get_section_info(0);

Get A Specific Activity

Here's how you retrieve the first forum of a course:

    static function get_first_forum($course) {
        $course_modinfo = get_fast_modinfo($course); 

        $all_forums = $course_modinfo->get_instances_of('forum');
        return array_shift($all_forums);
    } // function get_first_forum 

Print Course Module / Instance Specific Info in the Topics Overview

In your mod's library, add a function:

function mymod_cm_info_view(cm_info $cm) {
    global $CFG;

    $cm->set_after_link(" <span><b>Hello!</b></span>");
}

This will print “Hello!” after each instance of your module. Please keep in mind that this string will not be cached!

The forum module uses this function to print the number of unread messages in a specific forum.

UPDATE: it looks as though this function is now called [mymod]_get_coursemodule_info. Example:

function multilabel_get_coursemodule_info($coursemodule) {
    // Simplistic example
    $info = new cached_cm_info();
    $info->name  = 'Multilabel instance';
    $info->content = 'Multilabel content';
    return $info;
}

To output dynamic (i.e. uncached) data:

function multilabel_cm_info_dynamic(cm_info $cm) {
    $cm->set_content("Time: " . date('Y-m-d  H:i:s'));    
}

Add Course Settings

There is no clean way to add a field to the course settings form, e.g. through a local plugin. Instead, you have to hack the /course/edit_form.php file. Here's an example:

/* Added by Solin, 2015 */
$mform->addElement('date_selector', 'enddate', get_string('enddate', 'report_trainingreport'));
$mform->addHelpButton('enddate', 'enddate', 'report_trainingreport');
/* End of addition by Solin, 2015 */

To add the field to the mdl_course table, include this code in /your_plugin/db/install.php:

/**
 * Add column to course database table
 * @return bool
 */
function xmldb_local_your_plugin_install() {
    global $DB;
    $dbman = $DB->get_manager();

    // do the install
    $table = new xmldb_table('course');

    $field = new xmldb_field('enddate', XMLDB_TYPE_INTEGER, '11', null, null, null, null);
    if (!$dbman->field_exists($table, $field)) {
        $dbman->add_field($table, $field);
    }

} // function xmldb_local_your_plugin_install 

Course Settings in Custom Course Format

You can create your own course format by adding a new directory, e.g. course/format/planner. Define a class for your format in course/format/planner/lib.php and use the course_format_options to add course settings. Here's an (incomplete) example.

class format_planner extends format_base {
    public function course_format_options($foreditform = false) {
        global $DB, $CFG;
        static $courseformatoptions = false;
        
        // stuff left out...
        
            $courseformatoptionsedit = array(
     
                'replan' => array(
                    'label' => new lang_string('replan', 'format_planner'),
                    'element_type' => 'select',
                    'element_attributes' => array(
                        array(
                            1 => new lang_string('yes_to_all', 'format_planner'),
                            2 => new lang_string('yes_by_manager', 'format_planner'),
                            3 => new lang_string('yes_by_role', 'format_planner'),
                            0 => new lang_string('no'),
                        )
                    ),
                    'help' => 'replan',
                    'help_component' => 'format_planner',
                ),
                'replan_role' => array(
                    'label' => new lang_string('replan_role', 'format_planner'),
                    'element_type' => 'select',
                    'element_attributes' => array(
                                                  replan_selector()
                    ),
                    'help' => 'replan_role',
                ),

                'slow' => array(
                    'label' => new lang_string('slow', 'format_planner'),
                    'element_type' => 'select',
                    'element_attributes' => array(
                        array(
                            0 => new lang_string('no'),
                            1 => new lang_string('yes'),
                        )
                    ),
                    'help' => 'slow',
                    'help_component' => 'format_planner',
                )

            );
    }             

}

Now, apparently due to a bug in Moodle (at least version 2.4 - 2.6), you cannot use the type checkbox in your $courseformatoptionsedit array. Moodle will display the checkbox in the course settings form, but it does not add a hidden input field in the background. In other words: once checked, the checkbox can never be unchecked.

To circumvent this problem, simply use a select type with 'yes' and 'no' (see the 'slow' example above).

Custom Section Settings

To add custom settings to the section form of your favorite course format, add this code to the file course/format/[course_format_name]/lib.php:

    function section_format_options($foreditform = false) {
        return array(
            'drip_opening_day' => array(
                'default' => 0,
                'type' => PARAM_INT,
                'label' => new lang_string('opening_day', 'format_drip'),
                'help' => 'opening_day',
                'element_attributes' => array(" size='3' ")
            )
        );
    }

This example adds text box which stores its content in a configuration field called drip_opening_day.

Reading Custom Section Settings

To use the custom section settings in your renderer, you need access to the format object:

$course = $DB->get_record('course', array('id' => $course_id));
$course_format_object = course_get_format($course);

Now, using the course format object, we have access to the array containing the section format options:

$section_format_options = $course_format_object->get_format_options($section);
$opening_day = $section_format_options['drip_opening_day'];

Output Column Headers in Topic Course Format or Planner Format

To adapt the topics overview in the topic course format or derivatives such as the Planner format, look in files such as course/format/planner/lib_planner_section.php for section customizations.

Specifically, in the Planner format, each section gets a section header through the function section_header, in /course/format/planner/renderer.php.

Who Prints The Activities in The Topic Course Format?

For a “single section page”, this is the 'call stack' for printing the activities:

format_section_renderer_base#print_single_section_page()
-> core_course_renderer#course_section_cm_list()
-> core_course_renderer#course_section_cm_list_item()
-> core_course_renderer#course_section_cm()

The core_course_renderer is actually an instance, created as follows:

$this->courserenderer = $this->page->get_renderer('core', 'course');

In More Detail

/course/format/topics/renderer.php contains the class format_section_renderer_base, which has a method print_single_section_page. This method has access to an instance of core_course_renderer, through $this→courserenderer.

$this→courserenderer is instantiated in lib/pagelib.php, by moodle_page#get_renderer.

$this→course_section_cm_list() is called to output all activities in this section.

course/renderer.php contains course_section_cm_list, which loops over all activities in the section. For each activity, it calls: $this→course_section_cm_list_item. This function then calls $this→course_section_cm, which finally prints the actual activity in the section overview.

Extending / Overwriting The core_course_renderer Class

In your custom course format, create a file containing a new class, e.g.:

// Filename: /course/format/tiles/class.tiles_course_renderer.php
require_once($CFG->dirroot.'/course/renderer.php');
class tiles_course_renderer extends core_course_renderer  {

    public function course_section_cm($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = array()) {
    ...

Instantiate Your Own Course Renderer

Instead of using the factory method get_renderer, we'll just instantiate this class ourselves:

// Filename: /course/format/tiles/renderer.php
class format_tiles_renderer extends format_section_renderer_base {
    public function [2underscores]construct(moodle_page $page, $target) {
        parent::[2underscores]construct($page, $target);  
      
        $this->courserenderer = new tiles_course_renderer($page, $target);
        ...

(Apologies for the [2underscores], but the wiki software wreaks havoc on the formatting otherwise)

That's it, you are now ready to overwrite any core_course_renderer method you wish, in your own “course renderer” class.


Personal Tools