Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   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  
  25  defined('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   */
  34  function 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   */
  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();
  75      /** @var array plugin name => class name. */
  76      private static $classesforformat = array('site' => 'site');
  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      /**
  93       * Validates that course format exists and enabled and returns either itself or default format
  94       *
  95       * @param string $format
  96       * @return string
  97       */
  98      protected static final function get_format_or_default($format) {
  99          global $CFG;
 100          require_once($CFG->dirroot . '/course/lib.php');
 101  
 102          if (array_key_exists($format, self::$classesforformat)) {
 103              return self::$classesforformat[$format];
 104          }
 105  
 106          $plugins = get_sorted_course_formats();
 107          foreach ($plugins as $plugin) {
 108              self::$classesforformat[$plugin] = $plugin;
 109          }
 110  
 111          if (array_key_exists($format, self::$classesforformat)) {
 112              return self::$classesforformat[$format];
 113          }
 114  
 115          if (PHPUNIT_TEST && class_exists('format_' . $format)) {
 116              // Allow unittests to use non-existing course formats.
 117              return $format;
 118          }
 119  
 120          // Else return default format
 121          $defaultformat = get_config('moodlecourse', 'format');
 122          if (!in_array($defaultformat, $plugins)) {
 123              // when default format is not set correctly, use the first available format
 124              $defaultformat = reset($plugins);
 125          }
 126          debugging('Format plugin format_'.$format.' is not found. Using default format_'.$defaultformat, DEBUG_DEVELOPER);
 127  
 128          self::$classesforformat[$format] = $defaultformat;
 129          return $defaultformat;
 130      }
 131  
 132      /**
 133       * Get class name for the format
 134       *
 135       * If course format xxx does not declare class format_xxx, format_legacy will be returned.
 136       * This function also includes lib.php file from corresponding format plugin
 137       *
 138       * @param string $format
 139       * @return string
 140       */
 141      protected static final function get_class_name($format) {
 142          global $CFG;
 143          static $classnames = array('site' => 'format_site');
 144          if (!isset($classnames[$format])) {
 145              $plugins = core_component::get_plugin_list('format');
 146              $usedformat = self::get_format_or_default($format);
 147              if (isset($plugins[$usedformat]) && file_exists($plugins[$usedformat].'/lib.php')) {
 148                  require_once($plugins[$usedformat].'/lib.php');
 149              }
 150              $classnames[$format] = 'format_'. $usedformat;
 151              if (!class_exists($classnames[$format])) {
 152                  require_once($CFG->dirroot.'/course/format/formatlegacy.php');
 153                  $classnames[$format] = 'format_legacy';
 154              }
 155          }
 156          return $classnames[$format];
 157      }
 158  
 159      /**
 160       * Returns an instance of the class
 161       *
 162       * @todo MDL-35727 use MUC for caching of instances, limit the number of cached instances
 163       *
 164       * @param int|stdClass $courseorid either course id or
 165       *     an object that has the property 'format' and may contain property 'id'
 166       * @return format_base
 167       */
 168      public static final function instance($courseorid) {
 169          global $DB;
 170          if (!is_object($courseorid)) {
 171              $courseid = (int)$courseorid;
 172              if ($courseid && isset(self::$instances[$courseid]) && count(self::$instances[$courseid]) == 1) {
 173                  $formats = array_keys(self::$instances[$courseid]);
 174                  $format = reset($formats);
 175              } else {
 176                  $format = $DB->get_field('course', 'format', array('id' => $courseid), MUST_EXIST);
 177              }
 178          } else {
 179              $format = $courseorid->format;
 180              if (isset($courseorid->id)) {
 181                  $courseid = clean_param($courseorid->id, PARAM_INT);
 182              } else {
 183                  $courseid = 0;
 184              }
 185          }
 186          // validate that format exists and enabled, use default otherwise
 187          $format = self::get_format_or_default($format);
 188          if (!isset(self::$instances[$courseid][$format])) {
 189              $classname = self::get_class_name($format);
 190              self::$instances[$courseid][$format] = new $classname($format, $courseid);
 191          }
 192          return self::$instances[$courseid][$format];
 193      }
 194  
 195      /**
 196       * Resets cache for the course (or all caches)
 197       * To be called from {@link rebuild_course_cache()}
 198       *
 199       * @param int $courseid
 200       */
 201      public static final function reset_course_cache($courseid = 0) {
 202          if ($courseid) {
 203              if (isset(self::$instances[$courseid])) {
 204                  foreach (self::$instances[$courseid] as $format => $object) {
 205                      // in case somebody keeps the reference to course format object
 206                      self::$instances[$courseid][$format]->course = false;
 207                      self::$instances[$courseid][$format]->formatoptions = array();
 208                  }
 209                  unset(self::$instances[$courseid]);
 210              }
 211          } else {
 212              self::$instances = array();
 213          }
 214      }
 215  
 216      /**
 217       * Returns the format name used by this course
 218       *
 219       * @return string
 220       */
 221      public final function get_format() {
 222          return $this->format;
 223      }
 224  
 225      /**
 226       * Returns id of the course (0 if course is not specified)
 227       *
 228       * @return int
 229       */
 230      public final function get_courseid() {
 231          return $this->courseid;
 232      }
 233  
 234      /**
 235       * Returns a record from course database table plus additional fields
 236       * that course format defines
 237       *
 238       * @return stdClass
 239       */
 240      public function get_course() {
 241          global $DB;
 242          if (!$this->courseid) {
 243              return null;
 244          }
 245          if ($this->course === false) {
 246              $this->course = get_course($this->courseid);
 247              $options = $this->get_format_options();
 248              $dbcoursecolumns = null;
 249              foreach ($options as $optionname => $optionvalue) {
 250                  if (isset($this->course->$optionname)) {
 251                      // Course format options must not have the same names as existing columns in db table "course".
 252                      if (!isset($dbcoursecolumns)) {
 253                          $dbcoursecolumns = $DB->get_columns('course');
 254                      }
 255                      if (isset($dbcoursecolumns[$optionname])) {
 256                          debugging('The option name '.$optionname.' in course format '.$this->format.
 257                              ' is invalid because the field with the same name exists in {course} table',
 258                              DEBUG_DEVELOPER);
 259                          continue;
 260                      }
 261                  }
 262                  $this->course->$optionname = $optionvalue;
 263              }
 264          }
 265          return $this->course;
 266      }
 267  
 268      /**
 269       * Method used in the rendered and during backup instead of legacy 'numsections'
 270       *
 271       * Default renderer will treat sections with sectionnumber greater that the value returned by this
 272       * method as "orphaned" and not display them on the course page unless in editing mode.
 273       * Backup will store this value as 'numsections'.
 274       *
 275       * This method ensures that 3rd party course format plugins that still use 'numsections' continue to
 276       * work but at the same time we no longer expect formats to have 'numsections' property.
 277       *
 278       * @return int
 279       */
 280      public function get_last_section_number() {
 281          $course = $this->get_course();
 282          if (isset($course->numsections)) {
 283              return $course->numsections;
 284          }
 285          $modinfo = get_fast_modinfo($course);
 286          $sections = $modinfo->get_section_info_all();
 287          return (int)max(array_keys($sections));
 288      }
 289  
 290      /**
 291       * Method used to get the maximum number of sections for this course format.
 292       * @return int
 293       */
 294      public function get_max_sections() {
 295          $maxsections = get_config('moodlecourse', 'maxsections');
 296          if (!isset($maxsections) || !is_numeric($maxsections)) {
 297              $maxsections = 52;
 298          }
 299          return $maxsections;
 300      }
 301  
 302      /**
 303       * Returns true if the course has a front page.
 304       *
 305       * This function is called to determine if the course has a view page, whether or not
 306       * it contains a listing of activities. It can be useful to set this to false when the course
 307       * format has only one activity and ignores the course page. Or if there are multiple
 308       * activities but no page to see the centralised information.
 309       *
 310       * Initially this was created to know if forms should add a button to return to the course page.
 311       * So if 'Return to course' does not make sense in your format your should probably return false.
 312       *
 313       * @return boolean
 314       * @since Moodle 2.6
 315       */
 316      public function has_view_page() {
 317          return true;
 318      }
 319  
 320      /**
 321       * Returns true if this course format uses sections
 322       *
 323       * This function may be called without specifying the course id
 324       * i.e. in {@link course_format_uses_sections()}
 325       *
 326       * Developers, note that if course format does use sections there should be defined a language
 327       * string with the name 'sectionname' defining what the section relates to in the format, i.e.
 328       * $string['sectionname'] = 'Topic';
 329       * or
 330       * $string['sectionname'] = 'Week';
 331       *
 332       * @return bool
 333       */
 334      public function uses_sections() {
 335          return false;
 336      }
 337  
 338      /**
 339       * Returns a list of sections used in the course
 340       *
 341       * This is a shortcut to get_fast_modinfo()->get_section_info_all()
 342       * @see get_fast_modinfo()
 343       * @see course_modinfo::get_section_info_all()
 344       *
 345       * @return array of section_info objects
 346       */
 347      public final function get_sections() {
 348          if ($course = $this->get_course()) {
 349              $modinfo = get_fast_modinfo($course);
 350              return $modinfo->get_section_info_all();
 351          }
 352          return array();
 353      }
 354  
 355      /**
 356       * Returns information about section used in course
 357       *
 358       * @param int|stdClass $section either section number (field course_section.section) or row from course_section table
 359       * @param int $strictness
 360       * @return section_info
 361       */
 362      public final function get_section($section, $strictness = IGNORE_MISSING) {
 363          if (is_object($section)) {
 364              $sectionnum = $section->section;
 365          } else {
 366              $sectionnum = $section;
 367          }
 368          $sections = $this->get_sections();
 369          if (array_key_exists($sectionnum, $sections)) {
 370              return $sections[$sectionnum];
 371          }
 372          if ($strictness == MUST_EXIST) {
 373              throw new moodle_exception('sectionnotexist');
 374          }
 375          return null;
 376      }
 377  
 378      /**
 379       * Returns the display name of the given section that the course prefers.
 380       *
 381       * @param int|stdClass $section Section object from database or just field course_sections.section
 382       * @return Display name that the course format prefers, e.g. "Topic 2"
 383       */
 384      public function get_section_name($section) {
 385          if (is_object($section)) {
 386              $sectionnum = $section->section;
 387          } else {
 388              $sectionnum = $section;
 389          }
 390  
 391          if (get_string_manager()->string_exists('sectionname', 'format_' . $this->format)) {
 392              return get_string('sectionname', 'format_' . $this->format) . ' ' . $sectionnum;
 393          }
 394  
 395          // Return an empty string if there's no available section name string for the given format.
 396          return '';
 397      }
 398  
 399      /**
 400       * Returns the default section using format_base's implementation of get_section_name.
 401       *
 402       * @param int|stdClass $section Section object from database or just field course_sections section
 403       * @return string The default value for the section name based on the given course format.
 404       */
 405      public function get_default_section_name($section) {
 406          return self::get_section_name($section);
 407      }
 408  
 409      /**
 410       * Returns the information about the ajax support in the given source format
 411       *
 412       * The returned object's property (boolean)capable indicates that
 413       * the course format supports Moodle course ajax features.
 414       *
 415       * @return stdClass
 416       */
 417      public function supports_ajax() {
 418          // no support by default
 419          $ajaxsupport = new stdClass();
 420          $ajaxsupport->capable = false;
 421          return $ajaxsupport;
 422      }
 423  
 424      /**
 425       * Custom action after section has been moved in AJAX mode
 426       *
 427       * Used in course/rest.php
 428       *
 429       * @return array This will be passed in ajax respose
 430       */
 431      public function ajax_section_move() {
 432          return null;
 433      }
 434  
 435      /**
 436       * The URL to use for the specified course (with section)
 437       *
 438       * Please note that course view page /course/view.php?id=COURSEID is hardcoded in many
 439       * places in core and contributed modules. If course format wants to change the location
 440       * of the view script, it is not enough to change just this function. Do not forget
 441       * to add proper redirection.
 442       *
 443       * @param int|stdClass $section Section object from database or just field course_sections.section
 444       *     if null the course view page is returned
 445       * @param array $options options for view URL. At the moment core uses:
 446       *     'navigation' (bool) if true and section has no separate page, the function returns null
 447       *     'sr' (int) used by multipage formats to specify to which section to return
 448       * @return null|moodle_url
 449       */
 450      public function get_view_url($section, $options = array()) {
 451          global $CFG;
 452          $course = $this->get_course();
 453          $url = new moodle_url('/course/view.php', array('id' => $course->id));
 454  
 455          if (array_key_exists('sr', $options)) {
 456              $sectionno = $options['sr'];
 457          } else if (is_object($section)) {
 458              $sectionno = $section->section;
 459          } else {
 460              $sectionno = $section;
 461          }
 462          if (empty($CFG->linkcoursesections) && !empty($options['navigation']) && $sectionno !== null) {
 463              // by default assume that sections are never displayed on separate pages
 464              return null;
 465          }
 466          if ($this->uses_sections() && $sectionno !== null) {
 467              $url->set_anchor('section-'.$sectionno);
 468          }
 469          return $url;
 470      }
 471  
 472      /**
 473       * Loads all of the course sections into the navigation
 474       *
 475       * This method is called from {@link global_navigation::load_course_sections()}
 476       *
 477       * By default the method {@link global_navigation::load_generic_course_sections()} is called
 478       *
 479       * When overwriting please note that navigationlib relies on using the correct values for
 480       * arguments $type and $key in {@link navigation_node::add()}
 481       *
 482       * Example of code creating a section node:
 483       * $sectionnode = $node->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id);
 484       * $sectionnode->nodetype = navigation_node::NODETYPE_BRANCH;
 485       *
 486       * Example of code creating an activity node:
 487       * $activitynode = $sectionnode->add($activityname, $action, navigation_node::TYPE_ACTIVITY, null, $activity->id, $icon);
 488       * if (global_navigation::module_extends_navigation($activity->modname)) {
 489       *     $activitynode->nodetype = navigation_node::NODETYPE_BRANCH;
 490       * } else {
 491       *     $activitynode->nodetype = navigation_node::NODETYPE_LEAF;
 492       * }
 493       *
 494       * Also note that if $navigation->includesectionnum is not null, the section with this relative
 495       * number needs is expected to be loaded
 496       *
 497       * @param global_navigation $navigation
 498       * @param navigation_node $node The course node within the navigation
 499       */
 500      public function extend_course_navigation($navigation, navigation_node $node) {
 501          if ($course = $this->get_course()) {
 502              $navigation->load_generic_course_sections($course, $node);
 503          }
 504          return array();
 505      }
 506  
 507      /**
 508       * Returns the list of blocks to be automatically added for the newly created course
 509       *
 510       * @see blocks_add_default_course_blocks()
 511       *
 512       * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
 513       *     each of values is an array of block names (for left and right side columns)
 514       */
 515      public function get_default_blocks() {
 516          global $CFG;
 517          if (isset($CFG->defaultblocks)) {
 518              return blocks_parse_default_blocks_list($CFG->defaultblocks);
 519          }
 520          $blocknames = array(
 521              BLOCK_POS_LEFT => array(),
 522              BLOCK_POS_RIGHT => array()
 523          );
 524          return $blocknames;
 525      }
 526  
 527      /**
 528       * Returns the localised name of this course format plugin
 529       *
 530       * @return lang_string
 531       */
 532      public final function get_format_name() {
 533          return new lang_string('pluginname', 'format_'.$this->get_format());
 534      }
 535  
 536      /**
 537       * Definitions of the additional options that this course format uses for course
 538       *
 539       * This function may be called often, it should be as fast as possible.
 540       * Avoid using get_string() method, use "new lang_string()" instead
 541       * It is not recommended to use dynamic or course-dependant expressions here
 542       * This function may be also called when course does not exist yet.
 543       *
 544       * Option names must be different from fields in the {course} talbe or any form elements on
 545       * course edit form, it may even make sence to use special prefix for them.
 546       *
 547       * Each option must have the option name as a key and the array of properties as a value:
 548       * 'default' - default value for this option (assumed null if not specified)
 549       * 'type' - type of the option value (PARAM_INT, PARAM_RAW, etc.)
 550       *
 551       * Additional properties used by default implementation of
 552       * {@link format_base::create_edit_form_elements()} (calls this method with $foreditform = true)
 553       * 'label' - localised human-readable label for the edit form
 554       * 'element_type' - type of the form element, default 'text'
 555       * 'element_attributes' - additional attributes for the form element, these are 4th and further
 556       *    arguments in the moodleform::addElement() method
 557       * 'help' - string for help button. Note that if 'help' value is 'myoption' then the string with
 558       *    the name 'myoption_help' must exist in the language file
 559       * 'help_component' - language component to look for help string, by default this the component
 560       *    for this course format
 561       *
 562       * This is an interface for creating simple form elements. If format plugin wants to use other
 563       * methods such as disableIf, it can be done by overriding create_edit_form_elements().
 564       *
 565       * Course format options can be accessed as:
 566       * $this->get_course()->OPTIONNAME (inside the format class)
 567       * course_get_format($course)->get_course()->OPTIONNAME (outside of format class)
 568       *
 569       * All course options are returned by calling:
 570       * $this->get_format_options();
 571       *
 572       * @param bool $foreditform
 573       * @return array of options
 574       */
 575      public function course_format_options($foreditform = false) {
 576          return array();
 577      }
 578  
 579      /**
 580       * Definitions of the additional options that this course format uses for section
 581       *
 582       * See {@link format_base::course_format_options()} for return array definition.
 583       *
 584       * Additionally section format options may have property 'cache' set to true
 585       * if this option needs to be cached in {@link get_fast_modinfo()}. The 'cache' property
 586       * is recommended to be set only for fields used in {@link format_base::get_section_name()},
 587       * {@link format_base::extend_course_navigation()} and {@link format_base::get_view_url()}
 588       *
 589       * For better performance cached options are recommended to have 'cachedefault' property
 590       * Unlike 'default', 'cachedefault' should be static and not access get_config().
 591       *
 592       * Regardless of value of 'cache' all options are accessed in the code as
 593       * $sectioninfo->OPTIONNAME
 594       * where $sectioninfo is instance of section_info, returned by
 595       * get_fast_modinfo($course)->get_section_info($sectionnum)
 596       * or get_fast_modinfo($course)->get_section_info_all()
 597       *
 598       * All format options for particular section are returned by calling:
 599       * $this->get_format_options($section);
 600       *
 601       * @param bool $foreditform
 602       * @return array
 603       */
 604      public function section_format_options($foreditform = false) {
 605          return array();
 606      }
 607  
 608      /**
 609       * Returns the format options stored for this course or course section
 610       *
 611       * When overriding please note that this function is called from rebuild_course_cache()
 612       * and section_info object, therefore using of get_fast_modinfo() and/or any function that
 613       * accesses it may lead to recursion.
 614       *
 615       * @param null|int|stdClass|section_info $section if null the course format options will be returned
 616       *     otherwise options for specified section will be returned. This can be either
 617       *     section object or relative section number (field course_sections.section)
 618       * @return array
 619       */
 620      public function get_format_options($section = null) {
 621          global $DB;
 622          if ($section === null) {
 623              $options = $this->course_format_options();
 624          } else {
 625              $options = $this->section_format_options();
 626          }
 627          if (empty($options)) {
 628              // there are no option for course/sections anyway, no need to go further
 629              return array();
 630          }
 631          if ($section === null) {
 632              // course format options will be returned
 633              $sectionid = 0;
 634          } else if ($this->courseid && isset($section->id)) {
 635              // course section format options will be returned
 636              $sectionid = $section->id;
 637          } else if ($this->courseid && is_int($section) &&
 638                  ($sectionobj = $DB->get_record('course_sections',
 639                          array('section' => $section, 'course' => $this->courseid), 'id'))) {
 640              // course section format options will be returned
 641              $sectionid = $sectionobj->id;
 642          } else {
 643              // non-existing (yet) section was passed as an argument
 644              // default format options for course section will be returned
 645              $sectionid = -1;
 646          }
 647          if (!array_key_exists($sectionid, $this->formatoptions)) {
 648              $this->formatoptions[$sectionid] = array();
 649              // first fill with default values
 650              foreach ($options as $optionname => $optionparams) {
 651                  $this->formatoptions[$sectionid][$optionname] = null;
 652                  if (array_key_exists('default', $optionparams)) {
 653                      $this->formatoptions[$sectionid][$optionname] = $optionparams['default'];
 654                  }
 655              }
 656              if ($this->courseid && $sectionid !== -1) {
 657                  // overwrite the default options values with those stored in course_format_options table
 658                  // nothing can be stored if we are interested in generic course ($this->courseid == 0)
 659                  // or generic section ($sectionid === 0)
 660                  $records = $DB->get_records('course_format_options',
 661                          array('courseid' => $this->courseid,
 662                                'format' => $this->format,
 663                                'sectionid' => $sectionid
 664                              ), '', 'id,name,value');
 665  
 666                  $indexedrecords = [];
 667                  foreach ($records as $record) {
 668                      $indexedrecords[$record->name] = $record->value;
 669                  }
 670                  foreach ($options as $optionname => $option) {
 671                      contract_value($this->formatoptions[$sectionid], $indexedrecords, $option, $optionname);
 672                  }
 673              }
 674          }
 675          return $this->formatoptions[$sectionid];
 676      }
 677  
 678      /**
 679       * Adds format options elements to the course/section edit form
 680       *
 681       * This function is called from {@link course_edit_form::definition_after_data()}
 682       *
 683       * @param MoodleQuickForm $mform form the elements are added to
 684       * @param bool $forsection 'true' if this is a section edit form, 'false' if this is course edit form
 685       * @return array array of references to the added form elements
 686       */
 687      public function create_edit_form_elements(&$mform, $forsection = false) {
 688          $elements = array();
 689          if ($forsection) {
 690              $options = $this->section_format_options(true);
 691          } else {
 692              $options = $this->course_format_options(true);
 693          }
 694          foreach ($options as $optionname => $option) {
 695              if (!isset($option['element_type'])) {
 696                  $option['element_type'] = 'text';
 697              }
 698              $args = array($option['element_type'], $optionname, $option['label']);
 699              if (!empty($option['element_attributes'])) {
 700                  $args = array_merge($args, $option['element_attributes']);
 701              }
 702              $elements[] = call_user_func_array(array($mform, 'addElement'), $args);
 703              if (isset($option['help'])) {
 704                  $helpcomponent = 'format_'. $this->get_format();
 705                  if (isset($option['help_component'])) {
 706                      $helpcomponent = $option['help_component'];
 707                  }
 708                  $mform->addHelpButton($optionname, $option['help'], $helpcomponent);
 709              }
 710              if (isset($option['type'])) {
 711                  $mform->setType($optionname, $option['type']);
 712              }
 713              if (isset($option['default']) && !array_key_exists($optionname, $mform->_defaultValues)) {
 714                  // Set defaults for the elements in the form.
 715                  // Since we call this method after set_data() make sure that we don't override what was already set.
 716                  $mform->setDefault($optionname, $option['default']);
 717              }
 718          }
 719  
 720          if (!$forsection && empty($this->courseid)) {
 721              // Check if course end date form field should be enabled by default.
 722              // If a default date is provided to the form element, it is magically enabled by default in the
 723              // MoodleQuickForm_date_time_selector class, otherwise it's disabled by default.
 724              if (get_config('moodlecourse', 'courseenddateenabled')) {
 725                  // At this stage (this is called from definition_after_data) course data is already set as default.
 726                  // We can not overwrite what is in the database.
 727                  $mform->setDefault('enddate', $this->get_default_course_enddate($mform));
 728              }
 729          }
 730  
 731          return $elements;
 732      }
 733  
 734      /**
 735       * Override if you need to perform some extra validation of the format options
 736       *
 737       * @param array $data array of ("fieldname"=>value) of submitted data
 738       * @param array $files array of uploaded files "element_name"=>tmp_file_path
 739       * @param array $errors errors already discovered in edit form validation
 740       * @return array of "element_name"=>"error_description" if there are errors,
 741       *         or an empty array if everything is OK.
 742       *         Do not repeat errors from $errors param here
 743       */
 744      public function edit_form_validation($data, $files, $errors) {
 745          return array();
 746      }
 747  
 748      /**
 749       * Prepares values of course or section format options before storing them in DB
 750       *
 751       * If an option has invalid value it is not returned
 752       *
 753       * @param array $rawdata associative array of the proposed course/section format options
 754       * @param int|null $sectionid null if it is course format option
 755       * @return array array of options that have valid values
 756       */
 757      protected function validate_format_options(array $rawdata, int $sectionid = null) : array {
 758          if (!$sectionid) {
 759              $allformatoptions = $this->course_format_options(true);
 760          } else {
 761              $allformatoptions = $this->section_format_options(true);
 762          }
 763          $data = array_intersect_key($rawdata, $allformatoptions);
 764          foreach ($data as $key => $value) {
 765              $option = $allformatoptions[$key] + ['type' => PARAM_RAW, 'element_type' => null, 'element_attributes' => [[]]];
 766              expand_value($data, $data, $option, $key);
 767              if ($option['element_type'] === 'select' && !array_key_exists($data[$key], $option['element_attributes'][0])) {
 768                  // Value invalid for select element, skip.
 769                  unset($data[$key]);
 770              }
 771          }
 772          return $data;
 773      }
 774  
 775      /**
 776       * Validates format options for the course
 777       *
 778       * @param array $data data to insert/update
 779       * @return array array of options that have valid values
 780       */
 781      public function validate_course_format_options(array $data) : array {
 782          return $this->validate_format_options($data);
 783      }
 784  
 785      /**
 786       * Updates format options for a course or section
 787       *
 788       * If $data does not contain property with the option name, the option will not be updated
 789       *
 790       * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
 791       * @param null|int null if these are options for course or section id (course_sections.id)
 792       *     if these are options for section
 793       * @return bool whether there were any changes to the options values
 794       */
 795      protected function update_format_options($data, $sectionid = null) {
 796          global $DB;
 797          $data = $this->validate_format_options((array)$data, $sectionid);
 798          if (!$sectionid) {
 799              $allformatoptions = $this->course_format_options();
 800              $sectionid = 0;
 801          } else {
 802              $allformatoptions = $this->section_format_options();
 803          }
 804          if (empty($allformatoptions)) {
 805              // nothing to update anyway
 806              return false;
 807          }
 808          $defaultoptions = array();
 809          $cached = array();
 810          foreach ($allformatoptions as $key => $option) {
 811              $defaultoptions[$key] = null;
 812              if (array_key_exists('default', $option)) {
 813                  $defaultoptions[$key] = $option['default'];
 814              }
 815              expand_value($defaultoptions, $defaultoptions, $option, $key);
 816              $cached[$key] = ($sectionid === 0 || !empty($option['cache']));
 817          }
 818          $records = $DB->get_records('course_format_options',
 819                  array('courseid' => $this->courseid,
 820                        'format' => $this->format,
 821                        'sectionid' => $sectionid
 822                      ), '', 'name,id,value');
 823          $changed = $needrebuild = false;
 824          foreach ($defaultoptions as $key => $value) {
 825              if (isset($records[$key])) {
 826                  if (array_key_exists($key, $data) && $records[$key]->value !== $data[$key]) {
 827                      $DB->set_field('course_format_options', 'value',
 828                              $data[$key], array('id' => $records[$key]->id));
 829                      $changed = true;
 830                      $needrebuild = $needrebuild || $cached[$key];
 831                  }
 832              } else {
 833                  if (array_key_exists($key, $data) && $data[$key] !== $value) {
 834                      $newvalue = $data[$key];
 835                      $changed = true;
 836                      $needrebuild = $needrebuild || $cached[$key];
 837                  } else {
 838                      $newvalue = $value;
 839                      // we still insert entry in DB but there are no changes from user point of
 840                      // view and no need to call rebuild_course_cache()
 841                  }
 842                  $DB->insert_record('course_format_options', array(
 843                      'courseid' => $this->courseid,
 844                      'format' => $this->format,
 845                      'sectionid' => $sectionid,
 846                      'name' => $key,
 847                      'value' => $newvalue
 848                  ));
 849              }
 850          }
 851          if ($needrebuild) {
 852              rebuild_course_cache($this->courseid, true);
 853          }
 854          if ($changed) {
 855              // reset internal caches
 856              if (!$sectionid) {
 857                  $this->course = false;
 858              }
 859              unset($this->formatoptions[$sectionid]);
 860          }
 861          return $changed;
 862      }
 863  
 864      /**
 865       * Updates format options for a course
 866       *
 867       * If $data does not contain property with the option name, the option will not be updated
 868       *
 869       * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
 870       * @param stdClass $oldcourse if this function is called from {@link update_course()}
 871       *     this object contains information about the course before update
 872       * @return bool whether there were any changes to the options values
 873       */
 874      public function update_course_format_options($data, $oldcourse = null) {
 875          return $this->update_format_options($data);
 876      }
 877  
 878      /**
 879       * Updates format options for a section
 880       *
 881       * Section id is expected in $data->id (or $data['id'])
 882       * If $data does not contain property with the option name, the option will not be updated
 883       *
 884       * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
 885       * @return bool whether there were any changes to the options values
 886       */
 887      public function update_section_format_options($data) {
 888          $data = (array)$data;
 889          return $this->update_format_options($data, $data['id']);
 890      }
 891  
 892      /**
 893       * Return an instance of moodleform to edit a specified section
 894       *
 895       * Default implementation returns instance of editsection_form that automatically adds
 896       * additional fields defined in {@link format_base::section_format_options()}
 897       *
 898       * Format plugins may extend editsection_form if they want to have custom edit section form.
 899       *
 900       * @param mixed $action the action attribute for the form. If empty defaults to auto detect the
 901       *              current url. If a moodle_url object then outputs params as hidden variables.
 902       * @param array $customdata the array with custom data to be passed to the form
 903       *     /course/editsection.php passes section_info object in 'cs' field
 904       *     for filling availability fields
 905       * @return moodleform
 906       */
 907      public function editsection_form($action, $customdata = array()) {
 908          global $CFG;
 909          require_once($CFG->dirroot. '/course/editsection_form.php');
 910          $context = context_course::instance($this->courseid);
 911          if (!array_key_exists('course', $customdata)) {
 912              $customdata['course'] = $this->get_course();
 913          }
 914          return new editsection_form($action, $customdata);
 915      }
 916  
 917      /**
 918       * Allows course format to execute code on moodle_page::set_course()
 919       *
 920       * @param moodle_page $page instance of page calling set_course
 921       */
 922      public function page_set_course(moodle_page $page) {
 923      }
 924  
 925      /**
 926       * Allows course format to execute code on moodle_page::set_cm()
 927       *
 928       * Current module can be accessed as $page->cm (returns instance of cm_info)
 929       *
 930       * @param moodle_page $page instance of page calling set_cm
 931       */
 932      public function page_set_cm(moodle_page $page) {
 933      }
 934  
 935      /**
 936       * Course-specific information to be output on any course page (usually above navigation bar)
 937       *
 938       * Example of usage:
 939       * define
 940       * class format_FORMATNAME_XXX implements renderable {}
 941       *
 942       * create format renderer in course/format/FORMATNAME/renderer.php, define rendering function:
 943       * class format_FORMATNAME_renderer extends plugin_renderer_base {
 944       *     protected function render_format_FORMATNAME_XXX(format_FORMATNAME_XXX $xxx) {
 945       *         return html_writer::tag('div', 'This is my header/footer');
 946       *     }
 947       * }
 948       *
 949       * Return instance of format_FORMATNAME_XXX in this function, the appropriate method from
 950       * plugin renderer will be called
 951       *
 952       * @return null|renderable null for no output or object with data for plugin renderer
 953       */
 954      public function course_header() {
 955          return null;
 956      }
 957  
 958      /**
 959       * Course-specific information to be output on any course page (usually in the beginning of
 960       * standard footer)
 961       *
 962       * See {@link format_base::course_header()} for usage
 963       *
 964       * @return null|renderable null for no output or object with data for plugin renderer
 965       */
 966      public function course_footer() {
 967          return null;
 968      }
 969  
 970      /**
 971       * Course-specific information to be output immediately above content on any course page
 972       *
 973       * See {@link format_base::course_header()} for usage
 974       *
 975       * @return null|renderable null for no output or object with data for plugin renderer
 976       */
 977      public function course_content_header() {
 978          return null;
 979      }
 980  
 981      /**
 982       * Course-specific information to be output immediately below content on any course page
 983       *
 984       * See {@link format_base::course_header()} for usage
 985       *
 986       * @return null|renderable null for no output or object with data for plugin renderer
 987       */
 988      public function course_content_footer() {
 989          return null;
 990      }
 991  
 992      /**
 993       * Returns instance of page renderer used by this plugin
 994       *
 995       * @param moodle_page $page
 996       * @return renderer_base
 997       */
 998      public function get_renderer(moodle_page $page) {
 999          return $page->get_renderer('format_'. $this->get_format());
1000      }
1001  
1002      /**
1003       * Returns true if the specified section is current
1004       *
1005       * By default we analyze $course->marker
1006       *
1007       * @param int|stdClass|section_info $section
1008       * @return bool
1009       */
1010      public function is_section_current($section) {
1011          if (is_object($section)) {
1012              $sectionnum = $section->section;
1013          } else {
1014              $sectionnum = $section;
1015          }
1016          return ($sectionnum && ($course = $this->get_course()) && $course->marker == $sectionnum);
1017      }
1018  
1019      /**
1020       * Allows to specify for modinfo that section is not available even when it is visible and conditionally available.
1021       *
1022       * Note: affected user can be retrieved as: $section->modinfo->userid
1023       *
1024       * Course format plugins can override the method to change the properties $available and $availableinfo that were
1025       * calculated by conditional availability.
1026       * To make section unavailable set:
1027       *     $available = false;
1028       * To make unavailable section completely hidden set:
1029       *     $availableinfo = '';
1030       * To make unavailable section visible with availability message set:
1031       *     $availableinfo = get_string('sectionhidden', 'format_xxx');
1032       *
1033       * @param section_info $section
1034       * @param bool $available the 'available' propery of the section_info as it was evaluated by conditional availability.
1035       *     Can be changed by the method but 'false' can not be overridden by 'true'.
1036       * @param string $availableinfo the 'availableinfo' propery of the section_info as it was evaluated by conditional availability.
1037       *     Can be changed by the method
1038       */
1039      public function section_get_available_hook(section_info $section, &$available, &$availableinfo) {
1040      }
1041  
1042      /**
1043       * Whether this format allows to delete sections
1044       *
1045       * If format supports deleting sections it is also recommended to define language string
1046       * 'deletesection' inside the format.
1047       *
1048       * Do not call this function directly, instead use {@link course_can_delete_section()}
1049       *
1050       * @param int|stdClass|section_info $section
1051       * @return bool
1052       */
1053      public function can_delete_section($section) {
1054          return false;
1055      }
1056  
1057      /**
1058       * Deletes a section
1059       *
1060       * Do not call this function directly, instead call {@link course_delete_section()}
1061       *
1062       * @param int|stdClass|section_info $section
1063       * @param bool $forcedeleteifnotempty if set to false section will not be deleted if it has modules in it.
1064       * @return bool whether section was deleted
1065       */
1066      public function delete_section($section, $forcedeleteifnotempty = false) {
1067          global $DB;
1068          if (!$this->uses_sections()) {
1069              // Not possible to delete section if sections are not used.
1070              return false;
1071          }
1072          if (!is_object($section)) {
1073              $section = $DB->get_record('course_sections', array('course' => $this->get_courseid(), 'section' => $section),
1074                  'id,section,sequence,summary');
1075          }
1076          if (!$section || !$section->section) {
1077              // Not possible to delete 0-section.
1078              return false;
1079          }
1080  
1081          if (!$forcedeleteifnotempty && (!empty($section->sequence) || !empty($section->summary))) {
1082              return false;
1083          }
1084  
1085          $course = $this->get_course();
1086  
1087          // Remove the marker if it points to this section.
1088          if ($section->section == $course->marker) {
1089              course_set_marker($course->id, 0);
1090          }
1091  
1092          $lastsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
1093                              WHERE course = ?', array($course->id));
1094  
1095          // Find out if we need to descrease the 'numsections' property later.
1096          $courseformathasnumsections = array_key_exists('numsections',
1097              $this->get_format_options());
1098          $decreasenumsections = $courseformathasnumsections && ($section->section <= $course->numsections);
1099  
1100          // Move the section to the end.
1101          move_section_to($course, $section->section, $lastsection, true);
1102  
1103          // Delete all modules from the section.
1104          foreach (preg_split('/,/', $section->sequence, -1, PREG_SPLIT_NO_EMPTY) as $cmid) {
1105              course_delete_module($cmid);
1106          }
1107  
1108          // Delete section and it's format options.
1109          $DB->delete_records('course_format_options', array('sectionid' => $section->id));
1110          $DB->delete_records('course_sections', array('id' => $section->id));
1111          rebuild_course_cache($course->id, true);
1112  
1113          // Delete section summary files.
1114          $context = \context_course::instance($course->id);
1115          $fs = get_file_storage();
1116          $fs->delete_area_files($context->id, 'course', 'section', $section->id);
1117  
1118          // Descrease 'numsections' if needed.
1119          if ($decreasenumsections) {
1120              $this->update_course_format_options(array('numsections' => $course->numsections - 1));
1121          }
1122  
1123          return true;
1124      }
1125  
1126      /**
1127       * Prepares the templateable object to display section name
1128       *
1129       * @param \section_info|\stdClass $section
1130       * @param bool $linkifneeded
1131       * @param bool $editable
1132       * @param null|lang_string|string $edithint
1133       * @param null|lang_string|string $editlabel
1134       * @return \core\output\inplace_editable
1135       */
1136      public function inplace_editable_render_section_name($section, $linkifneeded = true,
1137                                                           $editable = null, $edithint = null, $editlabel = null) {
1138          global $USER, $CFG;
1139          require_once($CFG->dirroot.'/course/lib.php');
1140  
1141          if ($editable === null) {
1142              $editable = !empty($USER->editing) && has_capability('moodle/course:update',
1143                      context_course::instance($section->course));
1144          }
1145  
1146          $displayvalue = $title = get_section_name($section->course, $section);
1147          if ($linkifneeded) {
1148              // Display link under the section name if the course format setting is to display one section per page.
1149              $url = course_get_url($section->course, $section->section, array('navigation' => true));
1150              if ($url) {
1151                  $displayvalue = html_writer::link($url, $title);
1152              }
1153              $itemtype = 'sectionname';
1154          } else {
1155              // If $linkifneeded==false, we never display the link (this is used when rendering the section header).
1156              // Itemtype 'sectionnamenl' (nl=no link) will tell the callback that link should not be rendered -
1157              // there is no other way callback can know where we display the section name.
1158              $itemtype = 'sectionnamenl';
1159          }
1160          if (empty($edithint)) {
1161              $edithint = new lang_string('editsectionname');
1162          }
1163          if (empty($editlabel)) {
1164              $editlabel = new lang_string('newsectionname', '', $title);
1165          }
1166  
1167          return new \core\output\inplace_editable('format_' . $this->format, $itemtype, $section->id, $editable,
1168              $displayvalue, $section->name, $edithint, $editlabel);
1169      }
1170  
1171      /**
1172       * Updates the value in the database and modifies this object respectively.
1173       *
1174       * ALWAYS check user permissions before performing an update! Throw exceptions if permissions are not sufficient
1175       * or value is not legit.
1176       *
1177       * @param stdClass $section
1178       * @param string $itemtype
1179       * @param mixed $newvalue
1180       * @return \core\output\inplace_editable
1181       */
1182      public function inplace_editable_update_section_name($section, $itemtype, $newvalue) {
1183          if ($itemtype === 'sectionname' || $itemtype === 'sectionnamenl') {
1184              $context = context_course::instance($section->course);
1185              external_api::validate_context($context);
1186              require_capability('moodle/course:update', $context);
1187  
1188              $newtitle = clean_param($newvalue, PARAM_TEXT);
1189              if (strval($section->name) !== strval($newtitle)) {
1190                  course_update_section($section->course, $section, array('name' => $newtitle));
1191              }
1192              return $this->inplace_editable_render_section_name($section, ($itemtype === 'sectionname'), true);
1193          }
1194      }
1195  
1196  
1197      /**
1198       * Returns the default end date value based on the start date.
1199       *
1200       * This is the default implementation for course formats, it is based on
1201       * moodlecourse/courseduration setting. Course formats like format_weeks for
1202       * example can overwrite this method and return a value based on their internal options.
1203       *
1204       * @param moodleform $mform
1205       * @param array $fieldnames The form - field names mapping.
1206       * @return int
1207       */
1208      public function get_default_course_enddate($mform, $fieldnames = array()) {
1209  
1210          if (empty($fieldnames)) {
1211              $fieldnames = array('startdate' => 'startdate');
1212          }
1213  
1214          $startdate = $this->get_form_start_date($mform, $fieldnames);
1215          $courseduration = intval(get_config('moodlecourse', 'courseduration'));
1216          if (!$courseduration) {
1217              // Default, it should be already set during upgrade though.
1218              $courseduration = YEARSECS;
1219          }
1220  
1221          return $startdate + $courseduration;
1222      }
1223  
1224      /**
1225       * Indicates whether the course format supports the creation of the Announcements forum.
1226       *
1227       * For course format plugin developers, please override this to return true if you want the Announcements forum
1228       * to be created upon course creation.
1229       *
1230       * @return bool
1231       */
1232      public function supports_news() {
1233          // For backwards compatibility, check if default blocks include the news_items block.
1234          $defaultblocks = $this->get_default_blocks();
1235          foreach ($defaultblocks as $blocks) {
1236              if (in_array('news_items', $blocks)) {
1237                  return true;
1238              }
1239          }
1240          // Return false by default.
1241          return false;
1242      }
1243  
1244      /**
1245       * Get the start date value from the course settings page form.
1246       *
1247       * @param moodleform $mform
1248       * @param array $fieldnames The form - field names mapping.
1249       * @return int
1250       */
1251      protected function get_form_start_date($mform, $fieldnames) {
1252          $startdate = $mform->getElementValue($fieldnames['startdate']);
1253          return $mform->getElement($fieldnames['startdate'])->exportValue($startdate);
1254      }
1255  
1256      /**
1257       * Returns whether this course format allows the activity to
1258       * have "triple visibility state" - visible always, hidden on course page but available, hidden.
1259       *
1260       * @param stdClass|cm_info $cm course module (may be null if we are displaying a form for adding a module)
1261       * @param stdClass|section_info $section section where this module is located or will be added to
1262       * @return bool
1263       */
1264      public function allow_stealth_module_visibility($cm, $section) {
1265          return false;
1266      }
1267  
1268      /**
1269       * Callback used in WS core_course_edit_section when teacher performs an AJAX action on a section (show/hide)
1270       *
1271       * Access to the course is already validated in the WS but the callback has to make sure
1272       * that particular action is allowed by checking capabilities
1273       *
1274       * Course formats should register
1275       *
1276       * @param stdClass|section_info $section
1277       * @param string $action
1278       * @param int $sr
1279       * @return null|array|stdClass any data for the Javascript post-processor (must be json-encodeable)
1280       */
1281      public function section_action($section, $action, $sr) {
1282          global $PAGE;
1283          if (!$this->uses_sections() || !$section->section) {
1284              // No section actions are allowed if course format does not support sections.
1285              // No actions are allowed on the 0-section by default (overwrite in course format if needed).
1286              throw new moodle_exception('sectionactionnotsupported', 'core', null, s($action));
1287          }
1288  
1289          $course = $this->get_course();
1290          $coursecontext = context_course::instance($course->id);
1291          switch($action) {
1292              case 'hide':
1293              case 'show':
1294                  require_capability('moodle/course:sectionvisibility', $coursecontext);
1295                  $visible = ($action === 'hide') ? 0 : 1;
1296                  course_update_section($course, $section, array('visible' => $visible));
1297                  break;
1298              default:
1299                  throw new moodle_exception('sectionactionnotsupported', 'core', null, s($action));
1300          }
1301  
1302          $modules = [];
1303  
1304          $modinfo = get_fast_modinfo($course);
1305          $coursesections = $modinfo->sections;
1306          if (array_key_exists($section->section, $coursesections)) {
1307              $courserenderer = $PAGE->get_renderer('core', 'course');
1308              $completioninfo = new completion_info($course);
1309              foreach ($coursesections[$section->section] as $cmid) {
1310                  $cm = $modinfo->get_cm($cmid);
1311                  $modules[] = $courserenderer->course_section_cm_list_item($course, $completioninfo, $cm, $sr);
1312              }
1313          }
1314  
1315          return ['modules' => $modules];
1316      }
1317  
1318      /**
1319       * Return the plugin config settings for external functions,
1320       * in some cases the configs will need formatting or be returned only if the current user has some capabilities enabled.
1321       *
1322       * @return array the list of configs
1323       * @since Moodle 3.5
1324       */
1325      public function get_config_for_external() {
1326          return array();
1327      }
1328  }
1329  
1330  /**
1331   * Pseudo course format used for the site main page
1332   *
1333   * @package    core_course
1334   * @copyright  2012 Marina Glancy
1335   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1336   */
1337  class format_site extends format_base {
1338  
1339      /**
1340       * Returns the display name of the given section that the course prefers.
1341       *
1342       * @param int|stdClass $section Section object from database or just field section.section
1343       * @return Display name that the course format prefers, e.g. "Topic 2"
1344       */
1345      function get_section_name($section) {
1346          return get_string('site');
1347      }
1348  
1349      /**
1350       * For this fake course referring to the whole site, the site homepage is always returned
1351       * regardless of arguments
1352       *
1353       * @param int|stdClass $section
1354       * @param array $options
1355       * @return null|moodle_url
1356       */
1357      public function get_view_url($section, $options = array()) {
1358          return new moodle_url('/', array('redirect' => 0));
1359      }
1360  
1361      /**
1362       * Returns the list of blocks to be automatically added on the site frontpage when moodle is installed
1363       *
1364       * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
1365       *     each of values is an array of block names (for left and right side columns)
1366       */
1367      public function get_default_blocks() {
1368          return blocks_get_default_site_course_blocks();
1369      }
1370  
1371      /**
1372       * Definitions of the additional options that site uses
1373       *
1374       * @param bool $foreditform
1375       * @return array of options
1376       */
1377      public function course_format_options($foreditform = false) {
1378          static $courseformatoptions = false;
1379          if ($courseformatoptions === false) {
1380              $courseformatoptions = array(
1381                  'numsections' => array(
1382                      'default' => 1,
1383                      'type' => PARAM_INT,
1384                  ),
1385              );
1386          }
1387          return $courseformatoptions;
1388      }
1389  
1390      /**
1391       * Returns whether this course format allows the activity to
1392       * have "triple visibility state" - visible always, hidden on course page but available, hidden.
1393       *
1394       * @param stdClass|cm_info $cm course module (may be null if we are displaying a form for adding a module)
1395       * @param stdClass|section_info $section section where this module is located or will be added to
1396       * @return bool
1397       */
1398      public function allow_stealth_module_visibility($cm, $section) {
1399          return true;
1400      }
1401  }
1402  
1403  /**
1404   * 'Converts' a value from what is stored in the database into what is used by edit forms.
1405   *
1406   * @param array $dest The destination array
1407   * @param array $source The source array
1408   * @param array $option The definition structure of the option.
1409   * @param string $optionname The name of the option, as provided in the definition.
1410   * @author Jason den Dulk
1411   */
1412  function contract_value(array &$dest, array $source, array $option, string $optionname) : void {
1413      if (substr($optionname, -7) == '_editor') { // Suffix '_editor' indicates that the element is an editor.
1414          $name = substr($optionname, 0, -7);
1415          if (isset($source[$name])) {
1416              $dest[$optionname] = [
1417                  'text' => clean_param_if_not_null($source[$name], $option['type'] ?? PARAM_RAW),
1418                  'format' => clean_param_if_not_null($source[$name . 'format'], PARAM_INT),
1419              ];
1420          }
1421      } else {
1422          if (isset($source[$optionname])) {
1423              $dest[$optionname] = clean_param_if_not_null($source[$optionname], $option['type'] ?? PARAM_RAW);
1424          }
1425      }
1426  }
1427  
1428  /**
1429   * Cleans the given param, unless it is null.
1430   *
1431   * @param mixed $param The variable we are cleaning.
1432   * @param string $type Expected format of param after cleaning.
1433   * @return mixed Null if $param is null, otherwise the cleaned value.
1434   * @throws coding_exception
1435   * @author Jason den Dulk
1436   */
1437  function clean_param_if_not_null($param, string $type = PARAM_RAW) {
1438      if ($param === null) {
1439          return null;
1440      } else {
1441          return clean_param($param, $type);
1442      }
1443  }
1444  
1445  /**
1446   * 'Converts' a value from what is used in edit forms into a value(s) to be stored in the database.
1447   *
1448   * @param array $dest The destination array
1449   * @param array $source The source array
1450   * @param array $option The definition structure of the option.
1451   * @param string $optionname The name of the option, as provided in the definition.
1452   * @author Jason den Dulk
1453   */
1454  function expand_value(array &$dest, array $source, array $option, string $optionname) : void {
1455      if (substr($optionname, -7) == '_editor') { // Suffix '_editor' indicates that the element is an editor.
1456          $name = substr($optionname, 0, -7);
1457          if (is_string($source[$optionname])) {
1458              $dest[$name]          = clean_param($source[$optionname], $option['type'] ?? PARAM_RAW);
1459              $dest[$name.'format'] = 1;
1460          } else {
1461              $dest[$name]          = clean_param($source[$optionname]['text'], $option['type'] ?? PARAM_RAW);
1462              $dest[$name.'format'] = clean_param($source[$optionname]['format'], PARAM_INT);
1463          }
1464          unset($dest[$optionname]);
1465      } else {
1466          $dest[$optionname] = clean_param($source[$optionname], $option['type'] ?? PARAM_RAW);
1467      }
1468  }