Merge branch 'wip-MDL-36048-master' of git://github.com/marinaglancy/moodle
[moodle.git] / course / format / lib.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Base class for course format plugins
19  *
20  * @package    core_course
21  * @copyright  2012 Marina Glancy
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die;
27 /**
28  * Returns an instance of format class (extending format_base) for given course
29  *
30  * @param int|stdClass $courseorid either course id or
31  *     an object that has the property 'format' and may contain property 'id'
32  * @return format_base
33  */
34 function course_get_format($courseorid) {
35     return format_base::instance($courseorid);
36 }
38 /**
39  * Base class for course formats
40  *
41  * Each course format must declare class
42  * class format_FORMATNAME extends format_base {}
43  * in file lib.php
44  *
45  * For each course just one instance of this class is created and it will always be returned by
46  * course_get_format($courseorid). Format may store it's specific course-dependent options in
47  * variables of this class.
48  *
49  * In rare cases instance of child class may be created just for format without course id
50  * i.e. to check if format supports AJAX.
51  *
52  * Also course formats may extend class section_info and overwrite
53  * format_base::build_section_cache() to return more information about sections.
54  *
55  * If you are upgrading from Moodle 2.3 start with copying the class format_legacy and renaming
56  * it to format_FORMATNAME, then move the code from your callback functions into
57  * appropriate functions of the class.
58  *
59  * @package    core_course
60  * @copyright  2012 Marina Glancy
61  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
62  */
63 abstract class format_base {
64     /** @var int Id of the course in this instance (maybe 0) */
65     protected $courseid;
66     /** @var string format used for this course. Please note that it can be different from
67      * course.format field if course referes to non-existing of disabled format */
68     protected $format;
69     /** @var stdClass data for course object, please use {@link format_base::get_course()} */
70     protected $course = false;
71     /** @var array caches format options, please use {@link format_base::get_format_options()} */
72     protected $formatoptions = array();
73     /** @var array cached instances */
74     private static $instances = array();
76     /**
77      * Creates a new instance of class
78      *
79      * Please use {@link course_get_format($courseorid)} to get an instance of the format class
80      *
81      * @param string $format
82      * @param int $courseid
83      * @return format_base
84      */
85     protected function __construct($format, $courseid) {
86         $this->format = $format;
87         $this->courseid = $courseid;
88     }
90     /**
91      * Validates that course format exists and enabled and returns either itself or default format
92      *
93      * @param string $format
94      * @return string
95      */
96     protected static final function get_format_or_default($format) {
97         if ($format === 'site') {
98             return $format;
99         }
100         $plugins = get_sorted_course_formats();
101         if (in_array($format, $plugins)) {
102             return $format;
103         }
104         // Else return default format
105         $defaultformat = get_config('moodlecourse', 'format');
106         if (!in_array($defaultformat, $plugins)) {
107             // when default format is not set correctly, use the first available format
108             $defaultformat = reset($plugins);
109         }
110         static $warningprinted = array();
111         if (empty($warningprinted[$format])) {
112             debugging('Format plugin format_'.$format.' is not found. Using default format_'.$defaultformat, DEBUG_DEVELOPER);
113             $warningprinted[$format] = true;
114         }
115         return $defaultformat;
116     }
118     /**
119      * Get class name for the format
120      *
121      * If course format xxx does not declare class format_xxx, format_legacy will be returned.
122      * This function also includes lib.php file from corresponding format plugin
123      *
124      * @param string $format
125      * @return string
126      */
127     protected static final function get_class_name($format) {
128         global $CFG;
129         static $classnames = array('site' => 'format_site');
130         if (!isset($classnames[$format])) {
131             $plugins = get_plugin_list('format');
132             $usedformat = self::get_format_or_default($format);
133             if (file_exists($plugins[$usedformat].'/lib.php')) {
134                 require_once($plugins[$usedformat].'/lib.php');
135             }
136             $classnames[$format] = 'format_'. $usedformat;
137             if (!class_exists($classnames[$format])) {
138                 require_once($CFG->dirroot.'/course/format/formatlegacy.php');
139                 $classnames[$format] = 'format_legacy';
140             }
141         }
142         return $classnames[$format];
143     }
145     /**
146      * Returns an instance of the class
147      *
148      * @todo MDL-35727 use MUC for caching of instances, limit the number of cached instances
149      *
150      * @param int|stdClass $courseorid either course id or
151      *     an object that has the property 'format' and may contain property 'id'
152      * @return format_base
153      */
154     public static final function instance($courseorid) {
155         global $DB;
156         if (!is_object($courseorid)) {
157             $courseid = (int)$courseorid;
158             if ($courseid && isset(self::$instances[$courseid]) && count(self::$instances[$courseid]) == 1) {
159                 $formats = array_keys(self::$instances[$courseid]);
160                 $format = reset($formats);
161             } else {
162                 $format = $DB->get_field('course', 'format', array('id' => $courseid), MUST_EXIST);
163             }
164         } else {
165             $format = $courseorid->format;
166             if (isset($courseorid->id)) {
167                 $courseid = clean_param($courseorid->id, PARAM_INT);
168             } else {
169                 $courseid = 0;
170             }
171         }
172         // validate that format exists and enabled, use default otherwise
173         $format = self::get_format_or_default($format);
174         if (!isset(self::$instances[$courseid][$format])) {
175             $classname = self::get_class_name($format);
176             self::$instances[$courseid][$format] = new $classname($format, $courseid);
177         }
178         return self::$instances[$courseid][$format];
179     }
181     /**
182      * Resets cache for the course (or all caches)
183      * To be called from {@link rebuild_course_cache()}
184      *
185      * @param int $courseid
186      */
187     public static final function reset_course_cache($courseid = 0) {
188         if ($courseid) {
189             if (isset(self::$instances[$courseid])) {
190                 foreach (self::$instances[$courseid] as $format => $object) {
191                     // in case somebody keeps the reference to course format object
192                     self::$instances[$courseid][$format]->course = false;
193                     self::$instances[$courseid][$format]->formatoptions = array();
194                 }
195                 unset(self::$instances[$courseid]);
196             }
197         } else {
198             self::$instances = array();
199         }
200     }
202     /**
203      * Returns the format name used by this course
204      *
205      * @return string
206      */
207     public final function get_format() {
208         return $this->format;
209     }
211     /**
212      * Returns id of the course (0 if course is not specified)
213      *
214      * @return int
215      */
216     public final function get_courseid() {
217         return $this->courseid;
218     }
220     /**
221      * Returns a record from course database table plus additional fields
222      * that course format defines
223      *
224      * @return stdClass
225      */
226     public function get_course() {
227         global $DB;
228         if (!$this->courseid) {
229             return null;
230         }
231         if ($this->course === false) {
232             $this->course = $DB->get_record('course', array('id' => $this->courseid));
233             $options = $this->get_format_options();
234             foreach ($options as $optionname => $optionvalue) {
235                 if (!isset($this->course->$optionname)) {
236                     $this->course->$optionname = $optionvalue;
237                 } else {
238                     debugging('The option name '.$optionname.' in course format '.$this->format.
239                         ' is invalid because the field with the same name exists in {course} table',
240                         DEBUG_DEVELOPER);
241                 }
242             }
243         }
244         return $this->course;
245     }
247     /**
248      * Returns true if this course format uses sections
249      *
250      * This function may be called without specifying the course id
251      * i.e. in {@link course_format_uses_sections()}
252      *
253      * Developers, note that if course format does use sections there should be defined a language
254      * string with the name 'sectionname' defining what the section relates to in the format, i.e.
255      * $string['sectionname'] = 'Topic';
256      * or
257      * $string['sectionname'] = 'Week';
258      *
259      * @return bool
260      */
261     public function uses_sections() {
262         return false;
263     }
265     /**
266      * Returns a list of sections used in the course
267      *
268      * This is a shortcut to get_fast_modinfo()->get_section_info_all()
269      * @see get_fast_modinfo()
270      * @see course_modinfo::get_section_info_all()
271      *
272      * @return array of section_info objects
273      */
274     public final function get_sections() {
275         if ($course = $this->get_course()) {
276             $modinfo = get_fast_modinfo($course);
277             return $modinfo->get_section_info_all();
278         }
279         return array();
280     }
282     /**
283      * Returns information about section used in course
284      *
285      * @param int|stdClass $section either section number (field course_section.section) or row from course_section table
286      * @param int $strictness
287      * @return section_info
288      */
289     public final function get_section($section, $strictness = IGNORE_MISSING) {
290         if (is_object($section)) {
291             $sectionnum = $section->section;
292         } else {
293             $sectionnum = $section;
294         }
295         $sections = $this->get_sections();
296         if (array_key_exists($sectionnum, $sections)) {
297             return $sections[$sectionnum];
298         }
299         if ($strictness == MUST_EXIST) {
300             throw new moodle_exception('sectionnotexist');
301         }
302         return null;
303     }
305     /**
306      * Returns the display name of the given section that the course prefers.
307      *
308      * @param int|stdClass $section Section object from database or just field course_sections.section
309      * @return Display name that the course format prefers, e.g. "Topic 2"
310      */
311     public function get_section_name($section) {
312         if (is_object($section)) {
313             $sectionnum = $section->section;
314         } else {
315             $sectionnum = $section;
316         }
317         return get_string('sectionname', 'format_'.$this->format) . ' ' . $sectionnum;
318     }
320     /**
321      * Returns the information about the ajax support in the given source format
322      *
323      * The returned object's property (boolean)capable indicates that
324      * the course format supports Moodle course ajax features.
325      * The property (array)testedbrowsers can be used as a parameter for {@link ajaxenabled()}.
326      *
327      * @return stdClass
328      */
329     public function supports_ajax() {
330         // no support by default
331         $ajaxsupport = new stdClass();
332         $ajaxsupport->capable = false;
333         $ajaxsupport->testedbrowsers = array();
334         return $ajaxsupport;
335     }
337     /**
338      * Custom action after section has been moved in AJAX mode
339      *
340      * Used in course/rest.php
341      *
342      * @return array This will be passed in ajax respose
343      */
344     public function ajax_section_move() {
345         return null;
346     }
348     /**
349      * The URL to use for the specified course (with section)
350      *
351      * Please note that course view page /course/view.php?id=COURSEID is hardcoded in many
352      * places in core and contributed modules. If course format wants to change the location
353      * of the view script, it is not enough to change just this function. Do not forget
354      * to add proper redirection.
355      *
356      * @param int|stdClass $section Section object from database or just field course_sections.section
357      *     if null the course view page is returned
358      * @param array $options options for view URL. At the moment core uses:
359      *     'navigation' (bool) if true and section has no separate page, the function returns null
360      *     'sr' (int) used by multipage formats to specify to which section to return
361      * @return null|moodle_url
362      */
363     public function get_view_url($section, $options = array()) {
364         $course = $this->get_course();
365         $url = new moodle_url('/course/view.php', array('id' => $course->id));
367         if (array_key_exists('sr', $options)) {
368             $sectionno = $options['sr'];
369         } else if (is_object($section)) {
370             $sectionno = $section->section;
371         } else {
372             $sectionno = $section;
373         }
374         if (!empty($options['navigation']) && $sectionno !== null) {
375             // by default assume that sections are never displayed on separate pages
376             return null;
377         }
378         if ($this->uses_sections() && $sectionno !== null) {
379             $url->set_anchor('section-'.$sectionno);
380         }
381         return $url;
382     }
384     /**
385      * Loads all of the course sections into the navigation
386      *
387      * This method is called from {@link global_navigation::load_course_sections()}
388      *
389      * By default the method {@link global_navigation::load_generic_course_sections()} is called
390      *
391      * When overwriting please note that navigationlib relies on using the correct values for
392      * arguments $type and $key in {@link navigation_node::add()}
393      *
394      * Example of code creating a section node:
395      * $sectionnode = $node->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id);
396      * $sectionnode->nodetype = navigation_node::NODETYPE_BRANCH;
397      *
398      * Example of code creating an activity node:
399      * $activitynode = $sectionnode->add($activityname, $action, navigation_node::TYPE_ACTIVITY, null, $activity->id, $icon);
400      * if (global_navigation::module_extends_navigation($activity->modname)) {
401      *     $activitynode->nodetype = navigation_node::NODETYPE_BRANCH;
402      * } else {
403      *     $activitynode->nodetype = navigation_node::NODETYPE_LEAF;
404      * }
405      *
406      * Also note that if $navigation->includesectionnum is not null, the section with this relative
407      * number needs is expected to be loaded
408      *
409      * @param global_navigation $navigation
410      * @param navigation_node $node The course node within the navigation
411      */
412     public function extend_course_navigation($navigation, navigation_node $node) {
413         if ($course = $this->get_course()) {
414             $navigation->load_generic_course_sections($course, $node);
415         }
416         return array();
417     }
419     /**
420      * Returns the list of blocks to be automatically added for the newly created course
421      *
422      * @see blocks_add_default_course_blocks()
423      *
424      * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
425      *     each of values is an array of block names (for left and right side columns)
426      */
427     public function get_default_blocks() {
428         global $CFG;
429         if (!empty($CFG->defaultblocks)){
430             return blocks_parse_default_blocks_list($CFG->defaultblocks);
431         }
432         $blocknames = array(
433             BLOCK_POS_LEFT => array(),
434             BLOCK_POS_RIGHT => array('search_forums', 'news_items', 'calendar_upcoming', 'recent_activity')
435         );
436         return $blocknames;
437     }
439     /**
440      * Returns the localised name of this course format plugin
441      *
442      * @return lang_string
443      */
444     public final function get_format_name() {
445         return new lang_string('pluginname', 'format_'.$this->get_format());
446     }
448     /**
449      * Definitions of the additional options that this course format uses for course
450      *
451      * This function may be called often, it should be as fast as possible.
452      * Avoid using get_string() method, use "new lang_string()" instead
453      * It is not recommended to use dynamic or course-dependant expressions here
454      * This function may be also called when course does not exist yet.
455      *
456      * Option names must be different from fields in the {course} talbe or any form elements on
457      * course edit form, it may even make sence to use special prefix for them.
458      *
459      * Each option must have the option name as a key and the array of properties as a value:
460      * 'default' - default value for this option (assumed null if not specified)
461      * 'type' - type of the option value (PARAM_INT, PARAM_RAW, etc.)
462      *
463      * Additional properties used by default implementation of
464      * {@link format_base::create_edit_form_elements()} (calls this method with $foreditform = true)
465      * 'label' - localised human-readable label for the edit form
466      * 'element_type' - type of the form element, default 'text'
467      * 'element_attributes' - additional attributes for the form element, these are 4th and further
468      *    arguments in the moodleform::addElement() method
469      * 'help' - string for help button. Note that if 'help' value is 'myoption' then the string with
470      *    the name 'myoption_help' must exist in the language file
471      * 'help_component' - language component to look for help string, by default this the component
472      *    for this course format
473      *
474      * This is an interface for creating simple form elements. If format plugin wants to use other
475      * methods such as disableIf, it can be done by overriding create_edit_form_elements().
476      *
477      * Course format options can be accessed as:
478      * $this->get_course()->OPTIONNAME (inside the format class)
479      * course_get_format($course)->get_course()->OPTIONNAME (outside of format class)
480      *
481      * All course options are returned by calling:
482      * $this->get_format_options();
483      *
484      * @param bool $foreditform
485      * @return array of options
486      */
487     public function course_format_options($foreditform = false) {
488         return array();
489     }
491     /**
492      * Definitions of the additional options that this course format uses for section
493      *
494      * See {@link format_base::course_format_options()} for return array definition.
495      *
496      * Additionally section format options may have property 'cache' set to true
497      * if this option needs to be cached in {@link get_fast_modinfo()}. The 'cache' property
498      * is recommended to be set only for fields used in {@link format_base::get_section_name()},
499      * {@link format_base::extend_course_navigation()} and {@link format_base::get_view_url()}
500      *
501      * For better performance cached options are recommended to have 'cachedefault' property
502      * Unlike 'default', 'cachedefault' should be static and not access get_config().
503      *
504      * Regardless of value of 'cache' all options are accessed in the code as
505      * $sectioninfo->OPTIONNAME
506      * where $sectioninfo is instance of section_info, returned by
507      * get_fast_modinfo($course)->get_section_info($sectionnum)
508      * or get_fast_modinfo($course)->get_section_info_all()
509      *
510      * All format options for particular section are returned by calling:
511      * $this->get_format_options($section);
512      *
513      * @param bool $foreditform
514      * @return array
515      */
516     public function section_format_options($foreditform = false) {
517         return array();
518     }
520     /**
521      * Returns the format options stored for this course or course section
522      *
523      * When overriding please note that this function is called from rebuild_course_cache()
524      * and section_info object, therefore using of get_fast_modinfo() and/or any function that
525      * accesses it may lead to recursion.
526      *
527      * @param null|int|stdClass|section_info $section if null the course format options will be returned
528      *     otherwise options for specified section will be returned. This can be either
529      *     section object or relative section number (field course_sections.section)
530      * @return array
531      */
532     public function get_format_options($section = null) {
533         global $DB;
534         if ($section === null) {
535             $options = $this->course_format_options();
536         } else {
537             $options = $this->section_format_options();
538         }
539         if (empty($options)) {
540             // there are no option for course/sections anyway, no need to go further
541             return array();
542         }
543         if ($section === null) {
544             // course format options will be returned
545             $sectionid = 0;
546         } else if ($this->courseid && isset($section->id)) {
547             // course section format options will be returned
548             $sectionid = $section->id;
549         } else if ($this->courseid && is_int($section) &&
550                 ($sectionobj = $DB->get_record('course_sections',
551                         array('section' => $section, 'courseid' => $this->courseid), 'id'))) {
552             // course section format options will be returned
553             $sectionid = $sectionobj->id;
554         } else {
555             // non-existing (yet) section was passed as an argument
556             // default format options for course section will be returned
557             $sectionid = -1;
558         }
559         if (!array_key_exists($sectionid, $this->formatoptions)) {
560             $this->formatoptions[$sectionid] = array();
561             // first fill with default values
562             foreach ($options as $optionname => $optionparams) {
563                 $this->formatoptions[$sectionid][$optionname] = null;
564                 if (array_key_exists('default', $optionparams)) {
565                     $this->formatoptions[$sectionid][$optionname] = $optionparams['default'];
566                 }
567             }
568             if ($this->courseid && $sectionid !== -1) {
569                 // overwrite the default options values with those stored in course_format_options table
570                 // nothing can be stored if we are interested in generic course ($this->courseid == 0)
571                 // or generic section ($sectionid === 0)
572                 $records = $DB->get_records('course_format_options',
573                         array('courseid' => $this->courseid,
574                               'format' => $this->format,
575                               'sectionid' => $sectionid
576                             ), '', 'id,name,value');
577                 foreach ($records as $record) {
578                     if (array_key_exists($record->name, $this->formatoptions[$sectionid])) {
579                         $value = $record->value;
580                         if ($value !== null && isset($options[$record->name]['type'])) {
581                             // this will convert string value to number if needed
582                             $value = clean_param($value, $options[$record->name]['type']);
583                         }
584                         $this->formatoptions[$sectionid][$record->name] = $value;
585                     }
586                 }
587             }
588         }
589         return $this->formatoptions[$sectionid];
590     }
592     /**
593      * Adds format options elements to the course/section edit form
594      *
595      * This function is called from {@link course_edit_form::definition_after_data()}
596      *
597      * @param MoodleQuickForm $mform form the elements are added to
598      * @param bool $forsection 'true' if this is a section edit form, 'false' if this is course edit form
599      * @return array array of references to the added form elements
600      */
601     public function create_edit_form_elements(&$mform, $forsection = false) {
602         $elements = array();
603         if ($forsection) {
604             $options = $this->section_format_options(true);
605         } else {
606             $options = $this->course_format_options(true);
607         }
608         foreach ($options as $optionname => $option) {
609             if (!isset($option['element_type'])) {
610                 $option['element_type'] = 'text';
611             }
612             $args = array($option['element_type'], $optionname, $option['label']);
613             if (!empty($option['element_attributes'])) {
614                 $args = array_merge($args, $option['element_attributes']);
615             }
616             $elements[] = call_user_func_array(array($mform, 'addElement'), $args);
617             if (isset($option['help'])) {
618                 $helpcomponent = 'format_'. $this->get_format();
619                 if (isset($option['help_component'])) {
620                     $helpcomponent = $option['help_component'];
621                 }
622                 $mform->addHelpButton($optionname, $option['help'], $helpcomponent);
623             }
624             if (isset($option['type'])) {
625                 $mform->setType($optionname, $option['type']);
626             }
627             if (is_null($mform->getElementValue($optionname)) && isset($option['default'])) {
628                 $mform->setDefault($optionname, $option['default']);
629             }
630         }
631         return $elements;
632     }
634     /**
635      * Override if you need to perform some extra validation of the format options
636      *
637      * @param array $data array of ("fieldname"=>value) of submitted data
638      * @param array $files array of uploaded files "element_name"=>tmp_file_path
639      * @param array $errors errors already discovered in edit form validation
640      * @return array of "element_name"=>"error_description" if there are errors,
641      *         or an empty array if everything is OK.
642      *         Do not repeat errors from $errors param here
643      */
644     public function edit_form_validation($data, $files, $errors) {
645         return array();
646     }
648     /**
649      * Updates format options for a course or section
650      *
651      * If $data does not contain property with the option name, the option will not be updated
652      *
653      * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
654      * @param null|int null if these are options for course or section id (course_sections.id)
655      *     if these are options for section
656      * @return bool whether there were any changes to the options values
657      */
658     protected function update_format_options($data, $sectionid = null) {
659         global $DB;
660         if (!$sectionid) {
661             $allformatoptions = $this->course_format_options();
662             $sectionid = 0;
663         } else {
664             $allformatoptions = $this->section_format_options();
665         }
666         if (empty($allformatoptions)) {
667             // nothing to update anyway
668             return false;
669         }
670         $defaultoptions = array();
671         $cached = array();
672         foreach ($allformatoptions as $key => $option) {
673             $defaultoptions[$key] = null;
674             if (array_key_exists('default', $option)) {
675                 $defaultoptions[$key] = $option['default'];
676             }
677             $cached[$key] = ($sectionid === 0 || !empty($option['cache']));
678         }
679         $records = $DB->get_records('course_format_options',
680                 array('courseid' => $this->courseid,
681                       'format' => $this->format,
682                       'sectionid' => $sectionid
683                     ), '', 'name,id,value');
684         $changed = $needrebuild = false;
685         $data = (array)$data;
686         foreach ($defaultoptions as $key => $value) {
687             if (isset($records[$key])) {
688                 if (array_key_exists($key, $data) && $records[$key]->value !== $data[$key]) {
689                     $DB->set_field('course_format_options', 'value',
690                             $data[$key], array('id' => $records[$key]->id));
691                     $changed = true;
692                     $needrebuild = $needrebuild || $cached[$key];
693                 }
694             } else {
695                 if (array_key_exists($key, $data) && $data[$key] !== $value) {
696                     $newvalue = $data[$key];
697                     $changed = true;
698                     $needrebuild = $needrebuild || $cached[$key];
699                 } else {
700                     $newvalue = $value;
701                     // we still insert entry in DB but there are no changes from user point of
702                     // view and no need to call rebuild_course_cache()
703                 }
704                 $DB->insert_record('course_format_options', array(
705                     'courseid' => $this->courseid,
706                     'format' => $this->format,
707                     'sectionid' => $sectionid,
708                     'name' => $key,
709                     'value' => $newvalue
710                 ));
711             }
712         }
713         if ($needrebuild) {
714             rebuild_course_cache($this->courseid, true);
715         }
716         if ($changed) {
717             // reset internal caches
718             if (!$sectionid) {
719                 $this->course = false;
720             }
721             unset($this->formatoptions[$sectionid]);
722         }
723         return $changed;
724     }
726     /**
727      * Updates format options for a course
728      *
729      * If $data does not contain property with the option name, the option will not be updated
730      *
731      * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
732      * @param stdClass $oldcourse if this function is called from {@link update_course()}
733      *     this object contains information about the course before update
734      * @return bool whether there were any changes to the options values
735      */
736     public function update_course_format_options($data, $oldcourse = null) {
737         return $this->update_format_options($data);
738     }
740     /**
741      * Updates format options for a section
742      *
743      * Section id is expected in $data->id (or $data['id'])
744      * If $data does not contain property with the option name, the option will not be updated
745      *
746      * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
747      * @return bool whether there were any changes to the options values
748      */
749     public function update_section_format_options($data) {
750         $data = (array)$data;
751         return $this->update_format_options($data, $data['id']);
752     }
754     /**
755      * Return an instance of moodleform to edit a specified section
756      *
757      * Default implementation returns instance of editsection_form that automatically adds
758      * additional fields defined in {@link format_base::section_format_options()}
759      *
760      * Format plugins may extend editsection_form if they want to have custom edit section form.
761      *
762      * @param mixed $action the action attribute for the form. If empty defaults to auto detect the
763      *              current url. If a moodle_url object then outputs params as hidden variables.
764      * @param array $customdata the array with custom data to be passed to the form
765      *     /course/editsection.php passes section_info object in 'cs' field
766      *     for filling availability fields
767      * @return moodleform
768      */
769     public function editsection_form($action, $customdata = array()) {
770         global $CFG;
771         require_once($CFG->dirroot. '/course/editsection_form.php');
772         $context = context_course::instance($this->courseid);
773         if (!array_key_exists('course', $customdata)) {
774             $customdata['course'] = $this->get_course();
775         }
776         return new editsection_form($action, $customdata);
777     }
779     /**
780      * Allows course format to execute code on moodle_page::set_course()
781      *
782      * @param moodle_page $page instance of page calling set_course
783      */
784     public function page_set_course(moodle_page $page) {
785     }
787     /**
788      * Allows course format to execute code on moodle_page::set_cm()
789      *
790      * Current module can be accessed as $page->cm (returns instance of cm_info)
791      *
792      * @param moodle_page $page instance of page calling set_cm
793      */
794     public function page_set_cm(moodle_page $page) {
795     }
797     /**
798      * Course-specific information to be output on any course page (usually above navigation bar)
799      *
800      * Example of usage:
801      * define
802      * class format_FORMATNAME_XXX implements renderable {}
803      *
804      * create format renderer in course/format/FORMATNAME/renderer.php, define rendering function:
805      * class format_FORMATNAME_renderer extends plugin_renderer_base {
806      *     protected function render_format_FORMATNAME_XXX(format_FORMATNAME_XXX $xxx) {
807      *         return html_writer::tag('div', 'This is my header/footer');
808      *     }
809      * }
810      *
811      * Return instance of format_FORMATNAME_XXX in this function, the appropriate method from
812      * plugin renderer will be called
813      *
814      * @return null|renderable null for no output or object with data for plugin renderer
815      */
816     public function course_header() {
817         return null;
818     }
820     /**
821      * Course-specific information to be output on any course page (usually in the beginning of
822      * standard footer)
823      *
824      * See {@link format_base::course_header()} for usage
825      *
826      * @return null|renderable null for no output or object with data for plugin renderer
827      */
828     public function course_footer() {
829         return null;
830     }
832     /**
833      * Course-specific information to be output immediately above content on any course page
834      *
835      * See {@link format_base::course_header()} for usage
836      *
837      * @return null|renderable null for no output or object with data for plugin renderer
838      */
839     public function course_content_header() {
840         return null;
841     }
843     /**
844      * Course-specific information to be output immediately below content on any course page
845      *
846      * See {@link format_base::course_header()} for usage
847      *
848      * @return null|renderable null for no output or object with data for plugin renderer
849      */
850     public function course_content_footer() {
851         return null;
852     }
854     /**
855      * Returns instance of page renderer used by this plugin
856      *
857      * @param moodle_page $page
858      * @return renderer_base
859      */
860     public function get_renderer(moodle_page $page) {
861         return $page->get_renderer('format_'. $this->get_format());
862     }
865 /**
866  * Pseudo course format used for the site main page
867  *
868  * @package    core_course
869  * @copyright  2012 Marina Glancy
870  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
871  */
872 class format_site extends format_base {
874     /**
875      * Returns the display name of the given section that the course prefers.
876      *
877      * @param int|stdClass $section Section object from database or just field section.section
878      * @return Display name that the course format prefers, e.g. "Topic 2"
879      */
880     function get_section_name($section) {
881         return get_string('site');
882     }
884     /**
885      * For this fake course referring to the whole site, the site homepage is always returned
886      * regardless of arguments
887      *
888      * @param int|stdClass $section
889      * @param array $options
890      * @return null|moodle_url
891      */
892     public function get_view_url($section, $options = array()) {
893         return new moodle_url('/');
894     }
896     /**
897      * Returns the list of blocks to be automatically added on the site frontpage when moodle is installed
898      *
899      * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
900      *     each of values is an array of block names (for left and right side columns)
901      */
902     public function get_default_blocks() {
903         return blocks_get_default_site_course_blocks();
904     }
906     /**
907      * Definitions of the additional options that site uses
908      *
909      * @param bool $foreditform
910      * @return array of options
911      */
912     public function course_format_options($foreditform = false) {
913         static $courseformatoptions = false;
914         if ($courseformatoptions === false) {
915             $courseformatoptions = array(
916                 'numsections' => array(
917                     'default' => 1,
918                     'type' => PARAM_INT,
919                 ),
920             );
921         }
922         return $courseformatoptions;
923     }