[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |