MDL-35768 Added format-specific options to edit course and section forms
- Course format may define additional fields (format options) to store for course and each section - Edit course form allows to edit format-specific options and refreshes their list on format change - Course format may provide it's own form for editing a section - Default form for editing section allows to edit all format-specific fields - Class section_info refactored, it defines magic methods such as __get() to access basic section information and format-specific options (retrieved only on the first request) - format_base::update_course_format_options() allows to watch pre-update state of the course, format_legacy automatically copies the options with the same names between formats
This commit is contained in:
+1
-1
@@ -41,7 +41,7 @@ if ($id) { // editing course
|
||||
print_error('cannoteditsiteform');
|
||||
}
|
||||
|
||||
$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
|
||||
$course = course_get_format($id)->get_course();
|
||||
require_login($course);
|
||||
$category = $DB->get_record('course_categories', array('id'=>$course->category), '*', MUST_EXIST);
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
|
||||
+31
-2
@@ -10,9 +10,11 @@ class course_edit_form extends moodleform {
|
||||
protected $context;
|
||||
|
||||
function definition() {
|
||||
global $USER, $CFG, $DB;
|
||||
global $USER, $CFG, $DB, $PAGE;
|
||||
|
||||
$mform = $this->_form;
|
||||
$PAGE->requires->yui_module('moodle-course-formatchooser', 'M.course.init_formatchooser',
|
||||
array(array('formid' => $mform->getAttribute('id'))));
|
||||
|
||||
$course = $this->_customdata['course']; // this contains the data of this form
|
||||
$category = $this->_customdata['category'];
|
||||
@@ -120,6 +122,10 @@ class course_edit_form extends moodleform {
|
||||
$mform->addHelpButton('format', 'format');
|
||||
$mform->setDefault('format', $courseconfig->format);
|
||||
|
||||
// button to update format-specific options on format change (will be hidden by JavaScript)
|
||||
$mform->registerNoSubmitButton('updatecourseformat');
|
||||
$mform->addElement('submit', 'updatecourseformat', get_string('courseformatudpate'));
|
||||
|
||||
$mform->addElement('select', 'coursedisplay', get_string('coursedisplay'),
|
||||
array(COURSE_DISPLAY_SINGLEPAGE => get_string('coursedisplay_single'),
|
||||
COURSE_DISPLAY_MULTIPAGE => get_string('coursedisplay_multi')));
|
||||
@@ -193,6 +199,9 @@ class course_edit_form extends moodleform {
|
||||
$mform->addElement('select', 'theme', get_string('forcetheme'), $themes);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
$mform->addElement('hidden', 'addcourseformatoptionshere');
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
enrol_course_edit_form($mform, $course, $context);
|
||||
|
||||
@@ -310,8 +319,22 @@ class course_edit_form extends moodleform {
|
||||
$gr_el =& $mform->getElement('defaultgroupingid');
|
||||
$gr_el->load($options);
|
||||
}
|
||||
}
|
||||
|
||||
// add course format options
|
||||
$formatvalue = $mform->getElementValue('format');
|
||||
if (is_array($formatvalue) && !empty($formatvalue)) {
|
||||
$courseformat = course_get_format((object)array('format' => $formatvalue[0]));
|
||||
$newel = $mform->createElement('header', '', get_string('courseformatoptions', 'moodle',
|
||||
$courseformat->get_format_name()));
|
||||
$mform->insertElementBefore($newel, 'addcourseformatoptionshere');
|
||||
|
||||
$elements = $courseformat->create_edit_form_elements($mform);
|
||||
for ($i = 0; $i < count($elements); $i++) {
|
||||
$mform->insertElementBefore($mform->removeElement($elements[$i]->getName(), false),
|
||||
'addcourseformatoptionshere');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// perform some extra moodle validation
|
||||
function validation($data, $files) {
|
||||
@@ -333,6 +356,12 @@ class course_edit_form extends moodleform {
|
||||
|
||||
$errors = array_merge($errors, enrol_course_edit_validation($data, $this->context));
|
||||
|
||||
$courseformat = course_get_format((object)array('format' => $data['format']));
|
||||
$formaterrors = $courseformat->edit_form_validation($data, $files, $errors);
|
||||
if (!empty($formaterrors) && is_array($formaterrors)) {
|
||||
$errors = array_merge($errors, $formaterrors);
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
||||
+28
-48
@@ -16,7 +16,7 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Edit the introduction of a section
|
||||
* Edit the section basic information and availability
|
||||
*
|
||||
* @copyright 1999 Martin Dougiamas http://dougiamas.com
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
@@ -25,78 +25,58 @@
|
||||
|
||||
require_once("../config.php");
|
||||
require_once("lib.php");
|
||||
require_once($CFG->libdir.'/filelib.php');
|
||||
require_once($CFG->libdir . '/gradelib.php');
|
||||
require_once($CFG->libdir . '/completionlib.php');
|
||||
require_once($CFG->libdir . '/conditionlib.php');
|
||||
|
||||
require_once('editsection_form.php');
|
||||
|
||||
$id = required_param('id',PARAM_INT); // Week/topic ID
|
||||
$id = required_param('id', PARAM_INT); // course_sections.id
|
||||
$sectionreturn = optional_param('sr', 0, PARAM_INT);
|
||||
|
||||
$PAGE->set_url('/course/editsection.php', array('id'=>$id, 'sr'=> $sectionreturn));
|
||||
|
||||
$section = $DB->get_record('course_sections', array('id' => $id), '*', MUST_EXIST);
|
||||
$course = $DB->get_record('course', array('id' => $section->course), '*', MUST_EXIST);
|
||||
$sectionnum = $section->section;
|
||||
|
||||
require_login($course);
|
||||
$context = context_course::instance($course->id);
|
||||
require_capability('moodle/course:update', $context);
|
||||
|
||||
// get section_info object with all availability options
|
||||
$sectioninfo = get_fast_modinfo($course)->get_section_info($sectionnum);
|
||||
|
||||
$editoroptions = array('context'=>$context ,'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>false, 'noclean'=>true);
|
||||
$section = file_prepare_standard_editor($section, 'summary', $editoroptions, $context, 'course', 'section', $section->id);
|
||||
$section->usedefaultname = (is_null($section->name));
|
||||
$mform = course_get_format($course->id)->editsection_form($PAGE->url,
|
||||
array('cs' => $sectioninfo, 'editoroptions' => $editoroptions));
|
||||
// set current value, make an editable copy of section_info object
|
||||
// this will retrieve all format-specific options as well
|
||||
$mform->set_data(convert_to_array($sectioninfo));
|
||||
|
||||
if (!empty($CFG->enableavailability)) {
|
||||
// Get section availability conditions from sectioncache.
|
||||
$modinfo = get_fast_modinfo($course);
|
||||
$sectioninfo = $modinfo->get_section_info($section->section);
|
||||
$section->conditionsgrade = $sectioninfo->conditionsgrade;
|
||||
$section->conditionscompletion = $sectioninfo->conditionscompletion;
|
||||
$section->conditionsfield = $sectioninfo->conditionsfield;
|
||||
}
|
||||
|
||||
$mform = new editsection_form($PAGE->url, array('course' => $course, 'editoroptions' => $editoroptions,
|
||||
'cs' => $section, 'showavailability' => $section->showavailability));
|
||||
$mform->set_data($section); // set current value
|
||||
|
||||
$returnurl = course_get_url($course, $sectionreturn);
|
||||
|
||||
/// If data submitted, then process and store.
|
||||
if ($mform->is_cancelled()){
|
||||
redirect($returnurl);
|
||||
|
||||
// form cancelled, return to course
|
||||
redirect(course_get_url($course, $section, array('sr' => $sectionreturn)));
|
||||
} else if ($data = $mform->get_data()) {
|
||||
if (empty($data->usedefaultname)) {
|
||||
$section->name = $data->name;
|
||||
} else {
|
||||
$section->name = null;
|
||||
// data submitted and validated, update and return to course
|
||||
$DB->update_record('course_sections', $data);
|
||||
rebuild_course_cache($course->id, true);
|
||||
if (isset($data->section)) {
|
||||
// usually edit form does not change relative section number but just in case
|
||||
$sectionnum = $data->section;
|
||||
}
|
||||
$data = file_postupdate_standard_editor($data, 'summary', $editoroptions, $context, 'course', 'section', $section->id);
|
||||
$section->summary = $data->summary;
|
||||
$section->summaryformat = $data->summaryformat;
|
||||
if (!empty($CFG->enableavailability)) {
|
||||
$section->availablefrom = $data->availablefrom;
|
||||
$section->availableuntil = $data->availableuntil;
|
||||
if (isset($data->groupingid)) {
|
||||
$section->groupingid = $data->groupingid;
|
||||
}
|
||||
$section->showavailability = $data->showavailability;
|
||||
}
|
||||
$DB->update_record('course_sections', $section);
|
||||
if (!empty($CFG->enableavailability)) {
|
||||
// Update grade and completion conditions
|
||||
condition_info_section::update_section_from_form($section, $data);
|
||||
$sectioninfo = get_fast_modinfo($course)->get_section_info($sectionnum);
|
||||
condition_info_section::update_section_from_form($sectioninfo, $data);
|
||||
rebuild_course_cache($course->id, true);
|
||||
}
|
||||
rebuild_course_cache($course->id);
|
||||
course_get_format($course->id)->update_section_format_options($data);
|
||||
|
||||
add_to_log($course->id, "course", "editsection", "editsection.php?id=$section->id", "$section->section");
|
||||
add_to_log($course->id, "course", "editsection", "editsection.php?id=$id", "$sectionnum");
|
||||
$PAGE->navigation->clear_cache();
|
||||
redirect($returnurl);
|
||||
redirect(course_get_url($course, $section, array('sr' => $sectionreturn)));
|
||||
}
|
||||
|
||||
$sectionname = get_section_name($course, $section);
|
||||
// the edit form is displayed for the first time or there was a validation
|
||||
// error on the previous step. Display the edit form:
|
||||
$sectionname = get_section_name($course, $sectionnum);
|
||||
$stredit = get_string('edita', '', " $sectionname");
|
||||
$strsummaryof = get_string('summaryof', '', " $sectionname");
|
||||
|
||||
|
||||
@@ -5,7 +5,15 @@ if (!defined('MOODLE_INTERNAL')) {
|
||||
}
|
||||
|
||||
require_once($CFG->libdir.'/formslib.php');
|
||||
require_once($CFG->libdir.'/filelib.php');
|
||||
require_once($CFG->libdir.'/completionlib.php');
|
||||
require_once($CFG->libdir.'/gradelib.php');
|
||||
|
||||
/**
|
||||
* Default form for editing course section
|
||||
*
|
||||
* Course format plugins may specify different editing form to use
|
||||
*/
|
||||
class editsection_form extends moodleform {
|
||||
|
||||
function definition() {
|
||||
@@ -28,6 +36,13 @@ class editsection_form extends moodleform {
|
||||
$mform->addElement('hidden', 'id');
|
||||
$mform->setType('id', PARAM_INT);
|
||||
|
||||
// additional fields that course format has defined
|
||||
$courseformat = course_get_format($course);
|
||||
$formatoptions = $courseformat->section_format_options(true);
|
||||
if (!empty($formatoptions)) {
|
||||
$elements = $courseformat->create_edit_form_elements($mform, true);
|
||||
}
|
||||
|
||||
$mform->_registerCancelButton('cancel');
|
||||
}
|
||||
|
||||
@@ -195,8 +210,6 @@ class editsection_form extends moodleform {
|
||||
CONDITION_STUDENTVIEW_HIDE => get_string('showavailabilitysection_hide', 'condition'));
|
||||
$mform->addElement('select', 'showavailability',
|
||||
get_string('showavailabilitysection', 'condition'), $showhide);
|
||||
|
||||
$mform->setDefault('showavailability', $this->_customdata['showavailability']);
|
||||
}
|
||||
|
||||
$this->add_action_buttons();
|
||||
@@ -232,4 +245,47 @@ class editsection_form extends moodleform {
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load in existing data as form defaults
|
||||
*
|
||||
* @param stdClass|array $default_values object or array of default values
|
||||
*/
|
||||
function set_data($default_values) {
|
||||
if (!is_object($default_values)) {
|
||||
// we need object for file_prepare_standard_editor
|
||||
$default_values = (object)$default_values;
|
||||
}
|
||||
$editoroptions = $this->_customdata['editoroptions'];
|
||||
$default_values = file_prepare_standard_editor($default_values, 'summary', $editoroptions,
|
||||
$editoroptions['context'], 'course', 'section', $default_values->id);
|
||||
$default_values->usedefaultname = (is_null($default_values->name));
|
||||
parent::set_data($default_values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return submitted data if properly submitted or returns NULL if validation fails or
|
||||
* if there is no submitted data.
|
||||
*
|
||||
* @return object submitted data; NULL if not valid or not submitted or cancelled
|
||||
*/
|
||||
function get_data() {
|
||||
$data = parent::get_data();
|
||||
if ($data !== null) {
|
||||
$editoroptions = $this->_customdata['editoroptions'];
|
||||
if (!empty($data->usedefaultname)) {
|
||||
$data->name = null;
|
||||
}
|
||||
$data = file_postupdate_standard_editor($data, 'summary', $editoroptions,
|
||||
$editoroptions['context'], 'course', 'section', $data->id);
|
||||
$course = $this->_customdata['course'];
|
||||
foreach (course_get_format($course)->section_format_options() as $option => $unused) {
|
||||
// fix issue with unset checkboxes not being returned at all
|
||||
if (!isset($data->$option)) {
|
||||
$data->$option = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,4 +202,29 @@ class format_legacy extends format_base {
|
||||
}
|
||||
return parent::get_default_blocks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates format options for a course
|
||||
*
|
||||
* Legacy course formats may assume that course format options
|
||||
* ('coursedisplay', 'numsections' and 'hiddensections') are shared between formats.
|
||||
* Therefore we make sure to copy them from the previous format
|
||||
*
|
||||
* @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) {
|
||||
if ($oldcourse !== null) {
|
||||
$data = (array)$data;
|
||||
$oldcourse = (array)$oldcourse;
|
||||
foreach ($this->course_format_options() as $key => $unused) {
|
||||
if (array_key_exists($key, $oldcourse) && !array_key_exists($key, $data)) {
|
||||
$data[$key] = $oldcourse[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->update_format_options($data);
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,8 @@ abstract class format_base {
|
||||
protected $format;
|
||||
/** @var stdClass data for course object, please use {@link format_base::get_course()} */
|
||||
protected $course = false;
|
||||
/** @var array caches format options, please use {@link format_base::get_format_options()} */
|
||||
protected $formatoptions = array();
|
||||
/** @var array cached instances */
|
||||
private static $instances = array();
|
||||
|
||||
@@ -180,6 +182,7 @@ abstract class format_base {
|
||||
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]);
|
||||
}
|
||||
@@ -219,6 +222,16 @@ abstract class format_base {
|
||||
}
|
||||
if ($this->course === false) {
|
||||
$this->course = $DB->get_record('course', array('id' => $this->courseid));
|
||||
$options = $this->get_format_options();
|
||||
foreach ($options as $optionname => $optionvalue) {
|
||||
if (!isset($this->course->$optionname)) {
|
||||
$this->course->$optionname = $optionvalue;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->course;
|
||||
}
|
||||
@@ -411,6 +424,322 @@ abstract class format_base {
|
||||
);
|
||||
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
|
||||
* 'label' - localised human-readable label for the edit form
|
||||
* 'type' - type of the option value (PARAM_INT, PARAM_RAW, etc.)
|
||||
* '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
|
||||
*
|
||||
* Note that all properties except 'default' and 'type' are used only in
|
||||
* {@link format_base::create_edit_form_elements()}, which calls this function with the
|
||||
* argument $foreditform = true
|
||||
*
|
||||
* 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().
|
||||
*
|
||||
* @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 {@link format_base::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 {@link get_fast_modinfo()}. The 'cache' property
|
||||
* is recommended to be set only for fields used in {@link format_base::get_section_name()},
|
||||
* {@link format_base::extend_course_navigation()} and {@link format_base::get_view_url()}
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @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 = $this->get_section($section))) {
|
||||
// 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 {@link course_edit_form::definition_after_data()}
|
||||
*
|
||||
* @param moodleform $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 (is_null($mform->getElementValue($optionname)) && isset($option['default'])) {
|
||||
$mform->setDefault($optionname, $option['default']);
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@link moodleform::get_data()} or array with data
|
||||
* @param null|int 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;
|
||||
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;
|
||||
$data = (array)$data;
|
||||
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 {@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) {
|
||||
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 {@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;
|
||||
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 {@link format_base::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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
-2
@@ -3775,7 +3775,10 @@ function create_course($data, $editoroptions = NULL) {
|
||||
$DB->set_field('course', 'summaryformat', $data->summary_format, array('id'=>$newcourseid));
|
||||
}
|
||||
|
||||
$course = $DB->get_record('course', array('id'=>$newcourseid));
|
||||
// update course format options
|
||||
course_get_format($newcourseid)->update_course_format_options($data);
|
||||
|
||||
$course = course_get_format($newcourseid)->get_course();
|
||||
|
||||
// Setup the blocks
|
||||
blocks_add_default_course_blocks($course);
|
||||
@@ -3843,7 +3846,7 @@ function update_course($data, $editoroptions = NULL) {
|
||||
|
||||
$data->timemodified = time();
|
||||
|
||||
$oldcourse = $DB->get_record('course', array('id'=>$data->id), '*', MUST_EXIST);
|
||||
$oldcourse = course_get_format($data->id)->get_course();
|
||||
$context = context_course::instance($oldcourse->id);
|
||||
|
||||
if ($editoroptions) {
|
||||
@@ -3879,6 +3882,9 @@ function update_course($data, $editoroptions = NULL) {
|
||||
// make sure the modinfo cache is reset
|
||||
rebuild_course_cache($data->id);
|
||||
|
||||
// update course format options with full course data
|
||||
course_get_format($data->id)->update_course_format_options($data, $oldcourse);
|
||||
|
||||
$course = $DB->get_record('course', array('id'=>$data->id));
|
||||
|
||||
if ($movecat) {
|
||||
@@ -3901,6 +3907,14 @@ function update_course($data, $editoroptions = NULL) {
|
||||
|
||||
// Trigger events
|
||||
events_trigger('course_updated', $course);
|
||||
|
||||
if ($oldcourse->format !== $course->format) {
|
||||
// Remove all options stored for the previous format
|
||||
// We assume that new course format migrated everything it needed watching trigger
|
||||
// 'course_updated' and in method format_XXX::update_course_format_options()
|
||||
$DB->delete_records('course_format_options',
|
||||
array('courseid' => $course->id, 'format' => $oldcourse->format));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
YUI.add('moodle-course-formatchooser', function(Y) {
|
||||
var FORMATCHOOSER = function() {
|
||||
FORMATCHOOSER.superclass.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
Y.extend(FORMATCHOOSER, Y.Base, {
|
||||
initializer : function(params) {
|
||||
if (params && params.formid) {
|
||||
var updatebut = Y.one('#'+params.formid+' #id_updatecourseformat');
|
||||
var formatselect = Y.one('#'+params.formid+' #id_format');
|
||||
if (updatebut && formatselect) {
|
||||
updatebut.setStyle('display', 'none');
|
||||
formatselect.on('change', function() {
|
||||
updatebut.simulate('click');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
M.course = M.course || {};
|
||||
M.course.init_formatchooser = function(params) {
|
||||
return new FORMATCHOOSER(params);
|
||||
}
|
||||
}, '@VERSION@', {requires:['base', 'node', 'node-event-simulate']});
|
||||
@@ -887,6 +887,7 @@ class enrol_ldap_plugin extends enrol_plugin {
|
||||
$template = false;
|
||||
if ($this->get_config('template')) {
|
||||
if ($template = $DB->get_record('course', array('shortname'=>$this->get_config('template')))) {
|
||||
$template = fullclone(course_get_format($template)->get_course());
|
||||
unset($template->id); // So we are clear to reinsert the record
|
||||
unset($template->fullname);
|
||||
unset($template->shortname);
|
||||
|
||||
@@ -308,6 +308,8 @@ $string['coursefileswarning'] = 'Course files are deprecated';
|
||||
$string['coursefileswarning_help'] = 'Course files are deprecated since Moodle 2.0, please use external repositories instead as much as possible.';
|
||||
$string['courseformatdata'] = 'Course format data';
|
||||
$string['courseformats'] = 'Course formats';
|
||||
$string['courseformatoptions'] = 'Formatting options for {$a}';
|
||||
$string['courseformatudpate'] = 'Update format';
|
||||
$string['coursegrades'] = 'Course grades';
|
||||
$string['coursehelpcategory'] = 'Position the course on the course listing and may make it easier for students to find it.';
|
||||
$string['coursehelpforce'] = 'Force the course group mode to every activity in the course.';
|
||||
|
||||
+118
-41
@@ -1434,48 +1434,48 @@ class cached_cm_info {
|
||||
* Data about a single section on a course. This contains the fields from the
|
||||
* course_sections table, plus additional data when required.
|
||||
*/
|
||||
class section_info extends stdClass {
|
||||
class section_info implements IteratorAggregate {
|
||||
/**
|
||||
* Section ID - from course_sections table
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
private $_id;
|
||||
|
||||
/**
|
||||
* Course ID - from course_sections table
|
||||
* @var int
|
||||
*/
|
||||
public $course;
|
||||
private $_course;
|
||||
|
||||
/**
|
||||
* Section number - from course_sections table
|
||||
* @var int
|
||||
*/
|
||||
public $section;
|
||||
private $_section;
|
||||
|
||||
/**
|
||||
* Section name if specified - from course_sections table
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
private $_name;
|
||||
|
||||
/**
|
||||
* Section visibility (1 = visible) - from course_sections table
|
||||
* @var int
|
||||
*/
|
||||
public $visible;
|
||||
private $_visible;
|
||||
|
||||
/**
|
||||
* Section summary text if specified - from course_sections table
|
||||
* @var string
|
||||
*/
|
||||
public $summary;
|
||||
private $_summary;
|
||||
|
||||
/**
|
||||
* Section summary text format (FORMAT_xx constant) - from course_sections table
|
||||
* @var int
|
||||
*/
|
||||
public $summaryformat;
|
||||
private $_summaryformat;
|
||||
|
||||
/**
|
||||
* When section is unavailable, this field controls whether it is shown to students (0 =
|
||||
@@ -1483,28 +1483,28 @@ class section_info extends stdClass {
|
||||
* from course_sections table
|
||||
* @var int
|
||||
*/
|
||||
public $showavailability;
|
||||
private $_showavailability;
|
||||
|
||||
/**
|
||||
* Available date for this section (0 if not set, or set to seconds since epoch; before this
|
||||
* date, section does not display to students) - from course_sections table
|
||||
* @var int
|
||||
*/
|
||||
public $availablefrom;
|
||||
private $_availablefrom;
|
||||
|
||||
/**
|
||||
* Available until date for this section (0 if not set, or set to seconds since epoch; from
|
||||
* this date, section does not display to students) - from course_sections table
|
||||
* @var int
|
||||
*/
|
||||
public $availableuntil;
|
||||
private $_availableuntil;
|
||||
|
||||
/**
|
||||
* If section is restricted to users of a particular grouping, this is its id
|
||||
* (0 if not set) - from course_sections table
|
||||
* @var int
|
||||
*/
|
||||
public $groupingid;
|
||||
private $_groupingid;
|
||||
|
||||
/**
|
||||
* Availability conditions for this section based on the completion of
|
||||
@@ -1512,7 +1512,7 @@ class section_info extends stdClass {
|
||||
* for that module) - from cached data in sectioncache field
|
||||
* @var array
|
||||
*/
|
||||
public $conditionscompletion;
|
||||
private $_conditionscompletion;
|
||||
|
||||
/**
|
||||
* Availability conditions for this section based on course grades (array from
|
||||
@@ -1520,14 +1520,20 @@ class section_info extends stdClass {
|
||||
* sectioncache field
|
||||
* @var array
|
||||
*/
|
||||
public $conditionsgrade;
|
||||
private $_conditionsgrade;
|
||||
|
||||
/**
|
||||
* Availability conditions for this section based on user fields
|
||||
* @var array
|
||||
*/
|
||||
private $_conditionsfield;
|
||||
|
||||
/**
|
||||
* True if this section is available to students i.e. if all availability conditions
|
||||
* are met - obtained dynamically
|
||||
* @var bool
|
||||
*/
|
||||
public $available;
|
||||
private $_available;
|
||||
|
||||
/**
|
||||
* If section is not available to students, this string gives information about
|
||||
@@ -1535,7 +1541,7 @@ class section_info extends stdClass {
|
||||
* January 2010') for display on main page - obtained dynamically
|
||||
* @var string
|
||||
*/
|
||||
public $availableinfo;
|
||||
private $_availableinfo;
|
||||
|
||||
/**
|
||||
* True if this section is available to the CURRENT user (for example, if current user
|
||||
@@ -1543,7 +1549,7 @@ class section_info extends stdClass {
|
||||
* visible or not available, so this would be true in that case)
|
||||
* @var bool
|
||||
*/
|
||||
public $uservisible;
|
||||
private $_uservisible;
|
||||
|
||||
/**
|
||||
* Default values for sectioncache fields; if a field has this value, it won't
|
||||
@@ -1575,56 +1581,127 @@ class section_info extends stdClass {
|
||||
global $CFG;
|
||||
|
||||
// Data that is always present
|
||||
$this->id = $data->id;
|
||||
$this->_id = $data->id;
|
||||
|
||||
$defaults = self::$sectioncachedefaults +
|
||||
array('conditionscompletion' => array(),
|
||||
'conditionsgrade' => array(),
|
||||
'conditionsfield' => array());
|
||||
|
||||
// Data that may use default values to save cache size
|
||||
foreach (self::$sectioncachedefaults as $field => $value) {
|
||||
foreach ($defaults as $field => $value) {
|
||||
if (isset($data->{$field})) {
|
||||
$this->{$field} = $data->{$field};
|
||||
$this->{'_'.$field} = $data->{$field};
|
||||
} else {
|
||||
$this->{$field} = $value;
|
||||
$this->{'_'.$field} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Data with array defaults
|
||||
$this->conditionscompletion = isset($data->conditionscompletion)
|
||||
? $data->conditionscompletion : array();
|
||||
$this->conditionsgrade = isset($data->conditionsgrade)
|
||||
? $data->conditionsgrade : array();
|
||||
$this->conditionsfield = isset($data->conditionsfield)
|
||||
? $data->conditionsfield : array();
|
||||
|
||||
// Other data from other places
|
||||
$this->course = $courseid;
|
||||
$this->section = $number;
|
||||
$this->sequence = $sequence;
|
||||
$this->_course = $courseid;
|
||||
$this->_section = $number;
|
||||
$this->_sequence = $sequence;
|
||||
|
||||
// Availability data
|
||||
if (!empty($CFG->enableavailability)) {
|
||||
// Get availability information
|
||||
$ci = new condition_info_section($this);
|
||||
$this->available = $ci->is_available($this->availableinfo, true,
|
||||
$this->_available = $ci->is_available($this->_availableinfo, true,
|
||||
$userid, $modinfo);
|
||||
// Display grouping info if available & not already displaying
|
||||
// (it would already display if current user doesn't have access)
|
||||
// for people with managegroups - same logic/class as grouping label
|
||||
// on individual activities.
|
||||
$context = context_course::instance($courseid);
|
||||
if ($this->availableinfo === '' && $this->groupingid &&
|
||||
if ($this->_availableinfo === '' && $this->_groupingid &&
|
||||
has_capability('moodle/course:managegroups', $context)) {
|
||||
$groupings = groups_get_all_groupings($courseid);
|
||||
$this->availableinfo = html_writer::tag('span', '(' . format_string(
|
||||
$groupings[$this->groupingid]->name, true, array('context' => $context)) .
|
||||
$this->_availableinfo = html_writer::tag('span', '(' . format_string(
|
||||
$groupings[$this->_groupingid]->name, true, array('context' => $context)) .
|
||||
')', array('class' => 'groupinglabel'));
|
||||
}
|
||||
} else {
|
||||
$this->available = true;
|
||||
$this->_available = true;
|
||||
}
|
||||
|
||||
// Update visibility for current user
|
||||
$this->update_user_visible($userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to check if the property is set
|
||||
*
|
||||
* @param string $name name of the property
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name) {
|
||||
if (property_exists($this, '_'.$name)) {
|
||||
return isset($this->{'_'.$name});
|
||||
}
|
||||
$defaultformatoptions = course_get_format($this->_course)->section_format_options();
|
||||
if (array_key_exists($name, $defaultformatoptions)) {
|
||||
$value = $this->__get($name);
|
||||
return isset($value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to check if the property is empty
|
||||
*
|
||||
* @param string $name name of the property
|
||||
* @return bool
|
||||
*/
|
||||
public function __empty($name) {
|
||||
if (property_exists($this, '_'.$name)) {
|
||||
return empty($this->{'_'.$name});
|
||||
}
|
||||
$defaultformatoptions = course_get_format($this->_course)->section_format_options();
|
||||
if (array_key_exists($name, $defaultformatoptions)) {
|
||||
$value = $this->__get($name);
|
||||
return empty($value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to retrieve the property, this is either basic section property
|
||||
* or availability information or additional properties added by course format
|
||||
*
|
||||
* @param string $name name of the property
|
||||
* @return bool
|
||||
*/
|
||||
public function __get($name) {
|
||||
if (property_exists($this, '_'.$name)) {
|
||||
return $this->{'_'.$name};
|
||||
}
|
||||
$defaultformatoptions = course_get_format($this->_course)->section_format_options();
|
||||
// precheck if the option is defined in format to avoid unnecessary DB queries in get_format_options()
|
||||
if (array_key_exists($name, $defaultformatoptions)) {
|
||||
$formatoptions = course_get_format($this->_course)->get_format_options($this);
|
||||
return $formatoptions[$name];
|
||||
}
|
||||
debugging('Invalid section_info property accessed! '.$name);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of IteratorAggregate::getIterator(), allows to cycle through properties
|
||||
* and use {@link convert_to_array()}
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function getIterator() {
|
||||
$ret = array();
|
||||
foreach (get_object_vars($this) as $key => $value) {
|
||||
if (substr($key, 0, 1) == '_') {
|
||||
$ret[substr($key, 1)] = $this->$key;
|
||||
}
|
||||
}
|
||||
$ret = array_merge($ret, course_get_format($this->_course)->get_format_options($this));
|
||||
return new ArrayIterator($ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Works out whether activity is visible *for current user* - if this is false, they
|
||||
* aren't allowed to access it.
|
||||
@@ -1633,11 +1710,11 @@ class section_info extends stdClass {
|
||||
*/
|
||||
private function update_user_visible($userid) {
|
||||
global $CFG;
|
||||
$coursecontext = context_course::instance($this->course);
|
||||
$this->uservisible = true;
|
||||
if ((!$this->visible || !$this->available) &&
|
||||
$coursecontext = context_course::instance($this->_course);
|
||||
$this->_uservisible = true;
|
||||
if ((!$this->_visible || !$this->_available) &&
|
||||
!has_capability('moodle/course:viewhiddensections', $coursecontext, $userid)) {
|
||||
$this->uservisible = false;
|
||||
$this->_uservisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user