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