====== 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.