From 1c839f9072d7bc2ff7b7bbf4a8b3c91249ce364e Mon Sep 17 00:00:00 2001 From: Ferran Recio Date: Thu, 22 Oct 2020 09:18:59 +0200 Subject: [PATCH] MDL-65974 course: move format_base to core_course\course_format --- .../tests/moodle2_course_format_test.php | 8 +- course/classes/course_format.php | 1416 +++++++++++++++++ course/format/formatlegacy.php | 2 +- course/format/singleactivity/lib.php | 2 +- course/format/social/lib.php | 2 +- course/format/weeks/lib.php | 2 +- course/lib.php | 6 +- course/tests/courseformat_test.php | 4 +- lib/adminlib.php | 8 +- lib/behat/classes/util.php | 5 +- lib/db/renamedclasses.php | 1 + lib/deprecatedlib.php | 3 +- lib/modinfolib.php | 5 +- lib/moodlelib.php | 4 +- lib/navigationlib.php | 2 +- lib/phpunit/classes/util.php | 5 +- 16 files changed, 1440 insertions(+), 35 deletions(-) create mode 100644 course/classes/course_format.php diff --git a/backup/moodle2/tests/moodle2_course_format_test.php b/backup/moodle2/tests/moodle2_course_format_test.php index bac585934bf..9ca25197dff 100644 --- a/backup/moodle2/tests/moodle2_course_format_test.php +++ b/backup/moodle2/tests/moodle2_course_format_test.php @@ -58,7 +58,7 @@ class core_backup_moodle2_course_format_testcase extends advanced_testcase { 'enablecompletion' => COMPLETION_ENABLED), array('createsections' => true)); - $courseobject = format_base::instance($course->id); + $courseobject = core_course\course_format::instance($course->id); $section = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1), '*', MUST_EXIST); $data = array('id' => $section->id, @@ -91,7 +91,7 @@ class core_backup_moodle2_course_format_testcase extends advanced_testcase { 'enablecompletion' => COMPLETION_ENABLED), array('createsections' => true)); - $courseobject = format_base::instance($course->id); + $courseobject = core_course\course_format::instance($course->id); $section = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1), '*', MUST_EXIST); $data = array('id' => $section->id, @@ -134,7 +134,7 @@ class core_backup_moodle2_course_format_testcase extends advanced_testcase { array('createsections' => true)); // Set section 2 to have both options, and a name. - $courseobject = format_base::instance($course->id); + $courseobject = core_course\course_format::instance($course->id); $section = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 2), '*', MUST_EXIST); $data = array('id' => $section->id, @@ -151,7 +151,7 @@ class core_backup_moodle2_course_format_testcase extends advanced_testcase { // Check that the section contains the options suitable for the new // format and that even the one with the same name as from the old format // has NOT been set. - $newcourseobject = format_base::instance($newcourse->id); + $newcourseobject = core_course\course_format::instance($newcourse->id); $sectionoptions = $newcourseobject->get_format_options(2); $this->assertArrayHasKey('numdaystocomplete', $sectionoptions); $this->assertArrayNotHasKey('secondparameter', $sectionoptions); diff --git a/course/classes/course_format.php b/course/classes/course_format.php new file mode 100644 index 00000000000..debd894f51b --- /dev/null +++ b/course/classes/course_format.php @@ -0,0 +1,1416 @@ +. + +/** + * Contains the default activity list from a section. + * + * @package core_course + * @copyright 2020 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_course; + +use navigation_node; +use moodle_page; +use core_component; +use course_modinfo; +use html_writer; +use section_info; +use context_course; +use editsection_form; +use moodle_exception; +use moodle_url; +use lang_string; +use completion_info; +use external_api; +use stdClass; + +/** + * Base class for course formats + * + * Each course format must declare class + * class format_FORMATNAME extends core_course\course_format {} + * in file lib.php + * + * For each course just one instance of this class is created and it will always be returned by + * course_get_format($courseorid). Format may store it's specific course-dependent options in + * variables of this class. + * + * In rare cases instance of child class may be created just for format without course id + * i.e. to check if format supports AJAX. + * + * Also course formats may extend class section_info and overwrite + * course_format::build_section_cache() to return more information about sections. + * + * If you are upgrading from Moodle 2.3 start with copying the class format_legacy and renaming + * it to format_FORMATNAME, then move the code from your callback functions into + * appropriate functions of the class. + * + * @package core_course + * @copyright 2012 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class course_format { + /** @var int Id of the course in this instance (maybe 0) */ + protected $courseid; + /** @var string format used for this course. Please note that it can be different from + * course.format field if course referes to non-existing of disabled format */ + protected $format; + /** @var stdClass course data for course object, please use course_format::get_course() */ + protected $course = false; + /** @var array caches format options, please use course_format::get_format_options() */ + protected $formatoptions = array(); + /** @var int the section number in single section format, zero for multiple section formats. */ + protected $singlesection = 0; + /** @var course_modinfo the current course modinfo, please use course_format::get_modinfo() */ + private $modinfo = null; + /** @var array cached instances */ + private static $instances = array(); + /** @var array plugin name => class name. */ + private static $classesforformat = array('site' => 'site'); + + /** + * Creates a new instance of class + * + * Please use course_get_format($courseorid) to get an instance of the format class + * + * @param string $format + * @param int $courseid + * @return course_format + */ + protected function __construct($format, $courseid) { + $this->format = $format; + $this->courseid = $courseid; + } + + /** + * Validates that course format exists and enabled and returns either itself or default format + * + * @param string $format + * @return string + */ + protected static final function get_format_or_default($format) { + global $CFG; + require_once($CFG->dirroot . '/course/lib.php'); + + if (array_key_exists($format, self::$classesforformat)) { + return self::$classesforformat[$format]; + } + + $plugins = get_sorted_course_formats(); + foreach ($plugins as $plugin) { + self::$classesforformat[$plugin] = $plugin; + } + + if (array_key_exists($format, self::$classesforformat)) { + return self::$classesforformat[$format]; + } + + if (PHPUNIT_TEST && class_exists('format_' . $format)) { + // Allow unittests to use non-existing course formats. + return $format; + } + + // Else return default format. + $defaultformat = get_config('moodlecourse', 'format'); + if (!in_array($defaultformat, $plugins)) { + // When default format is not set correctly, use the first available format. + $defaultformat = reset($plugins); + } + debugging('Format plugin format_'.$format.' is not found. Using default format_'.$defaultformat, DEBUG_DEVELOPER); + + self::$classesforformat[$format] = $defaultformat; + return $defaultformat; + } + + /** + * Get class name for the format. + * + * If course format xxx does not declare class format_xxx, format_legacy will be returned. + * This function also includes lib.php file from corresponding format plugin + * + * @param string $format + * @return string + */ + protected static final function get_class_name($format) { + global $CFG; + static $classnames = array('site' => 'format_site'); + if (!isset($classnames[$format])) { + $plugins = core_component::get_plugin_list('format'); + $usedformat = self::get_format_or_default($format); + if (isset($plugins[$usedformat]) && file_exists($plugins[$usedformat].'/lib.php')) { + require_once($plugins[$usedformat].'/lib.php'); + } + $classnames[$format] = 'format_'. $usedformat; + if (!class_exists($classnames[$format])) { + require_once($CFG->dirroot.'/course/format/formatlegacy.php'); + $classnames[$format] = 'format_legacy'; + } + } + return $classnames[$format]; + } + + /** + * Returns an instance of the class + * + * @todo MDL-35727 use MUC for caching of instances, limit the number of cached instances + * + * @param int|stdClass $courseorid either course id or + * an object that has the property 'format' and may contain property 'id' + * @return course_format + */ + public static final function instance($courseorid) { + global $DB; + if (!is_object($courseorid)) { + $courseid = (int)$courseorid; + if ($courseid && isset(self::$instances[$courseid]) && count(self::$instances[$courseid]) == 1) { + $formats = array_keys(self::$instances[$courseid]); + $format = reset($formats); + } else { + $format = $DB->get_field('course', 'format', array('id' => $courseid), MUST_EXIST); + } + } else { + $format = $courseorid->format; + if (isset($courseorid->id)) { + $courseid = clean_param($courseorid->id, PARAM_INT); + } else { + $courseid = 0; + } + } + // Validate that format exists and enabled, use default otherwise. + $format = self::get_format_or_default($format); + if (!isset(self::$instances[$courseid][$format])) { + $classname = self::get_class_name($format); + self::$instances[$courseid][$format] = new $classname($format, $courseid); + } + return self::$instances[$courseid][$format]; + } + + /** + * Resets cache for the course (or all caches) + * + * To be called from rebuild_course_cache() + * + * @param int $courseid + */ + public static final function reset_course_cache($courseid = 0) { + if ($courseid) { + if (isset(self::$instances[$courseid])) { + foreach (self::$instances[$courseid] as $format => $object) { + // In case somebody keeps the reference to course format object. + self::$instances[$courseid][$format]->course = false; + self::$instances[$courseid][$format]->formatoptions = array(); + } + unset(self::$instances[$courseid]); + } + } else { + self::$instances = array(); + } + } + + /** + * Returns the format name used by this course + * + * @return string + */ + public final function get_format() { + return $this->format; + } + + /** + * Returns id of the course (0 if course is not specified) + * + * @return int + */ + public final function get_courseid() { + return $this->courseid; + } + + /** + * Returns a record from course database table plus additional fields + * that course format defines + * + * @return stdClass + */ + public function get_course() { + global $DB; + if (!$this->courseid) { + return null; + } + if ($this->course === false) { + $this->course = get_course($this->courseid); + $options = $this->get_format_options(); + $dbcoursecolumns = null; + foreach ($options as $optionname => $optionvalue) { + if (isset($this->course->$optionname)) { + // Course format options must not have the same names as existing columns in db table "course". + if (!isset($dbcoursecolumns)) { + $dbcoursecolumns = $DB->get_columns('course'); + } + if (isset($dbcoursecolumns[$optionname])) { + debugging('The option name '.$optionname.' in course format '.$this->format. + ' is invalid because the field with the same name exists in {course} table', + DEBUG_DEVELOPER); + continue; + } + } + $this->course->$optionname = $optionvalue; + } + } + return $this->course; + } + + /** + * Return the current course modinfo. + * + * This method is used mainly by the output components to avoid unnecesary get_fast_modinfo calls. + * + * @return course_modinfo + */ + public function get_modinfo(): course_modinfo { + global $USER; + if ($this->modinfo === null) { + $this->modinfo = course_modinfo::instance($this->courseid, $USER->id); + } + return $this->modinfo; + } + + /** + * Method used in the rendered and during backup instead of legacy 'numsections' + * + * Default renderer will treat sections with sectionnumber greater that the value returned by this + * method as "orphaned" and not display them on the course page unless in editing mode. + * Backup will store this value as 'numsections'. + * + * This method ensures that 3rd party course format plugins that still use 'numsections' continue to + * work but at the same time we no longer expect formats to have 'numsections' property. + * + * @return int + */ + public function get_last_section_number() { + $course = $this->get_course(); + if (isset($course->numsections)) { + return $course->numsections; + } + $modinfo = get_fast_modinfo($course); + $sections = $modinfo->get_section_info_all(); + return (int)max(array_keys($sections)); + } + + /** + * Method used to get the maximum number of sections for this course format. + * @return int + */ + public function get_max_sections() { + $maxsections = get_config('moodlecourse', 'maxsections'); + if (!isset($maxsections) || !is_numeric($maxsections)) { + $maxsections = 52; + } + return $maxsections; + } + + /** + * Returns true if the course has a front page. + * + * This function is called to determine if the course has a view page, whether or not + * it contains a listing of activities. It can be useful to set this to false when the course + * format has only one activity and ignores the course page. Or if there are multiple + * activities but no page to see the centralised information. + * + * Initially this was created to know if forms should add a button to return to the course page. + * So if 'Return to course' does not make sense in your format your should probably return false. + * + * @return boolean + * @since Moodle 2.6 + */ + public function has_view_page() { + return true; + } + + /** + * Generate the title for this section page. + * + * @return string the page title + */ + public function page_title(): string { + global $PAGE; + return $PAGE->title; + } + + /** + * Returns true if this course format uses sections + * + * This function may be called without specifying the course id + * i.e. in course_format_uses_sections() + * + * Developers, note that if course format does use sections there should be defined a language + * string with the name 'sectionname' defining what the section relates to in the format, i.e. + * $string['sectionname'] = 'Topic'; + * or + * $string['sectionname'] = 'Week'; + * + * @return bool + */ + public function uses_sections() { + return false; + } + + /** + * Returns a list of sections used in the course + * + * This is a shortcut to get_fast_modinfo()->get_section_info_all() + * @see get_fast_modinfo() + * @see course_modinfo::get_section_info_all() + * + * @return array of section_info objects + */ + public final function get_sections() { + if ($course = $this->get_course()) { + $modinfo = get_fast_modinfo($course); + return $modinfo->get_section_info_all(); + } + return array(); + } + + /** + * Returns information about section used in course + * + * @param int|stdClass $section either section number (field course_section.section) or row from course_section table + * @param int $strictness + * @return section_info + */ + public final function get_section($section, $strictness = IGNORE_MISSING) { + if (is_object($section)) { + $sectionnum = $section->section; + } else { + $sectionnum = $section; + } + $sections = $this->get_sections(); + if (array_key_exists($sectionnum, $sections)) { + return $sections[$sectionnum]; + } + if ($strictness == MUST_EXIST) { + throw new moodle_exception('sectionnotexist'); + } + return null; + } + + /** + * Returns the display name of the given section that the course prefers. + * + * @param int|stdClass $section Section object from database or just field course_sections.section + * @return Display name that the course format prefers, e.g. "Topic 2" + */ + public function get_section_name($section) { + if (is_object($section)) { + $sectionnum = $section->section; + } else { + $sectionnum = $section; + } + + if (get_string_manager()->string_exists('sectionname', 'format_' . $this->format)) { + return get_string('sectionname', 'format_' . $this->format) . ' ' . $sectionnum; + } + + // Return an empty string if there's no available section name string for the given format. + return ''; + } + + /** + * Returns the default section using course_format's implementation of get_section_name. + * + * @param int|stdClass $section Section object from database or just field course_sections section + * @return string The default value for the section name based on the given course format. + */ + public function get_default_section_name($section) { + return self::get_section_name($section); + } + + /** + * Set if the current format instance will show multiple sections or an individual one. + * + * Some formats has the hability to swith from one section to multiple sections per page, + * this method replaces the old print_multiple_section_page and print_single_section_page. + * + * @param int $singlesection zero for all sections or a section number + */ + public function set_section_number(int $singlesection): void { + $this->singlesection = $singlesection; + } + + /** + * Set if the current format instance will show multiple sections or an individual one. + * + * Some formats has the hability to swith from one section to multiple sections per page, + * output components will use this method to know if the current display is a single or + * multiple sections. + * + * @return int zero for all sections or the sectin number + */ + public function get_section_number(): int { + return $this->singlesection; + } + + /** + * Returns the information about the ajax support in the given source format + * + * The returned object's property (boolean)capable indicates that + * the course format supports Moodle course ajax features. + * + * @return stdClass + */ + public function supports_ajax() { + // No support by default. + $ajaxsupport = new stdClass(); + $ajaxsupport->capable = false; + return $ajaxsupport; + } + + /** + * Custom action after section has been moved in AJAX mode + * + * Used in course/rest.php + * + * @return array This will be passed in ajax respose + */ + public function ajax_section_move() { + return null; + } + + /** + * The URL to use for the specified course (with section) + * + * Please note that course view page /course/view.php?id=COURSEID is hardcoded in many + * places in core and contributed modules. If course format wants to change the location + * of the view script, it is not enough to change just this function. Do not forget + * to add proper redirection. + * + * @param int|stdClass $section Section object from database or just field course_sections.section + * if null the course view page is returned + * @param array $options options for view URL. At the moment core uses: + * 'navigation' (bool) if true and section has no separate page, the function returns null + * 'sr' (int) used by multipage formats to specify to which section to return + * @return null|moodle_url + */ + public function get_view_url($section, $options = array()) { + global $CFG; + $course = $this->get_course(); + $url = new moodle_url('/course/view.php', array('id' => $course->id)); + + if (array_key_exists('sr', $options)) { + $sectionno = $options['sr']; + } else if (is_object($section)) { + $sectionno = $section->section; + } else { + $sectionno = $section; + } + if (empty($CFG->linkcoursesections) && !empty($options['navigation']) && $sectionno !== null) { + // By default assume that sections are never displayed on separate pages. + return null; + } + if ($this->uses_sections() && $sectionno !== null) { + $url->set_anchor('section-'.$sectionno); + } + return $url; + } + + /** + * Loads all of the course sections into the navigation + * + * This method is called from global_navigation::load_course_sections() + * + * By default the method global_navigation::load_generic_course_sections() is called + * + * When overwriting please note that navigationlib relies on using the correct values for + * arguments $type and $key in navigation_node::add() + * + * Example of code creating a section node: + * $sectionnode = $node->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id); + * $sectionnode->nodetype = navigation_node::NODETYPE_BRANCH; + * + * Example of code creating an activity node: + * $activitynode = $sectionnode->add($activityname, $action, navigation_node::TYPE_ACTIVITY, null, $activity->id, $icon); + * if (global_navigation::module_extends_navigation($activity->modname)) { + * $activitynode->nodetype = navigation_node::NODETYPE_BRANCH; + * } else { + * $activitynode->nodetype = navigation_node::NODETYPE_LEAF; + * } + * + * Also note that if $navigation->includesectionnum is not null, the section with this relative + * number needs is expected to be loaded + * + * @param global_navigation $navigation + * @param navigation_node $node The course node within the navigation + */ + public function extend_course_navigation($navigation, navigation_node $node) { + if ($course = $this->get_course()) { + $navigation->load_generic_course_sections($course, $node); + } + return array(); + } + + /** + * Returns the list of blocks to be automatically added for the newly created course + * + * @see blocks_add_default_course_blocks() + * + * @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT + * each of values is an array of block names (for left and right side columns) + */ + public function get_default_blocks() { + global $CFG; + if (isset($CFG->defaultblocks)) { + return blocks_parse_default_blocks_list($CFG->defaultblocks); + } + $blocknames = array( + BLOCK_POS_LEFT => array(), + BLOCK_POS_RIGHT => array() + ); + return $blocknames; + } + + /** + * Returns the localised name of this course format plugin + * + * @return lang_string + */ + public final function get_format_name() { + return new lang_string('pluginname', 'format_'.$this->get_format()); + } + + /** + * Definitions of the additional options that this course format uses for course + * + * This function may be called often, it should be as fast as possible. + * Avoid using get_string() method, use "new lang_string()" instead + * It is not recommended to use dynamic or course-dependant expressions here + * This function may be also called when course does not exist yet. + * + * Option names must be different from fields in the {course} talbe or any form elements on + * course edit form, it may even make sence to use special prefix for them. + * + * Each option must have the option name as a key and the array of properties as a value: + * 'default' - default value for this option (assumed null if not specified) + * 'type' - type of the option value (PARAM_INT, PARAM_RAW, etc.) + * + * Additional properties used by default implementation of + * course_format::create_edit_form_elements() (calls this method with $foreditform = true) + * 'label' - localised human-readable label for the edit form + * 'element_type' - type of the form element, default 'text' + * 'element_attributes' - additional attributes for the form element, these are 4th and further + * arguments in the moodleform::addElement() method + * 'help' - string for help button. Note that if 'help' value is 'myoption' then the string with + * the name 'myoption_help' must exist in the language file + * 'help_component' - language component to look for help string, by default this the component + * for this course format + * + * This is an interface for creating simple form elements. If format plugin wants to use other + * methods such as disableIf, it can be done by overriding create_edit_form_elements(). + * + * Course format options can be accessed as: + * $this->get_course()->OPTIONNAME (inside the format class) + * course_get_format($course)->get_course()->OPTIONNAME (outside of format class) + * + * All course options are returned by calling: + * $this->get_format_options(); + * + * @param bool $foreditform + * @return array of options + */ + public function course_format_options($foreditform = false) { + return array(); + } + + /** + * Definitions of the additional options that this course format uses for section + * + * See course_format::course_format_options() for return array definition. + * + * Additionally section format options may have property 'cache' set to true + * if this option needs to be cached in get_fast_modinfo(). The 'cache' property + * is recommended to be set only for fields used in course_format::get_section_name(), + * course_format::extend_course_navigation() and course_format::get_view_url() + * + * For better performance cached options are recommended to have 'cachedefault' property + * Unlike 'default', 'cachedefault' should be static and not access get_config(). + * + * Regardless of value of 'cache' all options are accessed in the code as + * $sectioninfo->OPTIONNAME + * where $sectioninfo is instance of section_info, returned by + * get_fast_modinfo($course)->get_section_info($sectionnum) + * or get_fast_modinfo($course)->get_section_info_all() + * + * All format options for particular section are returned by calling: + * $this->get_format_options($section); + * + * @param bool $foreditform + * @return array + */ + public function section_format_options($foreditform = false) { + return array(); + } + + /** + * Returns the format options stored for this course or course section + * + * When overriding please note that this function is called from rebuild_course_cache() + * and section_info object, therefore using of get_fast_modinfo() and/or any function that + * accesses it may lead to recursion. + * + * @param null|int|stdClass|section_info $section if null the course format options will be returned + * otherwise options for specified section will be returned. This can be either + * section object or relative section number (field course_sections.section) + * @return array + */ + public function get_format_options($section = null) { + global $DB; + if ($section === null) { + $options = $this->course_format_options(); + } else { + $options = $this->section_format_options(); + } + if (empty($options)) { + // There are no option for course/sections anyway, no need to go further. + return array(); + } + if ($section === null) { + // Course format options will be returned. + $sectionid = 0; + } else if ($this->courseid && isset($section->id)) { + // Course section format options will be returned. + $sectionid = $section->id; + } else if ($this->courseid && is_int($section) && + ($sectionobj = $DB->get_record('course_sections', + array('section' => $section, 'course' => $this->courseid), 'id'))) { + // Course section format options will be returned. + $sectionid = $sectionobj->id; + } else { + // Non-existing (yet) section was passed as an argument + // default format options for course section will be returned. + $sectionid = -1; + } + if (!array_key_exists($sectionid, $this->formatoptions)) { + $this->formatoptions[$sectionid] = array(); + // First fill with default values. + foreach ($options as $optionname => $optionparams) { + $this->formatoptions[$sectionid][$optionname] = null; + if (array_key_exists('default', $optionparams)) { + $this->formatoptions[$sectionid][$optionname] = $optionparams['default']; + } + } + if ($this->courseid && $sectionid !== -1) { + // Overwrite the default options values with those stored in course_format_options table + // nothing can be stored if we are interested in generic course ($this->courseid == 0) + // or generic section ($sectionid === 0). + $records = $DB->get_records('course_format_options', + array('courseid' => $this->courseid, + 'format' => $this->format, + 'sectionid' => $sectionid + ), '', 'id,name,value'); + foreach ($records as $record) { + if (array_key_exists($record->name, $this->formatoptions[$sectionid])) { + $value = $record->value; + if ($value !== null && isset($options[$record->name]['type'])) { + // This will convert string value to number if needed. + $value = clean_param($value, $options[$record->name]['type']); + } + $this->formatoptions[$sectionid][$record->name] = $value; + } + } + } + } + return $this->formatoptions[$sectionid]; + } + + /** + * Adds format options elements to the course/section edit form + * + * This function is called from course_edit_form::definition_after_data() + * + * @param MoodleQuickForm $mform form the elements are added to + * @param bool $forsection 'true' if this is a section edit form, 'false' if this is course edit form + * @return array array of references to the added form elements + */ + public function create_edit_form_elements(&$mform, $forsection = false) { + $elements = array(); + if ($forsection) { + $options = $this->section_format_options(true); + } else { + $options = $this->course_format_options(true); + } + foreach ($options as $optionname => $option) { + if (!isset($option['element_type'])) { + $option['element_type'] = 'text'; + } + $args = array($option['element_type'], $optionname, $option['label']); + if (!empty($option['element_attributes'])) { + $args = array_merge($args, $option['element_attributes']); + } + $elements[] = call_user_func_array(array($mform, 'addElement'), $args); + if (isset($option['help'])) { + $helpcomponent = 'format_'. $this->get_format(); + if (isset($option['help_component'])) { + $helpcomponent = $option['help_component']; + } + $mform->addHelpButton($optionname, $option['help'], $helpcomponent); + } + if (isset($option['type'])) { + $mform->setType($optionname, $option['type']); + } + if (isset($option['default']) && !array_key_exists($optionname, $mform->_defaultValues)) { + // Set defaults for the elements in the form. + // Since we call this method after set_data() make sure that we don't override what was already set. + $mform->setDefault($optionname, $option['default']); + } + } + + if (!$forsection && empty($this->courseid)) { + // Check if course end date form field should be enabled by default. + // If a default date is provided to the form element, it is magically enabled by default in the + // MoodleQuickForm_date_time_selector class, otherwise it's disabled by default. + if (get_config('moodlecourse', 'courseenddateenabled')) { + // At this stage (this is called from definition_after_data) course data is already set as default. + // We can not overwrite what is in the database. + $mform->setDefault('enddate', $this->get_default_course_enddate($mform)); + } + } + + return $elements; + } + + /** + * Override if you need to perform some extra validation of the format options + * + * @param array $data array of ("fieldname"=>value) of submitted data + * @param array $files array of uploaded files "element_name"=>tmp_file_path + * @param array $errors errors already discovered in edit form validation + * @return array of "element_name"=>"error_description" if there are errors, + * or an empty array if everything is OK. + * Do not repeat errors from $errors param here + */ + public function edit_form_validation($data, $files, $errors) { + return array(); + } + + /** + * Prepares values of course or section format options before storing them in DB + * + * If an option has invalid value it is not returned + * + * @param array $rawdata associative array of the proposed course/section format options + * @param int|null $sectionid null if it is course format option + * @return array array of options that have valid values + */ + protected function validate_format_options(array $rawdata, int $sectionid = null) : array { + if (!$sectionid) { + $allformatoptions = $this->course_format_options(true); + } else { + $allformatoptions = $this->section_format_options(true); + } + $data = array_intersect_key($rawdata, $allformatoptions); + foreach ($data as $key => $value) { + $option = $allformatoptions[$key] + ['type' => PARAM_RAW, 'element_type' => null, 'element_attributes' => [[]]]; + $data[$key] = clean_param($value, $option['type']); + if ($option['element_type'] === 'select' && !array_key_exists($data[$key], $option['element_attributes'][0])) { + // Value invalid for select element, skip. + unset($data[$key]); + } + } + return $data; + } + + /** + * Validates format options for the course + * + * @param array $data data to insert/update + * @return array array of options that have valid values + */ + public function validate_course_format_options(array $data) : array { + return $this->validate_format_options($data); + } + + /** + * Updates format options for a course or section + * + * If $data does not contain property with the option name, the option will not be updated + * + * @param stdClass|array $data return value from moodleform::get_data() or array with data + * @param null|int $sectionid null if these are options for course or section id (course_sections.id) + * if these are options for section + * @return bool whether there were any changes to the options values + */ + protected function update_format_options($data, $sectionid = null) { + global $DB; + $data = $this->validate_format_options((array)$data, $sectionid); + if (!$sectionid) { + $allformatoptions = $this->course_format_options(); + $sectionid = 0; + } else { + $allformatoptions = $this->section_format_options(); + } + if (empty($allformatoptions)) { + // Nothing to update anyway. + return false; + } + $defaultoptions = array(); + $cached = array(); + foreach ($allformatoptions as $key => $option) { + $defaultoptions[$key] = null; + if (array_key_exists('default', $option)) { + $defaultoptions[$key] = $option['default']; + } + $cached[$key] = ($sectionid === 0 || !empty($option['cache'])); + } + $records = $DB->get_records('course_format_options', + array('courseid' => $this->courseid, + 'format' => $this->format, + 'sectionid' => $sectionid + ), '', 'name,id,value'); + $changed = $needrebuild = false; + foreach ($defaultoptions as $key => $value) { + if (isset($records[$key])) { + if (array_key_exists($key, $data) && $records[$key]->value !== $data[$key]) { + $DB->set_field('course_format_options', 'value', + $data[$key], array('id' => $records[$key]->id)); + $changed = true; + $needrebuild = $needrebuild || $cached[$key]; + } + } else { + if (array_key_exists($key, $data) && $data[$key] !== $value) { + $newvalue = $data[$key]; + $changed = true; + $needrebuild = $needrebuild || $cached[$key]; + } else { + $newvalue = $value; + // We still insert entry in DB but there are no changes from user point of + // view and no need to call rebuild_course_cache(). + } + $DB->insert_record('course_format_options', array( + 'courseid' => $this->courseid, + 'format' => $this->format, + 'sectionid' => $sectionid, + 'name' => $key, + 'value' => $newvalue + )); + } + } + if ($needrebuild) { + rebuild_course_cache($this->courseid, true); + } + if ($changed) { + // Reset internal caches. + if (!$sectionid) { + $this->course = false; + } + unset($this->formatoptions[$sectionid]); + } + return $changed; + } + + /** + * Updates format options for a course + * + * If $data does not contain property with the option name, the option will not be updated + * + * @param stdClass|array $data return value from moodleform::get_data() or array with data + * @param stdClass $oldcourse if this function is called from update_course() + * this object contains information about the course before update + * @return bool whether there were any changes to the options values + */ + public function update_course_format_options($data, $oldcourse = null) { + return $this->update_format_options($data); + } + + /** + * Updates format options for a section + * + * Section id is expected in $data->id (or $data['id']) + * If $data does not contain property with the option name, the option will not be updated + * + * @param stdClass|array $data return value from moodleform::get_data() or array with data + * @return bool whether there were any changes to the options values + */ + public function update_section_format_options($data) { + $data = (array)$data; + return $this->update_format_options($data, $data['id']); + } + + /** + * Return an instance of moodleform to edit a specified section + * + * Default implementation returns instance of editsection_form that automatically adds + * additional fields defined in course_format::section_format_options() + * + * Format plugins may extend editsection_form if they want to have custom edit section form. + * + * @param mixed $action the action attribute for the form. If empty defaults to auto detect the + * current url. If a moodle_url object then outputs params as hidden variables. + * @param array $customdata the array with custom data to be passed to the form + * /course/editsection.php passes section_info object in 'cs' field + * for filling availability fields + * @return moodleform + */ + public function editsection_form($action, $customdata = array()) { + global $CFG; + require_once($CFG->dirroot. '/course/editsection_form.php'); + $context = context_course::instance($this->courseid); + if (!array_key_exists('course', $customdata)) { + $customdata['course'] = $this->get_course(); + } + return new editsection_form($action, $customdata); + } + + /** + * Allows course format to execute code on moodle_page::set_course() + * + * @param moodle_page $page instance of page calling set_course + */ + public function page_set_course(moodle_page $page) { + } + + /** + * Allows course format to execute code on moodle_page::set_cm() + * + * Current module can be accessed as $page->cm (returns instance of cm_info) + * + * @param moodle_page $page instance of page calling set_cm + */ + public function page_set_cm(moodle_page $page) { + } + + /** + * Course-specific information to be output on any course page (usually above navigation bar) + * + * Example of usage: + * define + * class format_FORMATNAME_XXX implements renderable {} + * + * create format renderer in course/format/FORMATNAME/renderer.php, define rendering function: + * class format_FORMATNAME_renderer extends plugin_renderer_base { + * protected function render_format_FORMATNAME_XXX(format_FORMATNAME_XXX $xxx) { + * return html_writer::tag('div', 'This is my header/footer'); + * } + * } + * + * Return instance of format_FORMATNAME_XXX in this function, the appropriate method from + * plugin renderer will be called + * + * @return null|renderable null for no output or object with data for plugin renderer + */ + public function course_header() { + return null; + } + + /** + * Course-specific information to be output on any course page (usually in the beginning of + * standard footer) + * + * See course_format::course_header() for usage + * + * @return null|renderable null for no output or object with data for plugin renderer + */ + public function course_footer() { + return null; + } + + /** + * Course-specific information to be output immediately above content on any course page + * + * See course_format::course_header() for usage + * + * @return null|renderable null for no output or object with data for plugin renderer + */ + public function course_content_header() { + return null; + } + + /** + * Course-specific information to be output immediately below content on any course page + * + * See course_format::course_header() for usage + * + * @return null|renderable null for no output or object with data for plugin renderer + */ + public function course_content_footer() { + return null; + } + + /** + * Returns instance of page renderer used by this plugin + * + * @param moodle_page $page + * @return renderer_base + */ + public function get_renderer(moodle_page $page) { + return $page->get_renderer('format_'. $this->get_format()); + } + + /** + * Returns instance of output compornent used by this plugin + * + * @param string $outputname the element to render (section, activity...) + * @return string the output component classname + */ + public function get_output_classname(?string $outputname): string { + $component = 'format_'. $this->get_format(); + $outputclass = "$component\\output\\$outputname"; + if (class_exists($outputclass)) { + return $outputclass; + } + return "core_course\\output\\$outputname"; + } + + /** + * Returns true if the specified section is current + * + * By default we analyze $course->marker + * + * @param int|stdClass|section_info $section + * @return bool + */ + public function is_section_current($section) { + if (is_object($section)) { + $sectionnum = $section->section; + } else { + $sectionnum = $section; + } + return ($sectionnum && ($course = $this->get_course()) && $course->marker == $sectionnum); + } + + /** + * Allows to specify for modinfo that section is not available even when it is visible and conditionally available. + * + * Note: affected user can be retrieved as: $section->modinfo->userid + * + * Course format plugins can override the method to change the properties $available and $availableinfo that were + * calculated by conditional availability. + * To make section unavailable set: + * $available = false; + * To make unavailable section completely hidden set: + * $availableinfo = ''; + * To make unavailable section visible with availability message set: + * $availableinfo = get_string('sectionhidden', 'format_xxx'); + * + * @param section_info $section + * @param bool $available the 'available' propery of the section_info as it was evaluated by conditional availability. + * Can be changed by the method but 'false' can not be overridden by 'true'. + * @param string $availableinfo the 'availableinfo' propery of the section_info as it was evaluated by conditional availability. + * Can be changed by the method + */ + public function section_get_available_hook(section_info $section, &$available, &$availableinfo) { + } + + /** + * Whether this format allows to delete sections + * + * If format supports deleting sections it is also recommended to define language string + * 'deletesection' inside the format. + * + * Do not call this function directly, instead use course_can_delete_section() + * + * @param int|stdClass|section_info $section + * @return bool + */ + public function can_delete_section($section) { + return false; + } + + /** + * Deletes a section + * + * Do not call this function directly, instead call course_delete_section() + * + * @param int|stdClass|section_info $section + * @param bool $forcedeleteifnotempty if set to false section will not be deleted if it has modules in it. + * @return bool whether section was deleted + */ + public function delete_section($section, $forcedeleteifnotempty = false) { + global $DB; + if (!$this->uses_sections()) { + // Not possible to delete section if sections are not used. + return false; + } + if (!is_object($section)) { + $section = $DB->get_record('course_sections', array('course' => $this->get_courseid(), 'section' => $section), + 'id,section,sequence,summary'); + } + if (!$section || !$section->section) { + // Not possible to delete 0-section. + return false; + } + + if (!$forcedeleteifnotempty && (!empty($section->sequence) || !empty($section->summary))) { + return false; + } + + $course = $this->get_course(); + + // Remove the marker if it points to this section. + if ($section->section == $course->marker) { + course_set_marker($course->id, 0); + } + + $lastsection = $DB->get_field_sql('SELECT max(section) from {course_sections} + WHERE course = ?', array($course->id)); + + // Find out if we need to descrease the 'numsections' property later. + $courseformathasnumsections = array_key_exists('numsections', + $this->get_format_options()); + $decreasenumsections = $courseformathasnumsections && ($section->section <= $course->numsections); + + // Move the section to the end. + move_section_to($course, $section->section, $lastsection, true); + + // Delete all modules from the section. + foreach (preg_split('/,/', $section->sequence, -1, PREG_SPLIT_NO_EMPTY) as $cmid) { + course_delete_module($cmid); + } + + // Delete section and it's format options. + $DB->delete_records('course_format_options', array('sectionid' => $section->id)); + $DB->delete_records('course_sections', array('id' => $section->id)); + rebuild_course_cache($course->id, true); + + // Delete section summary files. + $context = \context_course::instance($course->id); + $fs = get_file_storage(); + $fs->delete_area_files($context->id, 'course', 'section', $section->id); + + // Descrease 'numsections' if needed. + if ($decreasenumsections) { + $this->update_course_format_options(array('numsections' => $course->numsections - 1)); + } + + return true; + } + + /** + * Prepares the templateable object to display section name + * + * @param \section_info|\stdClass $section + * @param bool $linkifneeded + * @param bool $editable + * @param null|lang_string|string $edithint + * @param null|lang_string|string $editlabel + * @return \core\output\inplace_editable + */ + public function inplace_editable_render_section_name($section, $linkifneeded = true, + $editable = null, $edithint = null, $editlabel = null) { + global $USER, $CFG; + require_once($CFG->dirroot.'/course/lib.php'); + + if ($editable === null) { + $editable = !empty($USER->editing) && has_capability('moodle/course:update', + context_course::instance($section->course)); + } + + $displayvalue = $title = get_section_name($section->course, $section); + if ($linkifneeded) { + // Display link under the section name if the course format setting is to display one section per page. + $url = course_get_url($section->course, $section->section, array('navigation' => true)); + if ($url) { + $displayvalue = html_writer::link($url, $title); + } + $itemtype = 'sectionname'; + } else { + // If $linkifneeded==false, we never display the link (this is used when rendering the section header). + // Itemtype 'sectionnamenl' (nl=no link) will tell the callback that link should not be rendered - + // there is no other way callback can know where we display the section name. + $itemtype = 'sectionnamenl'; + } + if (empty($edithint)) { + $edithint = new lang_string('editsectionname'); + } + if (empty($editlabel)) { + $editlabel = new lang_string('newsectionname', '', $title); + } + + return new \core\output\inplace_editable('format_' . $this->format, $itemtype, $section->id, $editable, + $displayvalue, $section->name, $edithint, $editlabel); + } + + /** + * Updates the value in the database and modifies this object respectively. + * + * ALWAYS check user permissions before performing an update! Throw exceptions if permissions are not sufficient + * or value is not legit. + * + * @param stdClass $section + * @param string $itemtype + * @param mixed $newvalue + * @return \core\output\inplace_editable + */ + public function inplace_editable_update_section_name($section, $itemtype, $newvalue) { + if ($itemtype === 'sectionname' || $itemtype === 'sectionnamenl') { + $context = context_course::instance($section->course); + external_api::validate_context($context); + require_capability('moodle/course:update', $context); + + $newtitle = clean_param($newvalue, PARAM_TEXT); + if (strval($section->name) !== strval($newtitle)) { + course_update_section($section->course, $section, array('name' => $newtitle)); + } + return $this->inplace_editable_render_section_name($section, ($itemtype === 'sectionname'), true); + } + } + + + /** + * Returns the default end date value based on the start date. + * + * This is the default implementation for course formats, it is based on + * moodlecourse/courseduration setting. Course formats like format_weeks for + * example can overwrite this method and return a value based on their internal options. + * + * @param moodleform $mform + * @param array $fieldnames The form - field names mapping. + * @return int + */ + public function get_default_course_enddate($mform, $fieldnames = array()) { + + if (empty($fieldnames)) { + $fieldnames = array('startdate' => 'startdate'); + } + + $startdate = $this->get_form_start_date($mform, $fieldnames); + $courseduration = intval(get_config('moodlecourse', 'courseduration')); + if (!$courseduration) { + // Default, it should be already set during upgrade though. + $courseduration = YEARSECS; + } + + return $startdate + $courseduration; + } + + /** + * Indicates whether the course format supports the creation of the Announcements forum. + * + * For course format plugin developers, please override this to return true if you want the Announcements forum + * to be created upon course creation. + * + * @return bool + */ + public function supports_news() { + // For backwards compatibility, check if default blocks include the news_items block. + $defaultblocks = $this->get_default_blocks(); + foreach ($defaultblocks as $blocks) { + if (in_array('news_items', $blocks)) { + return true; + } + } + // Return false by default. + return false; + } + + /** + * Get the start date value from the course settings page form. + * + * @param moodleform $mform + * @param array $fieldnames The form - field names mapping. + * @return int + */ + protected function get_form_start_date($mform, $fieldnames) { + $startdate = $mform->getElementValue($fieldnames['startdate']); + return $mform->getElement($fieldnames['startdate'])->exportValue($startdate); + } + + /** + * Returns whether this course format allows the activity to + * have "triple visibility state" - visible always, hidden on course page but available, hidden. + * + * @param stdClass|cm_info $cm course module (may be null if we are displaying a form for adding a module) + * @param stdClass|section_info $section section where this module is located or will be added to + * @return bool + */ + public function allow_stealth_module_visibility($cm, $section) { + return false; + } + + /** + * Callback used in WS core_course_edit_section when teacher performs an AJAX action on a section (show/hide) + * + * Access to the course is already validated in the WS but the callback has to make sure + * that particular action is allowed by checking capabilities + * + * Course formats should register + * + * @param stdClass|section_info $section + * @param string $action + * @param int $sr the section return + * @return null|array|stdClass any data for the Javascript post-processor (must be json-encodeable) + */ + public function section_action($section, $action, $sr) { + global $PAGE; + if (!$this->uses_sections() || !$section->section) { + // No section actions are allowed if course format does not support sections. + // No actions are allowed on the 0-section by default (overwrite in course format if needed). + throw new moodle_exception('sectionactionnotsupported', 'core', null, s($action)); + } + + $course = $this->get_course(); + $coursecontext = context_course::instance($course->id); + switch($action) { + case 'hide': + case 'show': + require_capability('moodle/course:sectionvisibility', $coursecontext); + $visible = ($action === 'hide') ? 0 : 1; + course_update_section($course, $section, array('visible' => $visible)); + break; + default: + throw new moodle_exception('sectionactionnotsupported', 'core', null, s($action)); + } + + $modules = []; + + $modinfo = $this->get_modinfo(); + + if (!($section instanceof section_info)) { + $section = $modinfo->get_section_info($section->section); + } + + if ($sr) { + $this->set_section_number($sr); + } + + // Load the cmlist output. + $cmitemclass = $this->get_output_classname('section_format\\cmitem'); + $renderer = $this->get_renderer($PAGE); + + $coursesections = $modinfo->sections; + if (array_key_exists($section->section, $coursesections)) { + $completioninfo = new completion_info($course); + foreach ($coursesections[$section->section] as $cmid) { + $cm = $modinfo->get_cm($cmid); + $cmitem = new $cmitemclass($this, $section, $cm); + $modules[] = $renderer->render($cmitem); + } + } + + return ['modules' => $modules]; + } + + /** + * Return the plugin config settings for external functions, + * in some cases the configs will need formatting or be returned only if the current user has some capabilities enabled. + * + * @return array the list of configs + * @since Moodle 3.5 + */ + public function get_config_for_external() { + return array(); + } +} diff --git a/course/format/formatlegacy.php b/course/format/formatlegacy.php index 665f5833821..063e47ab348 100644 --- a/course/format/formatlegacy.php +++ b/course/format/formatlegacy.php @@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die; * @copyright 2012 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class format_legacy extends format_base { +class format_legacy extends core_course\course_format { /** * Returns true if this course format uses sections diff --git a/course/format/singleactivity/lib.php b/course/format/singleactivity/lib.php index 88aba735101..b468c2bc047 100644 --- a/course/format/singleactivity/lib.php +++ b/course/format/singleactivity/lib.php @@ -32,7 +32,7 @@ require_once($CFG->dirroot. '/course/format/lib.php'); * @copyright 2012 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class format_singleactivity extends format_base { +class format_singleactivity extends core_course\course_format { /** @var cm_info the current activity. Use get_activity() to retrieve it. */ private $activity = false; diff --git a/course/format/social/lib.php b/course/format/social/lib.php index 088719a0a72..5ca13cba3ae 100644 --- a/course/format/social/lib.php +++ b/course/format/social/lib.php @@ -33,7 +33,7 @@ require_once($CFG->dirroot. '/course/format/lib.php'); * @copyright 2012 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class format_social extends format_base { +class format_social extends core_course\course_format { /** * The URL to use for the specified course diff --git a/course/format/weeks/lib.php b/course/format/weeks/lib.php index bc9326ed2f0..30fab80f671 100644 --- a/course/format/weeks/lib.php +++ b/course/format/weeks/lib.php @@ -34,7 +34,7 @@ require_once($CFG->dirroot. '/course/lib.php'); * @copyright 2012 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class format_weeks extends format_base { +class format_weeks extends core_course\course_format { /** * Returns true if this course format uses sections diff --git a/course/lib.php b/course/lib.php index d1b676ef53c..4fe1c9a67c9 100644 --- a/course/lib.php +++ b/course/lib.php @@ -592,9 +592,7 @@ function course_set_marker($courseid, $marker) { if ($COURSE && $COURSE->id == $courseid) { $COURSE->marker = $marker; } - if (class_exists('format_base')) { - format_base::reset_course_cache($courseid); - } + core_course\course_format::reset_course_cache($courseid); course_modinfo::clear_instance_cache($courseid); } @@ -2149,7 +2147,7 @@ function move_courses($courseids, $categoryid) { * Returns the display name of the given section that the course prefers * * Implementation of this function is provided by course format - * @see format_base::get_section_name() + * @see core_course\course_format::get_section_name() * * @param int|stdClass $courseorid The course to get the section name for (object or just course id) * @param int|stdClass $section Section object from database or just field course_sections.section diff --git a/course/tests/courseformat_test.php b/course/tests/courseformat_test.php index dd7b1a94dfe..15cb2d723c2 100644 --- a/course/tests/courseformat_test.php +++ b/course/tests/courseformat_test.php @@ -210,7 +210,7 @@ class core_course_courseformat_testcase extends advanced_testcase { * @copyright 2016 Jun Pataleta * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class format_testformat extends format_base { +class format_testformat extends core_course\course_format { /** * Returns the list of blocks to be automatically added for the newly created course. * @@ -232,7 +232,7 @@ class format_testformat extends format_base { * @copyright 2016 Jun Pataleta * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class format_testlegacy extends format_base { +class format_testlegacy extends core_course\course_format { /** * Returns the list of blocks to be automatically added for the newly created course. * diff --git a/lib/adminlib.php b/lib/adminlib.php index d23d1b15885..536413a786e 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -4479,7 +4479,7 @@ class admin_setting_sitesetselect extends admin_setting_configselect { if ($SITE->id == $COURSE->id) { $COURSE = $SITE; } - format_base::reset_course_cache($SITE->id); + core_course\course_format::reset_course_cache($SITE->id); return ''; @@ -4690,7 +4690,7 @@ class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox { if ($SITE->id == $COURSE->id) { $COURSE = $SITE; } - format_base::reset_course_cache($SITE->id); + core_course\course_format::reset_course_cache($SITE->id); return ''; } @@ -4772,7 +4772,7 @@ class admin_setting_sitesettext extends admin_setting_configtext { if ($SITE->id == $COURSE->id) { $COURSE = $SITE; } - format_base::reset_course_cache($SITE->id); + core_course\course_format::reset_course_cache($SITE->id); return ''; } @@ -4824,7 +4824,7 @@ class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor if ($SITE->id == $COURSE->id) { $COURSE = $SITE; } - format_base::reset_course_cache($SITE->id); + core_course\course_format::reset_course_cache($SITE->id); return ''; } diff --git a/lib/behat/classes/util.php b/lib/behat/classes/util.php index feaf4e67280..a0a13768895 100644 --- a/lib/behat/classes/util.php +++ b/lib/behat/classes/util.php @@ -411,10 +411,7 @@ class behat_util extends testing_util { filter_manager::reset_caches(); // Reset course and module caches. - if (class_exists('format_base')) { - // If file containing class is not loaded, there is no cache there anyway. - format_base::reset_course_cache(0); - } + core_course\course_format::reset_course_cache(0); get_fast_modinfo(0, 0, true); // Inform data generator. diff --git a/lib/db/renamedclasses.php b/lib/db/renamedclasses.php index d54813c06d7..390cd2993ed 100644 --- a/lib/db/renamedclasses.php +++ b/lib/db/renamedclasses.php @@ -42,4 +42,5 @@ $renamedclasses = array( 'core\\analytics\\target\\course_completion' => 'core_course\\analytics\\target\\course_completion', 'core\\analytics\\target\\course_gradetopass' => 'core_course\\analytics\\target\\course_gradetopass', 'core\\analytics\\target\\no_teaching' => 'core_course\\analytics\\target\\no_teaching', + 'format_base' => 'core_course\\course_format', ); diff --git a/lib/deprecatedlib.php b/lib/deprecatedlib.php index b02ddc0acf6..1a2f3d94c5a 100644 --- a/lib/deprecatedlib.php +++ b/lib/deprecatedlib.php @@ -918,7 +918,8 @@ function textlib_get_instance() { * @deprecated since 2.4 */ function get_generic_section_name() { - throw new coding_exception('get_generic_section_name() is deprecated. Please use appropriate functionality from class format_base'); + throw new coding_exception('get_generic_section_name() is deprecated. Please use appropriate functionality ' + .'from class core_course\\course_format'); } /** diff --git a/lib/modinfolib.php b/lib/modinfolib.php index 69bfbcb1009..933a14c03ca 100644 --- a/lib/modinfolib.php +++ b/lib/modinfolib.php @@ -2357,10 +2357,7 @@ function rebuild_course_cache($courseid=0, $clearonly=false) { // Destroy navigation caches navigation_cache::destroy_volatile_caches(); - if (class_exists('format_base')) { - // if file containing class is not loaded, there is no cache there anyway - format_base::reset_course_cache($courseid); - } + core_course\course_format::reset_course_cache($courseid); $cachecoursemodinfo = cache::make('core', 'coursemodinfo'); if (empty($courseid)) { diff --git a/lib/moodlelib.php b/lib/moodlelib.php index bbadbf47617..496bf20cbfc 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -5179,9 +5179,7 @@ function delete_course($courseorid, $showfeedback = true) { $DB->delete_records("course_format_options", array("courseid" => $courseid)); // Reset all course related caches here. - if (class_exists('format_base', false)) { - format_base::reset_course_cache($courseid); - } + core_course\course_format::reset_course_cache($courseid); // Tell search that we have deleted the course so it can delete course data from the index. \core_search\manager::course_deleting_finish($courseid); diff --git a/lib/navigationlib.php b/lib/navigationlib.php index 531056ef798..494f745b26a 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -2005,7 +2005,7 @@ class global_navigation extends navigation_node { * Loads all of the courses section into the navigation. * * This function calls method from current course format, see - * {@link format_base::extend_course_navigation()} + * {@link core_course\course_format::extend_course_navigation()} * If course module ($cm) is specified but course format failed to create the node, * the activity node is created anyway. * diff --git a/lib/phpunit/classes/util.php b/lib/phpunit/classes/util.php index 94ec4414dbe..a9be6517ebd 100644 --- a/lib/phpunit/classes/util.php +++ b/lib/phpunit/classes/util.php @@ -240,10 +240,7 @@ class phpunit_util extends testing_util { //TODO MDL-25290: add more resets here and probably refactor them to new core function // Reset course and module caches. - if (class_exists('format_base')) { - // If file containing class is not loaded, there is no cache there anyway. - format_base::reset_course_cache(0); - } + core_course\course_format::reset_course_cache(0); get_fast_modinfo(0, 0, true); // Reset other singletons.