[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/course/format/ -> lib.php (source)

   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          if (array_key_exists($format, self::$classesforformat)) {
 100              return self::$classesforformat[$format];
 101          }
 102  
 103          $plugins = get_sorted_course_formats();
 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];
 110          }
 111  
 112          if (PHPUNIT_TEST && class_exists('format_' . $format)) {
 113              // Allow unittests to use non-existing course formats.
 114              return $format;
 115          }
 116  
 117          // Else return default format
 118          $defaultformat = get_config('moodlecourse', 'format');
 119          if (!in_array($defaultformat, $plugins)) {
 120              // when default format is not set correctly, use the first available format
 121              $defaultformat = reset($plugins);
 122          }
 123          debugging('Format plugin format_'.$format.' is not found. Using default format_'.$defaultformat, DEBUG_DEVELOPER);
 124  
 125          self::$classesforformat[$format] = $defaultformat;
 126          return $defaultformat;
 127      }
 128  
 129      /**
 130       * Get class name for the format
 131       *
 132       * If course format xxx does not declare class format_xxx, format_legacy will be returned.
 133       * This function also includes lib.php file from corresponding format plugin
 134       *
 135       * @param string $format
 136       * @return string
 137       */
 138      protected static final function get_class_name($format) {
 139          global $CFG;
 140          static $classnames = array('site' => 'format_site');
 141          if (!isset($classnames[$format])) {
 142              $plugins = core_component::get_plugin_list('format');
 143              $usedformat = self::get_format_or_default($format);
 144              if (isset($plugins[$usedformat]) && file_exists($plugins[$usedformat].'/lib.php')) {
 145                  require_once($plugins[$usedformat].'/lib.php');
 146              }
 147              $classnames[$format] = 'format_'. $usedformat;
 148              if (!class_exists($classnames[$format])) {
 149                  require_once($CFG->dirroot.'/course/format/formatlegacy.php');
 150                  $classnames[$format] = 'format_legacy';
 151              }
 152          }
 153          return $classnames[$format];
 154      }
 155  
 156      /**
 157       * Returns an instance of the class
 158       *
 159       * @todo MDL-35727 use MUC for caching of instances, limit the number of cached instances
 160       *
 161       * @param int|stdClass $courseorid either course id or
 162       *     an object that has the property 'format' and may contain property 'id'
 163       * @return format_base
 164       */
 165      public static final function instance($courseorid) {
 166          global $DB;
 167          if (!is_object($courseorid)) {
 168              $courseid = (int)$courseorid;
 169              if ($courseid && isset(self::$instances[$courseid]) && count(self::$instances[$courseid]) == 1) {
 170                  $formats = array_keys(self::$instances[$courseid]);
 171                  $format = reset($formats);
 172              } else {
 173                  $format = $DB->get_field('course', 'format', array('id' => $courseid), MUST_EXIST);
 174              }
 175          } else {
 176              $format = $courseorid->format;
 177              if (isset($courseorid->id)) {
 178                  $courseid = clean_param($courseorid->id, PARAM_INT);
 179              } else {
 180                  $courseid = 0;
 181              }
 182          }
 183          // validate that format exists and enabled, use default otherwise
 184          $format = self::get_format_or_default($format);
 185          if (!isset(self::$instances[$courseid][$format])) {
 186              $classname = self::get_class_name($format);
 187              self::$instances[$courseid][$format] = new $classname($format, $courseid);
 188          }
 189          return self::$instances[$courseid][$format];
 190      }
 191  
 192      /**
 193       * Resets cache for the course (or all caches)
 194       * To be called from {@link rebuild_course_cache()}
 195       *
 196       * @param int $courseid
 197       */
 198      public static final function reset_course_cache($courseid = 0) {
 199          if ($courseid) {
 200              if (isset(self::$instances[$courseid])) {
 201                  foreach (self::$instances[$courseid] as $format => $object) {
 202                      // in case somebody keeps the reference to course format object
 203                      self::$instances[$courseid][$format]->course = false;
 204                      self::$instances[$courseid][$format]->formatoptions = array();
 205                  }
 206                  unset(self::$instances[$courseid]);
 207              }
 208          } else {
 209              self::$instances = array();
 210          }
 211      }
 212  
 213      /**
 214       * Returns the format name used by this course
 215       *
 216       * @return string
 217       */
 218      public final function get_format() {
 219          return $this->format;
 220      }
 221  
 222      /**
 223       * Returns id of the course (0 if course is not specified)
 224       *
 225       * @return int
 226       */
 227      public final function get_courseid() {
 228          return $this->courseid;
 229      }
 230  
 231      /**
 232       * Returns a record from course database table plus additional fields
 233       * that course format defines
 234       *
 235       * @return stdClass
 236       */
 237      public function get_course() {
 238          global $DB;
 239          if (!$this->courseid) {
 240              return null;
 241          }
 242          if ($this->course === false) {
 243              $this->course = get_course($this->courseid);
 244              $options = $this->get_format_options();
 245              $dbcoursecolumns = null;
 246              foreach ($options as $optionname => $optionvalue) {
 247                  if (isset($this->course->$optionname)) {
 248                      // Course format options must not have the same names as existing columns in db table "course".
 249                      if (!isset($dbcoursecolumns)) {
 250                          $dbcoursecolumns = $DB->get_columns('course');
 251                      }
 252                      if (isset($dbcoursecolumns[$optionname])) {
 253                          debugging('The option name '.$optionname.' in course format '.$this->format.
 254                              ' is invalid because the field with the same name exists in {course} table',
 255                              DEBUG_DEVELOPER);
 256                          continue;
 257                      }
 258                  }
 259                  $this->course->$optionname = $optionvalue;
 260              }
 261          }
 262          return $this->course;
 263      }
 264  
 265      /**
 266       * Returns true if the course has a front page.
 267       *
 268       * This function is called to determine if the course has a view page, whether or not
 269       * it contains a listing of activities. It can be useful to set this to false when the course
 270       * format has only one activity and ignores the course page. Or if there are multiple
 271       * activities but no page to see the centralised information.
 272       *
 273       * Initially this was created to know if forms should add a button to return to the course page.
 274       * So if 'Return to course' does not make sense in your format your should probably return false.
 275       *
 276       * @return boolean
 277       * @since Moodle 2.6
 278       */
 279      public function has_view_page() {
 280          return true;
 281      }
 282  
 283      /**
 284       * Returns true if this course format uses sections
 285       *
 286       * This function may be called without specifying the course id
 287       * i.e. in {@link course_format_uses_sections()}
 288       *
 289       * Developers, note that if course format does use sections there should be defined a language
 290       * string with the name 'sectionname' defining what the section relates to in the format, i.e.
 291       * $string['sectionname'] = 'Topic';
 292       * or
 293       * $string['sectionname'] = 'Week';
 294       *
 295       * @return bool
 296       */
 297      public function uses_sections() {
 298          return false;
 299      }
 300  
 301      /**
 302       * Returns a list of sections used in the course
 303       *
 304       * This is a shortcut to get_fast_modinfo()->get_section_info_all()
 305       * @see get_fast_modinfo()
 306       * @see course_modinfo::get_section_info_all()
 307       *
 308       * @return array of section_info objects
 309       */
 310      public final function get_sections() {
 311          if ($course = $this->get_course()) {
 312              $modinfo = get_fast_modinfo($course);
 313              return $modinfo->get_section_info_all();
 314          }
 315          return array();
 316      }
 317  
 318      /**
 319       * Returns information about section used in course
 320       *
 321       * @param int|stdClass $section either section number (field course_section.section) or row from course_section table
 322       * @param int $strictness
 323       * @return section_info
 324       */
 325      public final function get_section($section, $strictness = IGNORE_MISSING) {
 326          if (is_object($section)) {
 327              $sectionnum = $section->section;
 328          } else {
 329              $sectionnum = $section;
 330          }
 331          $sections = $this->get_sections();
 332          if (array_key_exists($sectionnum, $sections)) {
 333              return $sections[$sectionnum];
 334          }
 335          if ($strictness == MUST_EXIST) {
 336              throw new moodle_exception('sectionnotexist');
 337          }
 338          return null;
 339      }
 340  
 341      /**
 342       * Returns the display name of the given section that the course prefers.
 343       *
 344       * @param int|stdClass $section Section object from database or just field course_sections.section
 345       * @return Display name that the course format prefers, e.g. "Topic 2"
 346       */
 347      public function get_section_name($section) {
 348          if (is_object($section)) {
 349              $sectionnum = $section->section;
 350          } else {
 351              $sectionnum = $section;
 352          }
 353  
 354          if (get_string_manager()->string_exists('sectionname', 'format_' . $this->format)) {
 355              return get_string('sectionname', 'format_' . $this->format) . ' ' . $sectionnum;
 356          }
 357  
 358          // Return an empty string if there's no available section name string for the given format.
 359          return '';
 360      }
 361  
 362      /**
 363       * Returns the default section using format_base's implementation of get_section_name.
 364       *
 365       * @param int|stdClass $section Section object from database or just field course_sections section
 366       * @return string The default value for the section name based on the given course format.
 367       */
 368      public function get_default_section_name($section) {
 369          return self::get_section_name($section);
 370      }
 371  
 372      /**
 373       * Returns the information about the ajax support in the given source format
 374       *
 375       * The returned object's property (boolean)capable indicates that
 376       * the course format supports Moodle course ajax features.
 377       *
 378       * @return stdClass
 379       */
 380      public function supports_ajax() {
 381          // no support by default
 382          $ajaxsupport = new stdClass();
 383          $ajaxsupport->capable = false;
 384          return $ajaxsupport;
 385      }
 386  
 387      /**
 388       * Custom action after section has been moved in AJAX mode
 389       *
 390       * Used in course/rest.php
 391       *
 392       * @return array This will be passed in ajax respose
 393       */
 394      public function ajax_section_move() {
 395          return null;
 396      }
 397  
 398      /**
 399       * The URL to use for the specified course (with section)
 400       *
 401       * Please note that course view page /course/view.php?id=COURSEID is hardcoded in many
 402       * places in core and contributed modules. If course format wants to change the location
 403       * of the view script, it is not enough to change just this function. Do not forget
 404       * to add proper redirection.
 405       *
 406       * @param int|stdClass $section Section object from database or just field course_sections.section
 407       *     if null the course view page is returned
 408       * @param array $options options for view URL. At the moment core uses:
 409       *     'navigation' (bool) if true and section has no separate page, the function returns null
 410       *     'sr' (int) used by multipage formats to specify to which section to return
 411       * @return null|moodle_url
 412       */
 413      public function get_view_url($section, $options = array()) {
 414          global $CFG;
 415          $course = $this->get_course();
 416          $url = new moodle_url('/course/view.php', array('id' => $course->id));
 417  
 418          if (array_key_exists('sr', $options)) {
 419              $sectionno = $options['sr'];
 420          } else if (is_object($section)) {
 421              $sectionno = $section->section;
 422          } else {
 423              $sectionno = $section;
 424          }
 425          if (empty($CFG->linkcoursesections) && !empty($options['navigation']) && $sectionno !== null) {
 426              // by default assume that sections are never displayed on separate pages
 427              return null;
 428          }
 429          if ($this->uses_sections() && $sectionno !== null) {
 430              $url->set_anchor('section-'.$sectionno);
 431          }
 432          return $url;
 433      }
 434  
 435      /**
 436       * Loads all of the course sections into the navigation
 437       *
 438       * This method is called from {@link global_navigation::load_course_sections()}
 439       *
 440       * By default the method {@link global_navigation::load_generic_course_sections()} is called
 441       *
 442       * When overwriting please note that navigationlib relies on using the correct values for
 443       * arguments $type and $key in {@link navigation_node::add()}
 444       *
 445       * Example of code creating a section node:
 446       * $sectionnode = $node->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id);
 447       * $sectionnode->nodetype = navigation_node::NODETYPE_BRANCH;
 448       *
 449       * Example of code creating an activity node:
 450       * $activitynode = $sectionnode->add($activityname, $action, navigation_node::TYPE_ACTIVITY, null, $activity->id, $icon);
 451       * if (global_navigation::module_extends_navigation($activity->modname)) {
 452       *     $activitynode->nodetype = navigation_node::NODETYPE_BRANCH;
 453       * } else {
 454       *     $activitynode->nodetype = navigation_node::NODETYPE_LEAF;
 455       * }
 456       *
 457       * Also note that if $navigation->includesectionnum is not null, the section with this relative
 458       * number needs is expected to be loaded
 459       *
 460       * @param global_navigation $navigation
 461       * @param navigation_node $node The course node within the navigation
 462       */
 463      public function extend_course_navigation($navigation, navigation_node $node) {
 464          if ($course = $this->get_course()) {
 465              $navigation->load_generic_course_sections($course, $node);
 466          }
 467          return array();
 468      }
 469  
 470      /**
 471       * Returns the list of blocks to be automatically added for the newly created course
 472       *
 473       * @see blocks_add_default_course_blocks()
 474       *
 475       * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
 476       *     each of values is an array of block names (for left and right side columns)
 477       */
 478      public function get_default_blocks() {
 479          global $CFG;
 480          if (!empty($CFG->defaultblocks)){
 481              return blocks_parse_default_blocks_list($CFG->defaultblocks);
 482          }
 483          $blocknames = array(
 484              BLOCK_POS_LEFT => array(),
 485              BLOCK_POS_RIGHT => array('search_forums', 'news_items', 'calendar_upcoming', 'recent_activity')
 486          );
 487          return $blocknames;
 488      }
 489  
 490      /**
 491       * Returns the localised name of this course format plugin
 492       *
 493       * @return lang_string
 494       */
 495      public final function get_format_name() {
 496          return new lang_string('pluginname', 'format_'.$this->get_format());
 497      }
 498  
 499      /**
 500       * Definitions of the additional options that this course format uses for course
 501       *
 502       * This function may be called often, it should be as fast as possible.
 503       * Avoid using get_string() method, use "new lang_string()" instead
 504       * It is not recommended to use dynamic or course-dependant expressions here
 505       * This function may be also called when course does not exist yet.
 506       *
 507       * Option names must be different from fields in the {course} talbe or any form elements on
 508       * course edit form, it may even make sence to use special prefix for them.
 509       *
 510       * Each option must have the option name as a key and the array of properties as a value:
 511       * 'default' - default value for this option (assumed null if not specified)
 512       * 'type' - type of the option value (PARAM_INT, PARAM_RAW, etc.)
 513       *
 514       * Additional properties used by default implementation of
 515       * {@link format_base::create_edit_form_elements()} (calls this method with $foreditform = true)
 516       * 'label' - localised human-readable label for the edit form
 517       * 'element_type' - type of the form element, default 'text'
 518       * 'element_attributes' - additional attributes for the form element, these are 4th and further
 519       *    arguments in the moodleform::addElement() method
 520       * 'help' - string for help button. Note that if 'help' value is 'myoption' then the string with
 521       *    the name 'myoption_help' must exist in the language file
 522       * 'help_component' - language component to look for help string, by default this the component
 523       *    for this course format
 524       *
 525       * This is an interface for creating simple form elements. If format plugin wants to use other
 526       * methods such as disableIf, it can be done by overriding create_edit_form_elements().
 527       *
 528       * Course format options can be accessed as:
 529       * $this->get_course()->OPTIONNAME (inside the format class)
 530       * course_get_format($course)->get_course()->OPTIONNAME (outside of format class)
 531       *
 532       * All course options are returned by calling:
 533       * $this->get_format_options();
 534       *
 535       * @param bool $foreditform
 536       * @return array of options
 537       */
 538      public function course_format_options($foreditform = false) {
 539          return array();
 540      }
 541  
 542      /**
 543       * Definitions of the additional options that this course format uses for section
 544       *
 545       * See {@link format_base::course_format_options()} for return array definition.
 546       *
 547       * Additionally section format options may have property 'cache' set to true
 548       * if this option needs to be cached in {@link get_fast_modinfo()}. The 'cache' property
 549       * is recommended to be set only for fields used in {@link format_base::get_section_name()},
 550       * {@link format_base::extend_course_navigation()} and {@link format_base::get_view_url()}
 551       *
 552       * For better performance cached options are recommended to have 'cachedefault' property
 553       * Unlike 'default', 'cachedefault' should be static and not access get_config().
 554       *
 555       * Regardless of value of 'cache' all options are accessed in the code as
 556       * $sectioninfo->OPTIONNAME
 557       * where $sectioninfo is instance of section_info, returned by
 558       * get_fast_modinfo($course)->get_section_info($sectionnum)
 559       * or get_fast_modinfo($course)->get_section_info_all()
 560       *
 561       * All format options for particular section are returned by calling:
 562       * $this->get_format_options($section);
 563       *
 564       * @param bool $foreditform
 565       * @return array
 566       */
 567      public function section_format_options($foreditform = false) {
 568          return array();
 569      }
 570  
 571      /**
 572       * Returns the format options stored for this course or course section
 573       *
 574       * When overriding please note that this function is called from rebuild_course_cache()
 575       * and section_info object, therefore using of get_fast_modinfo() and/or any function that
 576       * accesses it may lead to recursion.
 577       *
 578       * @param null|int|stdClass|section_info $section if null the course format options will be returned
 579       *     otherwise options for specified section will be returned. This can be either
 580       *     section object or relative section number (field course_sections.section)
 581       * @return array
 582       */
 583      public function get_format_options($section = null) {
 584          global $DB;
 585          if ($section === null) {
 586              $options = $this->course_format_options();
 587          } else {
 588              $options = $this->section_format_options();
 589          }
 590          if (empty($options)) {
 591              // there are no option for course/sections anyway, no need to go further
 592              return array();
 593          }
 594          if ($section === null) {
 595              // course format options will be returned
 596              $sectionid = 0;
 597          } else if ($this->courseid && isset($section->id)) {
 598              // course section format options will be returned
 599              $sectionid = $section->id;
 600          } else if ($this->courseid && is_int($section) &&
 601                  ($sectionobj = $DB->get_record('course_sections',
 602                          array('section' => $section, 'course' => $this->courseid), 'id'))) {
 603              // course section format options will be returned
 604              $sectionid = $sectionobj->id;
 605          } else {
 606              // non-existing (yet) section was passed as an argument
 607              // default format options for course section will be returned
 608              $sectionid = -1;
 609          }
 610          if (!array_key_exists($sectionid, $this->formatoptions)) {
 611              $this->formatoptions[$sectionid] = array();
 612              // first fill with default values
 613              foreach ($options as $optionname => $optionparams) {
 614                  $this->formatoptions[$sectionid][$optionname] = null;
 615                  if (array_key_exists('default', $optionparams)) {
 616                      $this->formatoptions[$sectionid][$optionname] = $optionparams['default'];
 617                  }
 618              }
 619              if ($this->courseid && $sectionid !== -1) {
 620                  // overwrite the default options values with those stored in course_format_options table
 621                  // nothing can be stored if we are interested in generic course ($this->courseid == 0)
 622                  // or generic section ($sectionid === 0)
 623                  $records = $DB->get_records('course_format_options',
 624                          array('courseid' => $this->courseid,
 625                                'format' => $this->format,
 626                                'sectionid' => $sectionid
 627                              ), '', 'id,name,value');
 628                  foreach ($records as $record) {
 629                      if (array_key_exists($record->name, $this->formatoptions[$sectionid])) {
 630                          $value = $record->value;
 631                          if ($value !== null && isset($options[$record->name]['type'])) {
 632                              // this will convert string value to number if needed
 633                              $value = clean_param($value, $options[$record->name]['type']);
 634                          }
 635                          $this->formatoptions[$sectionid][$record->name] = $value;
 636                      }
 637                  }
 638              }
 639          }
 640          return $this->formatoptions[$sectionid];
 641      }
 642  
 643      /**
 644       * Adds format options elements to the course/section edit form
 645       *
 646       * This function is called from {@link course_edit_form::definition_after_data()}
 647       *
 648       * @param MoodleQuickForm $mform form the elements are added to
 649       * @param bool $forsection 'true' if this is a section edit form, 'false' if this is course edit form
 650       * @return array array of references to the added form elements
 651       */
 652      public function create_edit_form_elements(&$mform, $forsection = false) {
 653          $elements = array();
 654          if ($forsection) {
 655              $options = $this->section_format_options(true);
 656          } else {
 657              $options = $this->course_format_options(true);
 658          }
 659          foreach ($options as $optionname => $option) {
 660              if (!isset($option['element_type'])) {
 661                  $option['element_type'] = 'text';
 662              }
 663              $args = array($option['element_type'], $optionname, $option['label']);
 664              if (!empty($option['element_attributes'])) {
 665                  $args = array_merge($args, $option['element_attributes']);
 666              }
 667              $elements[] = call_user_func_array(array($mform, 'addElement'), $args);
 668              if (isset($option['help'])) {
 669                  $helpcomponent = 'format_'. $this->get_format();
 670                  if (isset($option['help_component'])) {
 671                      $helpcomponent = $option['help_component'];
 672                  }
 673                  $mform->addHelpButton($optionname, $option['help'], $helpcomponent);
 674              }
 675              if (isset($option['type'])) {
 676                  $mform->setType($optionname, $option['type']);
 677              }
 678              if (is_null($mform->getElementValue($optionname)) && isset($option['default'])) {
 679                  $mform->setDefault($optionname, $option['default']);
 680              }
 681          }
 682          return $elements;
 683      }
 684  
 685      /**
 686       * Override if you need to perform some extra validation of the format options
 687       *
 688       * @param array $data array of ("fieldname"=>value) of submitted data
 689       * @param array $files array of uploaded files "element_name"=>tmp_file_path
 690       * @param array $errors errors already discovered in edit form validation
 691       * @return array of "element_name"=>"error_description" if there are errors,
 692       *         or an empty array if everything is OK.
 693       *         Do not repeat errors from $errors param here
 694       */
 695      public function edit_form_validation($data, $files, $errors) {
 696          return array();
 697      }
 698  
 699      /**
 700       * Updates format options for a course or section
 701       *
 702       * If $data does not contain property with the option name, the option will not be updated
 703       *
 704       * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
 705       * @param null|int null if these are options for course or section id (course_sections.id)
 706       *     if these are options for section
 707       * @return bool whether there were any changes to the options values
 708       */
 709      protected function update_format_options($data, $sectionid = null) {
 710          global $DB;
 711          if (!$sectionid) {
 712              $allformatoptions = $this->course_format_options();
 713              $sectionid = 0;
 714          } else {
 715              $allformatoptions = $this->section_format_options();
 716          }
 717          if (empty($allformatoptions)) {
 718              // nothing to update anyway
 719              return false;
 720          }
 721          $defaultoptions = array();
 722          $cached = array();
 723          foreach ($allformatoptions as $key => $option) {
 724              $defaultoptions[$key] = null;
 725              if (array_key_exists('default', $option)) {
 726                  $defaultoptions[$key] = $option['default'];
 727              }
 728              $cached[$key] = ($sectionid === 0 || !empty($option['cache']));
 729          }
 730          $records = $DB->get_records('course_format_options',
 731                  array('courseid' => $this->courseid,
 732                        'format' => $this->format,
 733                        'sectionid' => $sectionid
 734                      ), '', 'name,id,value');
 735          $changed = $needrebuild = false;
 736          $data = (array)$data;
 737          foreach ($defaultoptions as $key => $value) {
 738              if (isset($records[$key])) {
 739                  if (array_key_exists($key, $data) && $records[$key]->value !== $data[$key]) {
 740                      $DB->set_field('course_format_options', 'value',
 741                              $data[$key], array('id' => $records[$key]->id));
 742                      $changed = true;
 743                      $needrebuild = $needrebuild || $cached[$key];
 744                  }
 745              } else {
 746                  if (array_key_exists($key, $data) && $data[$key] !== $value) {
 747                      $newvalue = $data[$key];
 748                      $changed = true;
 749                      $needrebuild = $needrebuild || $cached[$key];
 750                  } else {
 751                      $newvalue = $value;
 752                      // we still insert entry in DB but there are no changes from user point of
 753                      // view and no need to call rebuild_course_cache()
 754                  }
 755                  $DB->insert_record('course_format_options', array(
 756                      'courseid' => $this->courseid,
 757                      'format' => $this->format,
 758                      'sectionid' => $sectionid,
 759                      'name' => $key,
 760                      'value' => $newvalue
 761                  ));
 762              }
 763          }
 764          if ($needrebuild) {
 765              rebuild_course_cache($this->courseid, true);
 766          }
 767          if ($changed) {
 768              // reset internal caches
 769              if (!$sectionid) {
 770                  $this->course = false;
 771              }
 772              unset($this->formatoptions[$sectionid]);
 773          }
 774          return $changed;
 775      }
 776  
 777      /**
 778       * Updates format options for a course
 779       *
 780       * If $data does not contain property with the option name, the option will not be updated
 781       *
 782       * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
 783       * @param stdClass $oldcourse if this function is called from {@link update_course()}
 784       *     this object contains information about the course before update
 785       * @return bool whether there were any changes to the options values
 786       */
 787      public function update_course_format_options($data, $oldcourse = null) {
 788          return $this->update_format_options($data);
 789      }
 790  
 791      /**
 792       * Updates format options for a section
 793       *
 794       * Section id is expected in $data->id (or $data['id'])
 795       * If $data does not contain property with the option name, the option will not be updated
 796       *
 797       * @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data
 798       * @return bool whether there were any changes to the options values
 799       */
 800      public function update_section_format_options($data) {
 801          $data = (array)$data;
 802          return $this->update_format_options($data, $data['id']);
 803      }
 804  
 805      /**
 806       * Return an instance of moodleform to edit a specified section
 807       *
 808       * Default implementation returns instance of editsection_form that automatically adds
 809       * additional fields defined in {@link format_base::section_format_options()}
 810       *
 811       * Format plugins may extend editsection_form if they want to have custom edit section form.
 812       *
 813       * @param mixed $action the action attribute for the form. If empty defaults to auto detect the
 814       *              current url. If a moodle_url object then outputs params as hidden variables.
 815       * @param array $customdata the array with custom data to be passed to the form
 816       *     /course/editsection.php passes section_info object in 'cs' field
 817       *     for filling availability fields
 818       * @return moodleform
 819       */
 820      public function editsection_form($action, $customdata = array()) {
 821          global $CFG;
 822          require_once($CFG->dirroot. '/course/editsection_form.php');
 823          $context = context_course::instance($this->courseid);
 824          if (!array_key_exists('course', $customdata)) {
 825              $customdata['course'] = $this->get_course();
 826          }
 827          return new editsection_form($action, $customdata);
 828      }
 829  
 830      /**
 831       * Allows course format to execute code on moodle_page::set_course()
 832       *
 833       * @param moodle_page $page instance of page calling set_course
 834       */
 835      public function page_set_course(moodle_page $page) {
 836      }
 837  
 838      /**
 839       * Allows course format to execute code on moodle_page::set_cm()
 840       *
 841       * Current module can be accessed as $page->cm (returns instance of cm_info)
 842       *
 843       * @param moodle_page $page instance of page calling set_cm
 844       */
 845      public function page_set_cm(moodle_page $page) {
 846      }
 847  
 848      /**
 849       * Course-specific information to be output on any course page (usually above navigation bar)
 850       *
 851       * Example of usage:
 852       * define
 853       * class format_FORMATNAME_XXX implements renderable {}
 854       *
 855       * create format renderer in course/format/FORMATNAME/renderer.php, define rendering function:
 856       * class format_FORMATNAME_renderer extends plugin_renderer_base {
 857       *     protected function render_format_FORMATNAME_XXX(format_FORMATNAME_XXX $xxx) {
 858       *         return html_writer::tag('div', 'This is my header/footer');
 859       *     }
 860       * }
 861       *
 862       * Return instance of format_FORMATNAME_XXX in this function, the appropriate method from
 863       * plugin renderer will be called
 864       *
 865       * @return null|renderable null for no output or object with data for plugin renderer
 866       */
 867      public function course_header() {
 868          return null;
 869      }
 870  
 871      /**
 872       * Course-specific information to be output on any course page (usually in the beginning of
 873       * standard footer)
 874       *
 875       * See {@link format_base::course_header()} for usage
 876       *
 877       * @return null|renderable null for no output or object with data for plugin renderer
 878       */
 879      public function course_footer() {
 880          return null;
 881      }
 882  
 883      /**
 884       * Course-specific information to be output immediately above content on any course page
 885       *
 886       * See {@link format_base::course_header()} for usage
 887       *
 888       * @return null|renderable null for no output or object with data for plugin renderer
 889       */
 890      public function course_content_header() {
 891          return null;
 892      }
 893  
 894      /**
 895       * Course-specific information to be output immediately below content on any course page
 896       *
 897       * See {@link format_base::course_header()} for usage
 898       *
 899       * @return null|renderable null for no output or object with data for plugin renderer
 900       */
 901      public function course_content_footer() {
 902          return null;
 903      }
 904  
 905      /**
 906       * Returns instance of page renderer used by this plugin
 907       *
 908       * @param moodle_page $page
 909       * @return renderer_base
 910       */
 911      public function get_renderer(moodle_page $page) {
 912          return $page->get_renderer('format_'. $this->get_format());
 913      }
 914  
 915      /**
 916       * Returns true if the specified section is current
 917       *
 918       * By default we analyze $course->marker
 919       *
 920       * @param int|stdClass|section_info $section
 921       * @return bool
 922       */
 923      public function is_section_current($section) {
 924          if (is_object($section)) {
 925              $sectionnum = $section->section;
 926          } else {
 927              $sectionnum = $section;
 928          }
 929          return ($sectionnum && ($course = $this->get_course()) && $course->marker == $sectionnum);
 930      }
 931  
 932      /**
 933       * Allows to specify for modinfo that section is not available even when it is visible and conditionally available.
 934       *
 935       * Note: affected user can be retrieved as: $section->modinfo->userid
 936       *
 937       * Course format plugins can override the method to change the properties $available and $availableinfo that were
 938       * calculated by conditional availability.
 939       * To make section unavailable set:
 940       *     $available = false;
 941       * To make unavailable section completely hidden set:
 942       *     $availableinfo = '';
 943       * To make unavailable section visible with availability message set:
 944       *     $availableinfo = get_string('sectionhidden', 'format_xxx');
 945       *
 946       * @param section_info $section
 947       * @param bool $available the 'available' propery of the section_info as it was evaluated by conditional availability.
 948       *     Can be changed by the method but 'false' can not be overridden by 'true'.
 949       * @param string $availableinfo the 'availableinfo' propery of the section_info as it was evaluated by conditional availability.
 950       *     Can be changed by the method
 951       */
 952      public function section_get_available_hook(section_info $section, &$available, &$availableinfo) {
 953      }
 954  
 955      /**
 956       * Whether this format allows to delete sections
 957       *
 958       * If format supports deleting sections it is also recommended to define language string
 959       * 'deletesection' inside the format.
 960       *
 961       * Do not call this function directly, instead use {@link course_can_delete_section()}
 962       *
 963       * @param int|stdClass|section_info $section
 964       * @return bool
 965       */
 966      public function can_delete_section($section) {
 967          return false;
 968      }
 969  
 970      /**
 971       * Deletes a section
 972       *
 973       * Do not call this function directly, instead call {@link course_delete_section()}
 974       *
 975       * @param int|stdClass|section_info $section
 976       * @param bool $forcedeleteifnotempty if set to false section will not be deleted if it has modules in it.
 977       * @return bool whether section was deleted
 978       */
 979      public function delete_section($section, $forcedeleteifnotempty = false) {
 980          global $DB;
 981          if (!$this->uses_sections()) {
 982              // Not possible to delete section if sections are not used.
 983              return false;
 984          }
 985          if (!is_object($section)) {
 986              $section = $DB->get_record('course_sections', array('course' => $this->get_courseid(), 'section' => $section),
 987                  'id,section,sequence,summary');
 988          }
 989          if (!$section || !$section->section) {
 990              // Not possible to delete 0-section.
 991              return false;
 992          }
 993  
 994          if (!$forcedeleteifnotempty && (!empty($section->sequence) || !empty($section->summary))) {
 995              return false;
 996          }
 997  
 998          $course = $this->get_course();
 999  
1000          // Remove the marker if it points to this section.
1001          if ($section->section == $course->marker) {
1002              course_set_marker($course->id, 0);
1003          }
1004  
1005          $lastsection = $DB->get_field_sql('SELECT max(section) from {course_sections}
1006                              WHERE course = ?', array($course->id));
1007  
1008          // Find out if we need to descrease the 'numsections' property later.
1009          $courseformathasnumsections = array_key_exists('numsections',
1010              $this->get_format_options());
1011          $decreasenumsections = $courseformathasnumsections && ($section->section <= $course->numsections);
1012  
1013          // Move the section to the end.
1014          move_section_to($course, $section->section, $lastsection, true);
1015  
1016          // Delete all modules from the section.
1017          foreach (preg_split('/,/', $section->sequence, -1, PREG_SPLIT_NO_EMPTY) as $cmid) {
1018              course_delete_module($cmid);
1019          }
1020  
1021          // Delete section and it's format options.
1022          $DB->delete_records('course_format_options', array('sectionid' => $section->id));
1023          $DB->delete_records('course_sections', array('id' => $section->id));
1024          rebuild_course_cache($course->id, true);
1025  
1026          // Descrease 'numsections' if needed.
1027          if ($decreasenumsections) {
1028              $this->update_course_format_options(array('numsections' => $course->numsections - 1));
1029          }
1030  
1031          return true;
1032      }
1033  
1034      /**
1035       * Prepares the templateable object to display section name
1036       *
1037       * @param \section_info|\stdClass $section
1038       * @param bool $linkifneeded
1039       * @param bool $editable
1040       * @param null|lang_string|string $edithint
1041       * @param null|lang_string|string $editlabel
1042       * @return \core\output\inplace_editable
1043       */
1044      public function inplace_editable_render_section_name($section, $linkifneeded = true,
1045                                                           $editable = null, $edithint = null, $editlabel = null) {
1046          global $USER, $CFG;
1047          require_once($CFG->dirroot.'/course/lib.php');
1048  
1049          if ($editable === null) {
1050              $editable = !empty($USER->editing) && has_capability('moodle/course:update',
1051                      context_course::instance($section->course));
1052          }
1053  
1054          $displayvalue = $title = get_section_name($section->course, $section);
1055          if ($linkifneeded) {
1056              // Display link under the section name if the course format setting is to display one section per page.
1057              $url = course_get_url($section->course, $section->section, array('navigation' => true));
1058              if ($url) {
1059                  $displayvalue = html_writer::link($url, $title);
1060              }
1061              $itemtype = 'sectionname';
1062          } else {
1063              // If $linkifneeded==false, we never display the link (this is used when rendering the section header).
1064              // Itemtype 'sectionnamenl' (nl=no link) will tell the callback that link should not be rendered -
1065              // there is no other way callback can know where we display the section name.
1066              $itemtype = 'sectionnamenl';
1067          }
1068          if (empty($edithint)) {
1069              $edithint = new lang_string('editsectionname');
1070          }
1071          if (empty($editlabel)) {
1072              $editlabel = new lang_string('newsectionname', '', $title);
1073          }
1074  
1075          return new \core\output\inplace_editable('format_' . $this->format, $itemtype, $section->id, $editable,
1076              $displayvalue, $section->name, $edithint, $editlabel);
1077      }
1078  
1079      /**
1080       * Updates the value in the database and modifies this object respectively.
1081       *
1082       * ALWAYS check user permissions before performing an update! Throw exceptions if permissions are not sufficient
1083       * or value is not legit.
1084       *
1085       * @param stdClass $section
1086       * @param string $itemtype
1087       * @param mixed $newvalue
1088       * @return \core\output\inplace_editable
1089       */
1090      public function inplace_editable_update_section_name($section, $itemtype, $newvalue) {
1091          if ($itemtype === 'sectionname' || $itemtype === 'sectionnamenl') {
1092              $context = context_course::instance($section->course);
1093              external_api::validate_context($context);
1094              require_capability('moodle/course:update', $context);
1095  
1096              $newtitle = clean_param($newvalue, PARAM_TEXT);
1097              if (strval($section->name) !== strval($newtitle)) {
1098                  course_update_section($section->course, $section, array('name' => $newtitle));
1099              }
1100              return $this->inplace_editable_render_section_name($section, ($itemtype === 'sectionname'), true);
1101          }
1102      }
1103  }
1104  
1105  /**
1106   * Pseudo course format used for the site main page
1107   *
1108   * @package    core_course
1109   * @copyright  2012 Marina Glancy
1110   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1111   */
1112  class format_site extends format_base {
1113  
1114      /**
1115       * Returns the display name of the given section that the course prefers.
1116       *
1117       * @param int|stdClass $section Section object from database or just field section.section
1118       * @return Display name that the course format prefers, e.g. "Topic 2"
1119       */
1120      function get_section_name($section) {
1121          return get_string('site');
1122      }
1123  
1124      /**
1125       * For this fake course referring to the whole site, the site homepage is always returned
1126       * regardless of arguments
1127       *
1128       * @param int|stdClass $section
1129       * @param array $options
1130       * @return null|moodle_url
1131       */
1132      public function get_view_url($section, $options = array()) {
1133          return new moodle_url('/', array('redirect' => 0));
1134      }
1135  
1136      /**
1137       * Returns the list of blocks to be automatically added on the site frontpage when moodle is installed
1138       *
1139       * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
1140       *     each of values is an array of block names (for left and right side columns)
1141       */
1142      public function get_default_blocks() {
1143          return blocks_get_default_site_course_blocks();
1144      }
1145  
1146      /**
1147       * Definitions of the additional options that site uses
1148       *
1149       * @param bool $foreditform
1150       * @return array of options
1151       */
1152      public function course_format_options($foreditform = false) {
1153          static $courseformatoptions = false;
1154          if ($courseformatoptions === false) {
1155              $courseformatoptions = array(
1156                  'numsections' => array(
1157                      'default' => 1,
1158                      'type' => PARAM_INT,
1159                  ),
1160              );
1161          }
1162          return $courseformatoptions;
1163      }
1164  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1