/** * Reset contents of all database tables to initial values, reset caches, etc. * * Note: this is relatively slow (cca 2 seconds for pg and 7 for mysql) - please use with care! * * @static * @param bool $detectchanges * true - changes in global state and database are reported as errors * false - no errors reported * null - only critical problems are reported as errors * @return void */ public static function reset_all_data($detectchanges = false) { global $DB, $CFG, $USER, $SITE, $COURSE, $PAGE, $OUTPUT, $SESSION; // Stop any message redirection. phpunit_util::stop_message_redirection(); // Stop any message redirection. phpunit_util::stop_phpmailer_redirection(); // Stop any message redirection. phpunit_util::stop_event_redirection(); // We used to call gc_collect_cycles here to ensure desctructors were called between tests. // This accounted for 25% of the total time running phpunit - so we removed it. // Show any unhandled debugging messages, the runbare() could already reset it. self::display_debugging_messages(); self::reset_debugging(); // reset global $DB in case somebody mocked it $DB = self::get_global_backup('DB'); if ($DB->is_transaction_started()) { // we can not reset inside transaction $DB->force_transaction_rollback(); } $resetdb = self::reset_database(); $warnings = array(); if ($detectchanges === true) { if ($resetdb) { $warnings[] = 'Warning: unexpected database modification, resetting DB state'; } $oldcfg = self::get_global_backup('CFG'); $oldsite = self::get_global_backup('SITE'); foreach ($CFG as $k => $v) { if (!property_exists($oldcfg, $k)) { $warnings[] = 'Warning: unexpected new $CFG->' . $k . ' value'; } else { if ($oldcfg->{$k} !== $CFG->{$k}) { $warnings[] = 'Warning: unexpected change of $CFG->' . $k . ' value'; } } unset($oldcfg->{$k}); } if ($oldcfg) { foreach ($oldcfg as $k => $v) { $warnings[] = 'Warning: unexpected removal of $CFG->' . $k; } } if ($USER->id != 0) { $warnings[] = 'Warning: unexpected change of $USER'; } if ($COURSE->id != $oldsite->id) { $warnings[] = 'Warning: unexpected change of $COURSE'; } } if (ini_get('max_execution_time') != 0) { // This is special warning for all resets because we do not want any // libraries to mess with timeouts unintentionally. // Our PHPUnit integration is not supposed to change it either. if ($detectchanges !== false) { $warnings[] = 'Warning: max_execution_time was changed to ' . ini_get('max_execution_time'); } set_time_limit(0); } // restore original globals $_SERVER = self::get_global_backup('_SERVER'); $CFG = self::get_global_backup('CFG'); $SITE = self::get_global_backup('SITE'); $_GET = array(); $_POST = array(); $_FILES = array(); $_REQUEST = array(); $COURSE = $SITE; // reinitialise following globals $OUTPUT = new bootstrap_renderer(); $PAGE = new moodle_page(); $FULLME = null; $ME = null; $SCRIPT = null; // Empty sessison and set fresh new not-logged-in user. \core\session\manager::init_empty_session(); // reset all static caches \core\event\manager::phpunit_reset(); accesslib_clear_all_caches(true); get_string_manager()->reset_caches(true); reset_text_filters_cache(true); events_get_handlers('reset'); core_text::reset_caches(); get_message_processors(false, true); filter_manager::reset_caches(); // Reset internal users. core_user::reset_internal_users(); //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); } get_fast_modinfo(0, 0, true); // Reset other singletons. if (class_exists('core_plugin_manager')) { core_plugin_manager::reset_caches(true); } if (class_exists('\\core\\update\\checker')) { \core\update\checker::reset_caches(true); } if (class_exists('\\core\\update\\deployer')) { \core\update\deployer::reset_caches(true); } // purge dataroot directory self::reset_dataroot(); // restore original config once more in case resetting of caches changed CFG $CFG = self::get_global_backup('CFG'); // inform data generator self::get_data_generator()->reset(); // fix PHP settings error_reporting($CFG->debug); // verify db writes just in case something goes wrong in reset if (self::$lastdbwrites != $DB->perf_get_writes()) { error_log('Unexpected DB writes in phpunit_util::reset_all_data()'); self::$lastdbwrites = $DB->perf_get_writes(); } if ($warnings) { $warnings = implode("\n", $warnings); trigger_error($warnings, E_USER_WARNING); } }
/** * Set highlighted section. Only one section can be highlighted at the time. * * @param int $courseid course id * @param int $marker highlight section with this number, 0 means remove higlightin * @return void */ function course_set_marker($courseid, $marker) { global $DB; $DB->set_field("course", "marker", $marker, array('id' => $courseid)); format_base::reset_course_cache($courseid); }
/** * Reset contents of all database tables to initial values, reset caches, etc. */ public static function reset_all_data() { // Reset database. self::reset_database(); // Purge dataroot directory. self::reset_dataroot(); // Reset all static caches. accesslib_clear_all_caches(true); // Reset the nasty strings list used during the last test. nasty_strings::reset_used_strings(); 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); } get_fast_modinfo(0, 0, true); // Inform data generator. self::get_data_generator()->reset(); // Initialise $CFG with default values. This is needed for behat cli process, so we don't have modified // $CFG values from the old run. @see set_config. initialise_cfg(); }
/** * Loads all of the course sections into the navigation * * @param global_navigation $navigation * @param navigation_node $node The course node within the navigation */ public function extend_course_navigation($navigation, navigation_node $node) { global $PAGE; // if section is specified in course/view.php, make sure it is expanded in navigation if ($navigation->includesectionnum === false) { $selectedsection = optional_param('section', null, PARAM_INT); if ($selectedsection !== null && (!defined('AJAX_SCRIPT') || AJAX_SCRIPT == '0') && $PAGE->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) { $navigation->includesectionnum = $selectedsection; } } parent::extend_course_navigation($navigation, $node); }
/** * 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 {@link 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; // Resets the displayed image because changing the section name / details deletes the file. // See CONTRIB-4784. global $DB; $DB->set_field('format_grid_icon', 'displayedimageindex', 0, array('sectionid' => $data['id'])); return parent::update_section_format_options($data); }
/** * Course-specific information to be output immediately above content on any course page * * See {@link format_base::course_header()} for usage * * @return null|renderable null for no output or object with data for plugin renderer */ public function course_content_header() { global $PAGE; // if we are on course view page for particular section, return 'back to parent' control if ($this->get_viewed_section()) { $section = $this->get_section($this->get_viewed_section()); if ($section->parent) { $sr = $this->find_collapsed_parent($section->parent); $text = new lang_string('backtosection', 'format_flexsections', $this->get_section_name($section->parent)); } else { $sr = 0; $text = new lang_string('backtocourse', 'format_flexsections', $this->get_course()->fullname); } $url = $this->get_view_url($section->section, array('sr' => $sr)); return new format_flexsections_edit_control('backto', $url, strip_tags($text)); } // if we are on module view page, return 'back to section' control if ($PAGE->context && $PAGE->context->contextlevel == CONTEXT_MODULE && $PAGE->cm) { $sectionnum = $PAGE->cm->sectionnum; if ($sectionnum) { $text = new lang_string('backtosection', 'format_flexsections', $this->get_section_name($sectionnum)); } else { $text = new lang_string('backtocourse', 'format_flexsections', $this->get_course()->fullname); } return new format_flexsections_edit_control('backto', $this->get_view_url($sectionnum), strip_tags($text)); } return parent::course_content_header(); }
/** * 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) { if (empty($edithint)) { $edithint = new lang_string('editsectionname', 'format_topics'); } if (empty($editlabel)) { $title = get_section_name($section->course, $section); $editlabel = new lang_string('newsectionname', 'format_topics', $title); } return parent::inplace_editable_render_section_name($section, $linkifneeded, $editable, $edithint, $editlabel); }
/** * Tests that all section options are copied when the course format is changed. * None of the data is copied. * * It is a future enhancement to copy; * 1. Only the relevant options. * 2. Only the data associated with relevant options. */ public function test_course_format_options_restore_new_format() { global $DB, $CFG; $this->resetAfterTest(true); $this->setAdminUser(); $CFG->enableavailability = true; $CFG->enablecompletion = true; // Create a course with some availability data set. $generator = $this->getDataGenerator(); $course = $generator->create_course(array('format' => 'test_cs2_options', 'numsections' => 3, 'enablecompletion' => COMPLETION_ENABLED), array('createsections' => true)); $newcourse = $generator->create_course(array('format' => 'test_cs_options', 'numsections' => 3, 'enablecompletion' => COMPLETION_ENABLED), array('createsections' => true)); $courseobject = format_base::instance($course->id); $section = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 1), '*', MUST_EXIST); $data = array('id' => $section->id, 'numdaystocomplete' => 2, 'secondparameter' => 8); $courseobject->update_section_format_options($data); // Backup and restore it. $this->backup_and_restore($course, $newcourse); $newcourseobject = format_base::instance($newcourse->id); $sectionoptions = $newcourseobject->get_format_options(1); $this->assertArrayHasKey('numdaystocomplete', $sectionoptions); $this->assertArrayHasKey('secondparameter', $sectionoptions); $this->assertEquals(0, $sectionoptions['numdaystocomplete']); $this->assertEquals(0, $sectionoptions['secondparameter']); }
/** * Reset contents of all database tables to initial values, reset caches, etc. * * Note: this is relatively slow (cca 2 seconds for pg and 7 for mysql) - please use with care! * * @static * @param bool $logchanges log changes in global state and database in error log * @return void */ public static function reset_all_data($logchanges = false) { global $DB, $CFG, $USER, $SITE, $COURSE, $PAGE, $OUTPUT, $SESSION; // Stop any message redirection. phpunit_util::stop_message_redirection(); // Release memory and indirectly call destroy() methods to release resource handles, etc. gc_collect_cycles(); // Show any unhandled debugging messages, the runbare() could already reset it. self::display_debugging_messages(); self::reset_debugging(); // reset global $DB in case somebody mocked it $DB = self::get_global_backup('DB'); if ($DB->is_transaction_started()) { // we can not reset inside transaction $DB->force_transaction_rollback(); } $resetdb = self::reset_database(); $warnings = array(); if ($logchanges) { if ($resetdb) { $warnings[] = 'Warning: unexpected database modification, resetting DB state'; } $oldcfg = self::get_global_backup('CFG'); $oldsite = self::get_global_backup('SITE'); foreach ($CFG as $k => $v) { if (!property_exists($oldcfg, $k)) { $warnings[] = 'Warning: unexpected new $CFG->' . $k . ' value'; } else { if ($oldcfg->{$k} !== $CFG->{$k}) { $warnings[] = 'Warning: unexpected change of $CFG->' . $k . ' value'; } } unset($oldcfg->{$k}); } if ($oldcfg) { foreach ($oldcfg as $k => $v) { $warnings[] = 'Warning: unexpected removal of $CFG->' . $k; } } if ($USER->id != 0) { $warnings[] = 'Warning: unexpected change of $USER'; } if ($COURSE->id != $oldsite->id) { $warnings[] = 'Warning: unexpected change of $COURSE'; } } // restore original globals $_SERVER = self::get_global_backup('_SERVER'); $CFG = self::get_global_backup('CFG'); $SITE = self::get_global_backup('SITE'); $COURSE = $SITE; // reinitialise following globals $OUTPUT = new bootstrap_renderer(); $PAGE = new moodle_page(); $FULLME = null; $ME = null; $SCRIPT = null; $SESSION = new stdClass(); $_SESSION['SESSION'] =& $SESSION; // set fresh new not-logged-in user $user = new stdClass(); $user->id = 0; $user->mnethostid = $CFG->mnet_localhost_id; session_set_user($user); // reset all static caches accesslib_clear_all_caches(true); get_string_manager()->reset_caches(true); reset_text_filters_cache(true); events_get_handlers('reset'); textlib::reset_caches(); if (class_exists('repository')) { repository::reset_caches(); } filter_manager::reset_caches(); //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); } get_fast_modinfo(0, 0, true); // Reset other singletons. if (class_exists('plugin_manager')) { plugin_manager::reset_caches(true); } if (class_exists('available_update_checker')) { available_update_checker::reset_caches(true); } if (class_exists('available_update_deployer')) { available_update_deployer::reset_caches(true); } // purge dataroot directory self::reset_dataroot(); // restore original config once more in case resetting of caches changed CFG $CFG = self::get_global_backup('CFG'); // inform data generator self::get_data_generator()->reset(); // fix PHP settings error_reporting($CFG->debug); // verify db writes just in case something goes wrong in reset if (self::$lastdbwrites != $DB->perf_get_writes()) { error_log('Unexpected DB writes in phpunit_util::reset_all_data()'); self::$lastdbwrites = $DB->perf_get_writes(); } if ($warnings) { $warnings = implode("\n", $warnings); trigger_error($warnings, E_USER_WARNING); } }
/** * Tests that all section options are copied when the course format is changed. * None of the data is copied. * * It is a future enhancement to copy; * 1. Only the relevant options. * 2. Only the data associated with relevant options. */ public function test_course_format_options_restore_new_format() { global $DB, $CFG; $this->resetAfterTest(true); $this->setAdminUser(); // Create a source course using the test_cs2_options format. $generator = $this->getDataGenerator(); $course = $generator->create_course(array('format' => 'test_cs2_options', 'numsections' => 3, 'enablecompletion' => COMPLETION_ENABLED), array('createsections' => true)); // Create a target course using test_cs_options format. $newcourse = $generator->create_course(array('format' => 'test_cs_options', 'numsections' => 3, 'enablecompletion' => COMPLETION_ENABLED), array('createsections' => true)); // Set section 2 to have both options, and a name. $courseobject = format_base::instance($course->id); $section = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 2), '*', MUST_EXIST); $data = array('id' => $section->id, 'numdaystocomplete' => 2, 'secondparameter' => 8); $courseobject->update_section_format_options($data); $DB->set_field('course_sections', 'name', 'Frogs', array('id' => $section->id)); // Backup and restore to the new course using 'add to existing' so it // keeps the current (test_cs_options) format. $this->backup_and_restore($course, $newcourse, backup::TARGET_EXISTING_ADDING); // 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); $sectionoptions = $newcourseobject->get_format_options(2); $this->assertArrayHasKey('numdaystocomplete', $sectionoptions); $this->assertArrayNotHasKey('secondparameter', $sectionoptions); $this->assertEquals(0, $sectionoptions['numdaystocomplete']); // However, the name should have been changed, as this does not depend // on the format. $modinfo = get_fast_modinfo($newcourse->id); $section = $modinfo->get_section_info(2); $this->assertEquals('Frogs', $section->name); }
/** * Rebuilds the cached list of course activities stored in the database * @param int $courseid - id of course to rebuild, empty means all * @param boolean $clearonly - only clear the modinfo fields, gets rebuild automatically on the fly */ function rebuild_course_cache($courseid = 0, $clearonly = false) { global $COURSE, $SITE, $DB, $CFG; // 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); } if ($clearonly) { if (empty($courseid)) { $DB->set_field('course', 'modinfo', null); $DB->set_field('course', 'sectioncache', null); } else { // Clear both fields in one update $resetobj = (object) array('id' => $courseid, 'modinfo' => null, 'sectioncache' => null); $DB->update_record('course', $resetobj); } // update cached global COURSE too ;-) if ($courseid == $COURSE->id or empty($courseid)) { $COURSE->modinfo = null; $COURSE->sectioncache = null; } if ($courseid == $SITE->id) { $SITE->modinfo = null; $SITE->sectioncache = null; } // reset the fast modinfo cache get_fast_modinfo($courseid, 0, true); return; } require_once "{$CFG->dirroot}/course/lib.php"; if ($courseid) { $select = array('id' => $courseid); } else { $select = array(); @set_time_limit(0); // this could take a while! MDL-10954 } $rs = $DB->get_recordset("course", $select, '', 'id,fullname'); foreach ($rs as $course) { $modinfo = serialize(get_array_of_activities($course->id)); $sectioncache = serialize(course_modinfo::build_section_cache($course->id)); $updateobj = (object) array('id' => $course->id, 'modinfo' => $modinfo, 'sectioncache' => $sectioncache); $DB->update_record("course", $updateobj); // update cached global COURSE too ;-) if ($course->id == $COURSE->id) { $COURSE->modinfo = $modinfo; $COURSE->sectioncache = $sectioncache; } if ($course->id == $SITE->id) { $SITE->modinfo = $modinfo; $SITE->sectioncache = $sectioncache; } } $rs->close(); // reset the fast modinfo cache get_fast_modinfo($courseid, 0, true); }
/** * Save the new setting * * @param string $data The new value to save * @return string empty or error message */ public function write_setting($data) { global $DB, $SITE; $record = new stdClass(); $record->id = $SITE->id; $record->{$this->name} = $data; $record->timemodified = time(); $SITE->{$this->name} = $data; course_get_format($SITE)->update_course_format_options($record); $DB->update_record('course', $record); // There is something wrong in cache updates somewhere, let's reset everything. format_base::reset_course_cache(); return ''; }
/** * Is the section passed in the current section? * * @param stdClass $section The course_section entry from the DB * @return bool true if the section is current */ public function is_section_current($section) { $tcsettings = $this->get_settings(); if ($tcsettings['layoutstructure'] == 2 || $tcsettings['layoutstructure'] == 3) { if ($section->section < 1) { return false; } $timenow = time(); $dates = $this->format_collblct_get_section_dates($section, $this->get_course()); return $timenow >= $dates->start && $timenow < $dates->end; } else { if ($tcsettings['layoutstructure'] == 5) { if ($section->section < 1) { return false; } $timenow = time(); $day = $this->format_collblct_get_section_day($section, $this->get_course()); $onedayseconds = 86400; return $timenow >= $day && $timenow < $day + $onedayseconds; } else { return parent::is_section_current($section); } } }
/** * Adds format options elements to the course/section edit form * * This function is called from {@link 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 = true) { global $PAGE, $CFG; if ($PAGE->pagetype == 'course-edit') { $elements = parent::create_edit_form_elements($mform, $forsection); // Increase the number of sections combo box values if the user has increased the number of sections // using the icon on the course page beyond course 'maxsections' or course 'maxsections' has been // reduced below the number of sections already set for the course on the site administration course // defaults page. This is so that the number of sections is not reduced leaving unintended orphaned // activities / resources. if (!$forsection) { $maxsections = get_config('moodlecourse', 'maxsections'); $numsections = $mform->getElementValue('numsections'); $numsections = $numsections[0]; if ($numsections > $maxsections) { $element = $mform->getElement('numsections'); for ($i = $maxsections + 1; $i <= $numsections; $i++) { $element->addOption("{$i}", $i); } } } return $elements; } else { $validationerror = optional_param('validationerror', null, PARAM_INT); $mform->addElement('header', 'gpssettings', new lang_string('editsection_geo_heading', 'format_gps')); $mform->addHelpButton('gpssettings', 'gpshelp', 'format_gps'); if ($validationerror == 'yes') { $error = html_writer::div(new lang_string('validationerror', 'format_gps'), 'bold red error'); $errorlabel = html_writer::div(new lang_string('error'), 'bold red error'); $mform->addElement('static', 'validationerrror', $errorlabel, $error); $mform->addHelpButton('validationerrror', 'errorhelp', 'format_gps'); } $mform->addElement('checkbox', 'format_gps_restricted', new lang_string('active', 'format_gps')); $mform->setDefault('format_gps_restricted', FORMAT_GPS_UNRESTRICTED); $attributes = array('size' => '100', 'width' => '500', 'maxlength' => '100'); $mform->addElement('text', 'format_gps_address', new lang_string('address', 'format_gps'), $attributes); $mform->setType('format_gps_address', PARAM_TEXT); $mform->addElement('text', 'format_gps_latitude', new lang_string('latitude', 'format_gps')); $mform->addElement('text', 'format_gps_longitude', new lang_string('longitude', 'format_gps')); $mform->addRule('format_gps_address', null, 'maxlength', 255, 'client'); $mform->addRule('format_gps_longitude', null, 'numeric', null, 'client'); $mform->addRule('format_gps_longitude', null, 'numeric', null, 'client'); $mform->addRule('format_gps_latitude', null, 'numeric', null, 'client'); $mform->setType('format_gps_latitude', PARAM_FLOAT); $mform->setType('format_gps_longitude', PARAM_FLOAT); $mform->disabledIf('format_gps_address', 'format_gps_restricted', 'notchecked'); $mform->disabledIf('format_gps_latitude', 'format_gps_restricted', 'notchecked'); $mform->disabledIf('format_gps_longitude', 'format_gps_restricted', 'notchecked'); } }
/** * Delete a course, including all related data from the database, and any associated files. * * @param mixed $courseorid The id of the course or course object to delete. * @param bool $showfeedback Whether to display notifications of each action the function performs. * @return bool true if all the removals succeeded. false if there were any failures. If this * method returns false, some of the removals will probably have succeeded, and others * failed, but you have no way of knowing which. */ function delete_course($courseorid, $showfeedback = true) { global $DB; if (is_object($courseorid)) { $courseid = $courseorid->id; $course = $courseorid; } else { $courseid = $courseorid; if (!($course = $DB->get_record('course', array('id' => $courseid)))) { return false; } } $context = context_course::instance($courseid); // Frontpage course can not be deleted!! if ($courseid == SITEID) { return false; } // Allow plugins to use this course before we completely delete it. if ($pluginsfunction = get_plugins_with_function('pre_course_delete')) { foreach ($pluginsfunction as $plugintype => $plugins) { foreach ($plugins as $pluginfunction) { $pluginfunction($course); } } } // Make the course completely empty. remove_course_contents($courseid, $showfeedback); // Delete the course and related context instance. context_helper::delete_instance(CONTEXT_COURSE, $courseid); $DB->delete_records("course", array("id" => $courseid)); $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); } // Trigger a course deleted event. $event = \core\event\course_deleted::create(array('objectid' => $course->id, 'context' => $context, 'other' => array('shortname' => $course->shortname, 'fullname' => $course->fullname, 'idnumber' => $course->idnumber))); $event->add_record_snapshot('course', $course); $event->trigger(); return true; }
/** * Returns the list of blocks to be automatically added for the newly created course * * This function checks the existence of the file config.php in the course format folder. * If file exists and contains the code * $format['defaultblocks'] = 'leftblock1,leftblock2:rightblock1,rightblock2'; * these blocks are used, otherwise parent function is called * * @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; $formatconfig = $CFG->dirroot . '/course/format/' . $this->format . '/config.php'; $format = array(); // initialize array in external file if (is_readable($formatconfig)) { include $formatconfig; } if (!empty($format['defaultblocks'])) { return blocks_parse_default_blocks_list($format['defaultblocks']); } return parent::get_default_blocks(); }
/** * Adds format options elements to the course/section edit form. * * This function is called from {@link 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 = parent::create_edit_form_elements($mform, $forsection); // Increase the number of sections combo box values if the user has increased the number of sections // using the icon on the course page beyond course 'maxsections' or course 'maxsections' has been // reduced below the number of sections already set for the course on the site administration course // defaults page. This is so that the number of sections is not reduced leaving unintended orphaned // activities / resources. if (!$forsection) { $maxsections = get_config('moodlecourse', 'maxsections'); $numsections = $mform->getElementValue('numsections'); $numsections = $numsections[0]; if ($numsections > $maxsections) { $element = $mform->getElement('numsections'); for ($i = $maxsections + 1; $i <= $numsections; $i++) { $element->addOption("{$i}", $i); } } } return $elements; }
/** * Reset contents of all database tables to initial values, reset caches, etc. */ public static function reset_all_data() { // Reset database. self::reset_database(); // Purge dataroot directory. self::reset_dataroot(); // Reset all static caches. accesslib_clear_all_caches(true); // Reset the nasty strings list used during the last test. nasty_strings::reset_used_strings(); 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); } get_fast_modinfo(0, 0, true); // Inform data generator. self::get_data_generator()->reset(); }
/** * Allows course format to execute code on moodle_page::set_cm() * * If we are inside the main module for this course, remove extra node level * from navigation: substitute course node with activity node, move all children * * @param moodle_page $page instance of page calling set_cm */ public function page_set_cm(moodle_page $page) { global $PAGE; parent::page_set_cm($page); if ($PAGE == $page && ($cm = $this->get_activity()) && $cm->uservisible && $cm->id === $page->cm->id && ($activitynode = $page->navigation->find($cm->id, navigation_node::TYPE_ACTIVITY)) && ($node = $page->navigation->find($page->course->id, navigation_node::TYPE_COURSE))) { // Substitute course node with activity node, move all children. $node->action = $activitynode->action; $node->type = $activitynode->type; $node->id = $activitynode->id; $node->key = $activitynode->key; $node->isactive = $node->isactive || $activitynode->isactive; $node->icon = null; if ($activitynode->children->count()) { foreach ($activitynode->children as &$child) { $child->remove(); $node->add_node($child); } } else { $node->search_for_active_node(); } $activitynode->remove(); } }
/** * Is the section passed in the current section? * * @param stdClass $section The course_section entry from the DB * @return bool true if the section is current */ public function is_section_current($section) { $tcsettings = $this->get_settings(); if (($tcsettings['layoutstructure'] == 2) || ($tcsettings['layoutstructure'] == 3)) { if ($section->section < 1) { return false; } $timenow = time(); $dates = $this->format_topcoll_get_section_dates($section, $this->get_course()); return (($timenow >= $dates->start) && ($timenow < $dates->end)); } else if ($tcsettings['layoutstructure'] == 5) { if ($section->section < 1) { return false; } $timenow = time(); $day = $this->format_topcoll_get_section_day($section, $this->get_course()); $onedayseconds = 86400; return (($timenow >= $day) && ($timenow < ($day + $onedayseconds))); } else { return parent::is_section_current($section); } }
/** * Save the new setting * * @param string $data The new value to save * @return string empty or error message */ public function write_setting($data) { global $DB, $SITE, $COURSE; $record = new stdClass(); $record->id = $SITE->id; $record->{$this->name} = $data; $record->timemodified = time(); course_get_format($SITE)->update_course_format_options($record); $DB->update_record('course', $record); // Reset caches. $SITE = $DB->get_record('course', array('id' => $SITE->id), '*', MUST_EXIST); if ($SITE->id == $COURSE->id) { $COURSE = $SITE; } format_base::reset_course_cache($SITE->id); return ''; }
/** * Rebuilds or resets the cached list of course activities stored in MUC. * * rebuild_course_cache() must NEVER be called from lib/db/upgrade.php. * At the same time course cache may ONLY be cleared using this function in * upgrade scripts of plugins. * * During the bulk operations if it is necessary to reset cache of multiple * courses it is enough to call {@link increment_revision_number()} for the * table 'course' and field 'cacherev' specifying affected courses in select. * * Cached course information is stored in MUC core/coursemodinfo and is * validated with the DB field {course}.cacherev * * @global moodle_database $DB * @param int $courseid id of course to rebuild, empty means all * @param boolean $clearonly only clear the cache, gets rebuild automatically on the fly. * Recommended to set to true to avoid unnecessary multiple rebuilding. */ function rebuild_course_cache($courseid=0, $clearonly=false) { global $COURSE, $SITE, $DB, $CFG; // Function rebuild_course_cache() can not be called during upgrade unless it's clear only. if (!$clearonly && !upgrade_ensure_not_running(true)) { $clearonly = true; } // 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); } $cachecoursemodinfo = cache::make('core', 'coursemodinfo'); if (empty($courseid)) { // Clearing caches for all courses. increment_revision_number('course', 'cacherev', ''); $cachecoursemodinfo->purge(); course_modinfo::clear_instance_cache(); // Update global values too. $sitecacherev = $DB->get_field('course', 'cacherev', array('id' => SITEID)); $SITE->cachrev = $sitecacherev; if ($COURSE->id == SITEID) { $COURSE->cacherev = $sitecacherev; } else { $COURSE->cacherev = $DB->get_field('course', 'cacherev', array('id' => $COURSE->id)); } } else { // Clearing cache for one course, make sure it is deleted from user request cache as well. increment_revision_number('course', 'cacherev', 'id = :id', array('id' => $courseid)); $cachecoursemodinfo->delete($courseid); course_modinfo::clear_instance_cache($courseid); // Update global values too. if ($courseid == $COURSE->id || $courseid == $SITE->id) { $cacherev = $DB->get_field('course', 'cacherev', array('id' => $courseid)); if ($courseid == $COURSE->id) { $COURSE->cacherev = $cacherev; } if ($courseid == $SITE->id) { $SITE->cachrev = $cacherev; } } } if ($clearonly) { return; } if ($courseid) { $select = array('id'=>$courseid); } else { $select = array(); core_php_time_limit::raise(); // this could take a while! MDL-10954 } $rs = $DB->get_recordset("course", $select,'','id,'.join(',', course_modinfo::$cachedfields)); // Rebuild cache for each course. foreach ($rs as $course) { course_modinfo::build_course_cache($course); } $rs->close(); }
/** * Resets cache for the course (or all caches) * To be called from {@link 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(); } }
protected function set_chapters() { $this->chapters = (object) []; $this->chapters->listlarge = $this->course->numsections > 9 ? 'list-large' : ''; $this->chapters->chapters = []; $canviewhidden = has_capability('moodle/course:viewhiddensections', context_course::instance($this->course->id)); $modinfo = get_fast_modinfo($this->course); foreach ($modinfo->get_section_info_all() as $section => $thissection) { if ($section > $this->course->numsections) { continue; } // Students - If course hidden sections completely invisible & section is hidden, and you cannot // see hidden things, bale out. if ($this->course->hiddensections && !$thissection->visible && !$canviewhidden) { continue; } $conditional = $this->is_section_conditional($thissection); $chapter = new course_toc_chapter(); $chapter->outputlink = true; if ($canviewhidden) { // Teachers. if ($conditional) { $chapter->availabilityclass = 'text-danger'; $chapter->availabilitystatus = get_string('conditional', 'theme_snap'); } if (!$thissection->visible) { $chapter->availabilityclass = 'text-warning'; $chapter->availabilitystatus = get_string('notpublished', 'theme_snap'); } } else { // Students. if ($conditional && !$thissection->uservisible && !$thissection->availableinfo) { // Conditional section, totally hidden from user so skip. continue; } if ($conditional && $thissection->availableinfo) { $chapter->availabilityclass = 'text-danger'; $chapter->availabilitystatus = get_string('conditional', 'theme_snap'); } if (!$conditional && !$thissection->visible) { // Hidden section collapsed, so show as text in TOC. $chapter->outputlink = false; $chapter->availabilityclass = 'text-warning'; $chapter->availabilitystatus = get_string('notavailable'); } } $chapter->title = get_section_name($this->course, $section); if ($chapter->title == get_string('general')) { $chapter->title = get_string('introduction', 'theme_snap'); } if ($this->format->is_section_current($section)) { $chapter->iscurrent = true; } if ($chapter->outputlink) { $singlepage = $this->course->format !== 'folderview'; if ($singlepage) { $chapter->url = '#section-' . $section; } else { if ($section > 0) { $chapter->url = course_get_url($this->course, $section, ['navigation' => true, 'sr' => $section]); } else { // We need to create the url for section 0, or a hash will get returned. $chapter->url = new moodle_url('/course/view.php', ['id' => $this->course->id, 'section' => $section]); } } } $chapter->progress = new course_toc_progress($this->course, $thissection); $this->chapters->chapters[] = $chapter; } }