====== 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 "" . get_section_name($COURSE, $section) . "
";
}
===== 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 "" . get_section_name($COURSE, $section->section) . "
";
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
";
}
}
(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(" Hello!");
}
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.
==== How Modules Are Displayed ====
* The course format fetches all the cm class instances for the modules to be displayed in the page
* During the export of context for the template, it calls the add_alternative_content_data: https://bitbucket.org/solindevs/icm/src/7a5f65b5d9b5e15337e0a5b54e7285dd682b0935/course/format/classes/output/local/content/cm.php#lines-117
* This in turns calls the cm_info get_formatted_content function from the cm_info: https://bitbucket.org/solindevs/icm/src/7a5f65b5d9b5e15337e0a5b54e7285dd682b0935/course/format/classes/output/local/content/cm.php#lines-183:185
Here three distinct steps are performed:
* Module information cache is either built or retrieved, but information for each module is generated by their own libs: https://bitbucket.org/solindevs/icm/src/7a5f65b5d9b5e15337e0a5b54e7285dd682b0935/lib/modinfolib.php#lines-1672:1678
* The full content of the icmupcomingcalendar is rendered from its lib.php file, in the icmupcomingcalendar_get_coursemodule_info function: https://bitbucket.org/solindevs/icm/src/7a5f65b5d9b5e15337e0a5b54e7285dd682b0935/mod/icmupcomingcalendar/lib.php#lines-141:160
* Next the LMS filters are loaded and cached: https://bitbucket.org/solindevs/icm/src/7a5f65b5d9b5e15337e0a5b54e7285dd682b0935/lib/modinfolib.php#lines-1683
* Lastly, before returning the module’s content, it applies the format_text function to provide support for LMS installed and security filters.