Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** This file contains main class for the course format Weeks** @since Moodle 2.0* @package format_weeks* @copyright 2009 Sam Hemelryk* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/defined('MOODLE_INTERNAL') || die();require_once($CFG->dirroot. '/course/format/lib.php');require_once($CFG->dirroot. '/course/lib.php');/*** Main class for the Weeks course format** @package format_weeks* @copyright 2012 Marina Glancy* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class format_weeks extends core_courseformat\base {/*** Returns true if this course format uses sections** @return bool*/public function uses_sections() {return true;}public function uses_course_index() {return true;}public function uses_indentation(): bool {return (get_config('format_weeks', 'indentation')) ? true : false;}/*** Generate the title for this section page* @return string the page title*/public function page_title(): string {return get_string('sectionoutline');}/*** Returns the display name of the given section that the course prefers.** @param int|stdClass $section Section object from database or just field section.section* @return string Display name that the course format prefers, e.g. "Topic 2"*/public function get_section_name($section) {$section = $this->get_section($section);if ((string)$section->name !== '') {// Return the name the user set.return format_string($section->name, true, array('context' => context_course::instance($this->courseid)));} else {return $this->get_default_section_name($section);}}/*** Returns the default section name for the weekly course format.** If the section number is 0, it will use the string with key = section0name from the course format's lang file.* Otherwise, the default format of "[start date] - [end date]" will be returned.** @param stdClass $section Section object from database or just field course_sections section* @return string The default value for the section name.*/public function get_default_section_name($section) {if ($section->section == 0) {// Return the general section.return get_string('section0name', 'format_weeks');} else {$dates = $this->get_section_dates($section);// We subtract 24 hours for display purposes.$dates->end = ($dates->end - 86400);$dateformat = get_string('strftimedateshort');$weekday = userdate($dates->start, $dateformat);$endweekday = userdate($dates->end, $dateformat);return $weekday.' - '.$endweekday;}}/*** Returns the name for the highlighted section.** @return string The name for the highlighted section based on the given course format.*/public function get_section_highlighted_name(): string {return get_string('currentsection', 'format_weeks');}/*** The URL to use for the specified course (with section)** @param int|stdClass $section Section object from database or just field course_sections.section* if omitted the course view page is returned* @param array $options options for view URL. At the moment core uses:* 'navigation' (bool) if true and section not empty, the function returns section page; otherwise, it returns course page.* 'sr' (int) used by course formats to specify to which section to return* @return null|moodle_url*/public function get_view_url($section, $options = array()) {$course = $this->get_course();if (array_key_exists('sr', $options) && !is_null($options['sr'])) {$sectionno = $options['sr'];} else if (is_object($section)) {$sectionno = $section->section;} else {$sectionno = $section;}if ((!empty($options['navigation']) || array_key_exists('sr', $options)) && $sectionno !== null) {// Display section on separate page.$sectioninfo = $this->get_section($sectionno);return new moodle_url('/course/section.php', ['id' => $sectioninfo->id]);}return new moodle_url('/course/view.php', ['id' => $course->id]);}/*** 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() {$ajaxsupport = new stdClass();$ajaxsupport->capable = true;return $ajaxsupport;}public function supports_components() {return true;}/*** 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 navigationif ($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);// We want to remove the general section if it is empty.$modinfo = get_fast_modinfo($this->get_course());$sections = $modinfo->get_sections();if (!isset($sections[0])) {// The general section is empty to find the navigation node for it we need to get its ID.$section = $modinfo->get_section_info(0);$generalsection = $node->get($section->id, navigation_node::TYPE_SECTION);if ($generalsection) {// We found the node - now remove it.$generalsection->remove();}}}/*** Custom action after section has been moved in AJAX mode** Used in course/rest.php** @return array This will be passed in ajax respose*/function ajax_section_move() {global $PAGE;$titles = array();$current = -1;$course = $this->get_course();$modinfo = get_fast_modinfo($course);$renderer = $this->get_renderer($PAGE);if ($renderer && ($sections = $modinfo->get_section_info_all())) {foreach ($sections as $number => $section) {$titles[$number] = $renderer->section_title($section, $course);if ($this->is_section_current($section)) {$current = $number;}}}return array('sectiontitles' => $titles, 'current' => $current, 'action' => 'move');}/*** Returns the list of blocks to be automatically added for the newly created course** @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() {return array(BLOCK_POS_LEFT => array(),BLOCK_POS_RIGHT => array());}/*** Definitions of the additional options that this course format uses for course** Weeks format uses the following options:* - coursedisplay* - hiddensections* - automaticenddate** @param bool $foreditform* @return array of options*/public function course_format_options($foreditform = false) {static $courseformatoptions = false;if ($courseformatoptions === false) {$courseconfig = get_config('moodlecourse');$courseformatoptions = array('hiddensections' => array('default' => $courseconfig->hiddensections,'type' => PARAM_INT,),'coursedisplay' => array('default' => $courseconfig->coursedisplay ?? COURSE_DISPLAY_SINGLEPAGE,'type' => PARAM_INT,),'automaticenddate' => array('default' => 1,'type' => PARAM_BOOL,),);}if ($foreditform && !isset($courseformatoptions['coursedisplay']['label'])) {$courseformatoptionsedit = array('hiddensections' => array('label' => new lang_string('hiddensections'),'help' => 'hiddensections','help_component' => 'moodle','element_type' => 'select','element_attributes' => array(array(0 => new lang_string('hiddensectionscollapsed'),1 => new lang_string('hiddensectionsinvisible'))),),'coursedisplay' => array('label' => new lang_string('coursedisplay'),'element_type' => 'select','element_attributes' => array(array(COURSE_DISPLAY_SINGLEPAGE => new lang_string('coursedisplay_single'),COURSE_DISPLAY_MULTIPAGE => new lang_string('coursedisplay_multi'))),'help' => 'coursedisplay','help_component' => 'moodle',),'automaticenddate' => array('label' => new lang_string('automaticenddate', 'format_weeks'),'help' => 'automaticenddate','help_component' => 'format_weeks','element_type' => 'advcheckbox',));$courseformatoptions = array_merge_recursive($courseformatoptions, $courseformatoptionsedit);}return $courseformatoptions;}/*** 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) {global $COURSE;$elements = parent::create_edit_form_elements($mform, $forsection);if (!$forsection && (empty($COURSE->id) || $COURSE->id == SITEID)) {// Add "numsections" element to the create course form - it will force new course to be prepopulated// with empty sections.// The "Number of sections" option is no longer available when editing course, instead teachers should// delete and add sections when needed.$courseconfig = get_config('moodlecourse');$max = (int)$courseconfig->maxsections;$element = $mform->addElement('select', 'numsections', get_string('numberweeks'), range(0, $max ?: 52));$mform->setType('numsections', PARAM_INT);if (is_null($mform->getElementValue('numsections'))) {$mform->setDefault('numsections', $courseconfig->numsections);}array_unshift($elements, $element);}// Re-order things.$mform->insertElementBefore($mform->removeElement('automaticenddate', false), 'idnumber');$mform->disabledIf('enddate', 'automaticenddate', 'checked');foreach ($elements as $key => $element) {if ($element->getName() == 'automaticenddate') {unset($elements[$key]);}}return $elements;}/*** Updates format options for a course** In case if course format was changed to 'weeks', we try to copy options* 'coursedisplay', 'numsections' and 'hiddensections' from the previous format.* If previous course format did not have 'numsections' option, we populate it with the* current number of sections** @param stdClass|array $data return value from {@link moodleform::get_data()} or array with data* @param stdClass $oldcourse if this function is called from {@link 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) {global $DB;$data = (array)$data;if ($oldcourse !== null) {$oldcourse = (array)$oldcourse;$options = $this->course_format_options();foreach ($options as $key => $unused) {if (!array_key_exists($key, $data)) {if (array_key_exists($key, $oldcourse)) {$data[$key] = $oldcourse[$key];}}}}return $this->update_format_options($data);}/*** Return the start and end date of the passed section** @param int|stdClass|section_info $section section to get the dates for* @param int $startdate Force course start date, useful when the course is not yet created* @return stdClass property start for startdate, property end for enddate*/public function get_section_dates($section, $startdate = false) {global $USER;if ($startdate === false) {$course = $this->get_course();$userdates = course_get_course_dates_for_user_id($course, $USER->id);$startdate = $userdates['start'];}if (is_object($section)) {$sectionnum = $section->section;} else {$sectionnum = $section;}// Create a DateTime object for the start date.$startdateobj = new DateTime("@$startdate");$startdateobj->setTimezone(core_date::get_user_timezone_object());// Calculate the interval for one week.$oneweekinterval = new DateInterval('P7D');// Calculate the interval for the specified number of sections.for ($i = 1; $i < $sectionnum; $i++) {$startdateobj->add($oneweekinterval);}// Calculate the end date.$enddateobj = clone $startdateobj;$enddateobj->add($oneweekinterval);$dates = new stdClass();$dates->start = $startdateobj->getTimestamp();$dates->end = $enddateobj->getTimestamp();return $dates;}/*** Returns true if the specified week is current** @param int|stdClass|section_info $section* @return bool*/public function is_section_current($section) {if (is_object($section)) {$sectionnum = $section->section;} else {$sectionnum = $section;}if ($sectionnum < 1) {return false;}$timenow = time();$dates = $this->get_section_dates($section);return (($timenow >= $dates->start) && ($timenow < $dates->end));}/*** Whether this format allows to delete sections** Do not call this function directly, instead use {@link course_can_delete_section()}** @param int|stdClass|section_info $section* @return bool*/public function can_delete_section($section) {return true;}/*** Returns the default end date for weeks course format.** @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['startdate'])) {$fieldnames['startdate'] = 'startdate';}if (empty($fieldnames['numsections'])) {$fieldnames['numsections'] = 'numsections';}$startdate = $this->get_form_start_date($mform, $fieldnames);if ($mform->elementExists($fieldnames['numsections'])) {$numsections = $mform->getElementValue($fieldnames['numsections']);$numsections = $mform->getElement($fieldnames['numsections'])->exportValue($numsections);} else if ($this->get_courseid()) {// For existing courses get the number of sections.$numsections = $this->get_last_section_number();} else {// Fallback to the default value for new courses.$numsections = get_config('moodlecourse', $fieldnames['numsections']);}// Final week's last day.$dates = $this->get_section_dates(intval($numsections), $startdate);return $dates->end;}/*** Indicates whether the course format supports the creation of a news forum.** @return bool*/public function supports_news() {return true;}/*** 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) {// Allow the third visibility state inside visible sections or in section 0.return !$section->section || $section->visible;}public function section_action($section, $action, $sr) {global $PAGE;// Call the parent method and return the new content for .section_availability element.$rv = parent::section_action($section, $action, $sr);$renderer = $PAGE->get_renderer('format_weeks');if (!($section instanceof section_info)) {$modinfo = course_modinfo::instance($this->courseid);$section = $modinfo->get_section_info($section->section);}$elementclass = $this->get_output_classname('content\\section\\availability');$availability = new $elementclass($this, $section);$rv['section_availability'] = $renderer->render($availability);return $rv;}/*** Updates the end date for a course in weeks format if option automaticenddate is set.** This method is called from event observers and it can not use any modinfo or format caches because* events are triggered before the caches are reset.** @param int $courseid*/public static function update_end_date($courseid) {global $DB, $COURSE;// Use one DB query to retrieve necessary fields in course, value for automaticenddate and number of the last// section. This query will also validate that the course is indeed in 'weeks' format.$insql = "SELECT c.id, c.format, c.startdate, c.enddate, MAX(s.section) AS lastsectionFROM {course} cJOIN {course_sections} sON s.course = c.idWHERE c.format = :formatAND c.id = :courseidGROUP BY c.id, c.format, c.startdate, c.enddate";$sql = "SELECT co.id, co.format, co.startdate, co.enddate, co.lastsection, fo.value AS automaticenddateFROM ($insql) coLEFT JOIN {course_format_options} foON fo.courseid = co.idAND fo.format = co.formatAND fo.name = :optionnameAND fo.sectionid = 0";$course = $DB->get_record_sql($sql,['optionname' => 'automaticenddate', 'format' => 'weeks', 'courseid' => $courseid]);if (!$course) {// Looks like it is a course in a different format, nothing to do here.return;}// Create an instance of this class and mock the course object.$format = new format_weeks('weeks', $courseid);$format->course = $course;// If automaticenddate is not specified take the default value.if (!isset($course->automaticenddate)) {$defaults = $format->course_format_options();$course->automaticenddate = $defaults['automaticenddate']['default'];}// Check that the course format for setting an automatic date is set.if (!empty($course->automaticenddate)) {// Get the final week's last day.$dates = $format->get_section_dates((int)$course->lastsection);// Set the course end date.if ($course->enddate != $dates->end) {$DB->set_field('course', 'enddate', $dates->end, array('id' => $course->id));if (isset($COURSE->id) && $COURSE->id == $courseid) {$COURSE->enddate = $dates->end;}}}}/*** Return the plugin configs for external functions.** @return array the list of configuration settings* @since Moodle 3.5*/public function get_config_for_external() {// Return everything (nothing to hide).$formatoptions = $this->get_format_options();$formatoptions['indentation'] = get_config('format_weeks', 'indentation');return $formatoptions;}/*** Get the required javascript files for the course format.** @return array The list of javascript files required by the course format.*/public function get_required_jsfiles(): array {return [];}}/*** Implements callback inplace_editable() allowing to edit values in-place** @param string $itemtype* @param int $itemid* @param mixed $newvalue* @return \core\output\inplace_editable*/function format_weeks_inplace_editable($itemtype, $itemid, $newvalue) {global $DB, $CFG;require_once($CFG->dirroot . '/course/lib.php');if ($itemtype === 'sectionname' || $itemtype === 'sectionnamenl') {$section = $DB->get_record_sql('SELECT s.* FROM {course_sections} s JOIN {course} c ON s.course = c.id WHERE s.id = ? AND c.format = ?',array($itemid, 'weeks'), MUST_EXIST);return course_get_format($section->course)->inplace_editable_update_section_name($section, $itemtype, $newvalue);}}