Merge branch 'wip-MDL-34859-master' of git://github.com/marinaglancy/moodle

This commit is contained in:
Dan Poltawski 2017-03-20 07:54:22 +00:00
commit 48f5c05019
29 changed files with 978 additions and 83 deletions

View File

@ -343,4 +343,105 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
//$temp->add(new admin_setting_configcheckbox('backup/backup_auto_blogs', new lang_string('blogs', 'blog'), new lang_string('backupblogshelp','blog'), 0));
$ADMIN->add('backups', $temp);
// Create a page for general restore configuration and defaults.
$temp = new admin_settingpage('restoregeneralsettings', new lang_string('generalrestoredefaults', 'backup'));
// General restore defaults.
$temp->add(new admin_setting_heading('generalsettings', new lang_string('generalrestoresettings', 'backup'), ''));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_users',
new lang_string('generalusers', 'backup'), new lang_string('configrestoreusers', 'backup'),
array('value' => 1, 'locked' => 0)));
// Can not use actual constants here because we'd need to include 100 of backup/restore files.
$options = [
0/*backup::ENROL_NEVER*/ => get_string('rootsettingenrolments_never', 'backup'),
1/*backup::ENROL_WITHUSERS*/ => get_string('rootsettingenrolments_withusers', 'backup'),
2/*backup::ENROL_ALWAYS*/ => get_string('rootsettingenrolments_always', 'backup'),
];
$temp->add(new admin_setting_configselect_with_lock('restore/restore_general_enrolments',
new lang_string('generalenrolments', 'backup'), new lang_string('configrestoreenrolments', 'backup'),
array('value' => 1/*backup::ENROL_WITHUSERS*/, 'locked' => 0), $options));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_role_assignments',
new lang_string('generalroleassignments', 'backup'),
new lang_string('configrestoreroleassignments', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_activities',
new lang_string('generalactivities', 'backup'),
new lang_string('configrestoreactivities', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_blocks',
new lang_string('generalblocks', 'backup'),
new lang_string('configrestoreblocks', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_filters',
new lang_string('generalfilters', 'backup'),
new lang_string('configrestorefilters', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_comments',
new lang_string('generalcomments', 'backup'),
new lang_string('configrestorecomments', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_badges',
new lang_string('generalbadges', 'backup'),
new lang_string('configrestorebadges', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_calendarevents',
new lang_string('generalcalendarevents', 'backup'),
new lang_string('configrestorecalendarevents', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_userscompletion',
new lang_string('generaluserscompletion', 'backup'),
new lang_string('configrestoreuserscompletion', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_logs',
new lang_string('generallogs', 'backup'),
new lang_string('configrestorelogs', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_histories',
new lang_string('generalhistories', 'backup'),
new lang_string('configrestorehistories', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_groups',
new lang_string('generalgroups', 'backup'), new lang_string('configrestoregroups', 'backup'),
array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_competencies',
new lang_string('generalcompetencies', 'backup'),
new lang_string('configrestorecompetencies', 'backup'), array('value' => 1, 'locked' => 0)));
// Restore defaults when merging into another course.
$temp->add(new admin_setting_heading('mergerestoredefaults', new lang_string('mergerestoredefaults', 'backup'), ''));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_merge_overwrite_conf',
new lang_string('setting_overwrite_conf', 'backup'),
new lang_string('config_overwrite_conf', 'backup'), array('value' => 0, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_merge_course_fullname',
new lang_string('setting_overwrite_course_fullname', 'backup'),
new lang_string('config_overwrite_course_fullname', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_merge_course_shortname',
new lang_string('setting_overwrite_course_shortname', 'backup'),
new lang_string('config_overwrite_course_shortname', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_merge_course_startdate',
new lang_string('setting_overwrite_course_startdate', 'backup'),
new lang_string('config_overwrite_course_startdate', 'backup'), array('value' => 1, 'locked' => 0)));
// Restore defaults when replacing course contents.
$temp->add(new admin_setting_heading('replacerestoredefaults', new lang_string('replacerestoredefaults', 'backup'), ''));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_replace_overwrite_conf',
new lang_string('setting_overwrite_conf', 'backup'),
new lang_string('config_overwrite_conf', 'backup'), array('value' => 0, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_replace_course_fullname',
new lang_string('setting_overwrite_course_fullname', 'backup'),
new lang_string('config_overwrite_course_fullname', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_replace_course_shortname',
new lang_string('setting_overwrite_course_shortname', 'backup'),
new lang_string('config_overwrite_course_shortname', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_replace_course_startdate',
new lang_string('setting_overwrite_course_startdate', 'backup'),
new lang_string('config_overwrite_course_startdate', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configselect_with_lock('restore/restore_replace_keep_roles_and_enrolments',
new lang_string('setting_keep_roles_and_enrolments', 'backup'),
new lang_string('config_keep_roles_and_enrolments', 'backup'), array('value' => 0, 'locked' => 0),
array(1 => get_string('yes'), 0 => get_string('no'))));
$temp->add(new admin_setting_configselect_with_lock('restore/restore_replace_keep_groups_and_groupings',
new lang_string('setting_keep_groups_and_groupings', 'backup'),
new lang_string('config_keep_groups_and_groupings', 'backup'), array('value' => 0, 'locked' => 0),
array(1 => get_string('yes'), 0 => get_string('no'))));
$ADMIN->add('backups', $temp);
}

View File

@ -741,6 +741,33 @@ class tool_uploadcourse_course_testcase extends advanced_testcase {
$this->assertTrue($found);
}
/**
* Test that specifying course template respects default restore settings
*/
public function test_restore_file_settings() {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
// Set admin config setting so that activities are not restored by default.
set_config('restore_general_activities', 0, 'restore');
$c1 = $this->getDataGenerator()->create_course();
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => 'A1', 'backupfile' => __DIR__ . '/fixtures/backup.mbz',
'summary' => 'A', 'category' => 1, 'fullname' => 'A1', 'templatecourse' => $c1->shortname);
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertTrue($co->prepare());
$co->proceed();
$course = $DB->get_record('course', array('shortname' => 'A1'));
// Make sure the glossary is not restored.
$modinfo = get_fast_modinfo($course);
$this->assertEmpty($modinfo->get_instances_of('glossary'));
}
public function test_restore_invalid_file() {
$this->resetAfterTest();

View File

@ -142,6 +142,9 @@ class restore_controller extends base_controller {
// Load plan
$this->load_plan();
// Apply all default settings (based on type/format/mode).
$this->apply_defaults();
// Perform all initial security checks and apply (2nd param) them to settings automatically
restore_check::check_security($this, true);
@ -510,6 +513,15 @@ class restore_controller extends base_controller {
$this->plan->build(); // Build plan for this controller
$this->set_status(backup::STATUS_PLANNED);
}
/**
* Apply defaults from the global admin settings
*/
protected function apply_defaults() {
$this->log('applying restore defaults', backup::LOG_DEBUG);
restore_controller_dbops::apply_config_defaults($this);
$this->set_status(backup::STATUS_CONFIGURED);
}
}
/*

View File

@ -162,17 +162,37 @@ class restore_course_task extends restore_task {
*/
protected function define_settings() {
//$name, $vtype, $value = null, $visibility = self::VISIBLE, $status = self::NOT_LOCKED
$fullname = new restore_course_generic_text_setting('course_fullname', base_setting::IS_TEXT, $this->get_info()->original_course_fullname);
$fullname->get_ui()->set_label(get_string('setting_course_fullname', 'backup'));
// Define overwrite_conf to decide if course configuration will be restored over existing one.
$overwrite = new restore_course_overwrite_conf_setting('overwrite_conf', base_setting::IS_BOOLEAN, false);
$overwrite->set_ui(new backup_setting_ui_select($overwrite, $overwrite->get_name(),
array(1 => get_string('yes'), 0 => get_string('no'))));
$overwrite->get_ui()->set_label(get_string('setting_overwrite_conf', 'backup'));
if ($this->get_target() == backup::TARGET_NEW_COURSE) {
$overwrite->set_value(true);
$overwrite->set_status(backup_setting::LOCKED_BY_CONFIG);
$overwrite->set_visibility(backup_setting::HIDDEN);
$course = (object)['fullname' => null, 'shortname' => null, 'startdate' => null];
} else {
$course = get_course($this->get_courseid());
}
$this->add_setting($overwrite);
$fullnamedefaultvalue = $this->get_info()->original_course_fullname;
$fullname = new restore_course_defaultcustom_setting('course_fullname', base_setting::IS_TEXT, $fullnamedefaultvalue);
$fullname->set_ui(new backup_setting_ui_defaultcustom($fullname, get_string('setting_course_fullname', 'backup'),
['customvalue' => $fullnamedefaultvalue, 'defaultvalue' => $course->fullname]));
$this->add_setting($fullname);
$shortname = new restore_course_generic_text_setting('course_shortname', base_setting::IS_TEXT, $this->get_info()->original_course_shortname);
$shortname->get_ui()->set_label(get_string('setting_course_shortname', 'backup'));
$shortnamedefaultvalue = $this->get_info()->original_course_shortname;
$shortname = new restore_course_defaultcustom_setting('course_shortname', base_setting::IS_TEXT, $shortnamedefaultvalue);
$shortname->set_ui(new backup_setting_ui_defaultcustom($shortname, get_string('setting_course_shortname', 'backup'),
['customvalue' => $shortnamedefaultvalue, 'defaultvalue' => $course->shortname]));
$this->add_setting($shortname);
$startdate = new restore_course_generic_text_setting('course_startdate', base_setting::IS_INTEGER, $this->get_info()->original_course_startdate);
$startdate->set_ui(new backup_setting_ui_dateselector($startdate, get_string('setting_course_startdate', 'backup')));
$startdatedefaultvalue = $this->get_info()->original_course_startdate;
$startdate = new restore_course_defaultcustom_setting('course_startdate', base_setting::IS_INTEGER, $startdatedefaultvalue);
$startdate->set_ui(new backup_setting_ui_defaultcustom($startdate, get_string('setting_course_startdate', 'backup'),
['customvalue' => $startdatedefaultvalue, 'defaultvalue' => $course->startdate, 'type' => 'date_selector']));
$this->add_setting($startdate);
$keep_enrols = new restore_course_generic_setting('keep_roles_and_enrolments', base_setting::IS_BOOLEAN, false);
@ -195,16 +215,5 @@ class restore_course_task extends restore_task {
}
$this->add_setting($keep_groups);
// Define overwrite_conf to decide if course configuration will be restored over existing one
$overwrite = new restore_course_overwrite_conf_setting('overwrite_conf', base_setting::IS_BOOLEAN, false);
$overwrite->set_ui(new backup_setting_ui_select($overwrite, $overwrite->get_name(), array(1=>get_string('yes'), 0=>get_string('no'))));
$overwrite->get_ui()->set_label(get_string('setting_overwriteconf', 'backup'));
if ($this->get_target() == backup::TARGET_NEW_COURSE) {
$overwrite->set_value(true);
$overwrite->set_status(backup_setting::LOCKED_BY_CONFIG);
$overwrite->set_visibility(backup_setting::HIDDEN);
}
$this->add_setting($overwrite);
}
}

View File

@ -143,6 +143,41 @@ class restore_course_generic_setting extends course_backup_setting {}
*/
class restore_course_overwrite_conf_setting extends restore_course_generic_setting {}
/**
* Setting to switch between current and new course name/startdate
*
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_course_defaultcustom_setting extends restore_course_generic_setting {
/**
* Validates that the value $value has type $vtype
* @param int $vtype
* @param mixed $value
* @return mixed
*/
public function validate_value($vtype, $value) {
if ($value === false) {
// Value "false" means default and is allowed for this setting type even if it does not match $vtype.
return $value;
}
return parent::validate_value($vtype, $value);
}
/**
* Special method for this element only. When value is "false" returns the default value.
* @return mixed
*/
public function get_normalized_value() {
$value = $this->get_value();
if ($value === false && $this->get_ui() instanceof backup_setting_ui_defaultcustom) {
$attributes = $this->get_ui()->get_attributes();
return $attributes['defaultvalue'];
}
return $value;
}
}
class restore_course_generic_text_setting extends restore_course_generic_setting {

View File

@ -1810,36 +1810,30 @@ class restore_course_structure_step extends restore_structure_step {
$context = context::instance_by_id($this->task->get_contextid());
$userid = $this->task->get_userid();
$target = $this->get_task()->get_target();
$isnewcourse = $target != backup::TARGET_CURRENT_ADDING && $target != backup::TARGET_EXISTING_ADDING;
$isnewcourse = $target == backup::TARGET_NEW_COURSE;
// When restoring to a new course we can set all the things except for the ID number.
$canchangeidnumber = $isnewcourse || has_capability('moodle/course:changeidnumber', $context, $userid);
$canchangeshortname = $isnewcourse || has_capability('moodle/course:changeshortname', $context, $userid);
$canchangefullname = $isnewcourse || has_capability('moodle/course:changefullname', $context, $userid);
$canchangesummary = $isnewcourse || has_capability('moodle/course:changesummary', $context, $userid);
$data = (object)$data;
$data->id = $this->get_courseid();
// Calculate final course names, to avoid dupes.
$fullname = $this->get_setting_value('course_fullname');
$shortname = $this->get_setting_value('course_shortname');
$startdate = $this->get_setting_value('course_startdate');
// Calculate final course names, to avoid dupes.
list($fullname, $shortname) = restore_dbops::calculate_course_names($this->get_courseid(), $fullname, $shortname);
if ($canchangefullname) {
$data->fullname = $fullname;
} else {
list($data->fullname, $data->shortname) = restore_dbops::calculate_course_names($this->get_courseid(),
$fullname === false ? $data->fullname : $fullname,
$shortname === false ? $data->shortname : $shortname);
// Do not modify the course names at all when merging and user selected to keep the names (or prohibited by cap).
if (!$isnewcourse && $fullname === false) {
unset($data->fullname);
}
if ($canchangeshortname) {
$data->shortname = $shortname;
} else {
if (!$isnewcourse && $shortname === false) {
unset($data->shortname);
}
// Unset summary if user can't change it.
if (!$canchangesummary) {
unset($data->summary);
unset($data->summaryformat);

View File

@ -176,6 +176,8 @@ abstract class restore_check {
if (!$hasrolldatescap) {
$startdatesetting = $restore_controller->get_plan()->get_setting('course_startdate');
if ($startdatesetting) {
$startdatesetting->set_status(base_setting::NOT_LOCKED); // Permission lock overrides config lock.
$startdatesetting->set_value(false);
$startdatesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
}
}
@ -185,6 +187,8 @@ abstract class restore_check {
$haschangefullnamecap = has_capability('moodle/course:changefullname', $coursectx, $userid);
if (!$haschangefullnamecap) {
$fullnamesetting = $restore_controller->get_plan()->get_setting('course_fullname');
$fullnamesetting->set_status(base_setting::NOT_LOCKED); // Permission lock overrides config lock.
$fullnamesetting->set_value(false);
$fullnamesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
}
@ -193,6 +197,8 @@ abstract class restore_check {
$haschangeshortnamecap = has_capability('moodle/course:changeshortname', $coursectx, $userid);
if (!$haschangeshortnamecap) {
$shortnamesetting = $restore_controller->get_plan()->get_setting('course_shortname');
$shortnamesetting->set_status(base_setting::NOT_LOCKED); // Permission lock overrides config lock.
$shortnamesetting->set_value(false);
$shortnamesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
}
@ -201,6 +207,8 @@ abstract class restore_check {
$hasupdatecap = has_capability('moodle/course:update', $coursectx, $userid);
if (!$hasupdatecap) {
$overwritesetting = $restore_controller->get_plan()->get_setting('overwrite_conf');
$overwritesetting->set_status(base_setting::NOT_LOCKED); // Permission lock overrides config lock.
$overwritesetting->set_value(false);
$overwritesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
}

View File

@ -134,4 +134,132 @@ abstract class restore_controller_dbops extends restore_dbops {
// Invalidate the backup_ids caches.
restore_dbops::reset_backup_ids_cached();
}
/**
* Sets the default values for the settings in a restore operation
*
* @param restore_controller $controller
*/
public static function apply_config_defaults(restore_controller $controller) {
$settings = array(
'restore_general_users' => 'users',
'restore_general_enrolments' => 'enrolments',
'restore_general_role_assignments' => 'role_assignments',
'restore_general_activities' => 'activities',
'restore_general_blocks' => 'blocks',
'restore_general_filters' => 'filters',
'restore_general_comments' => 'comments',
'restore_general_badges' => 'badges',
'restore_general_calendarevents' => 'calendarevents',
'restore_general_userscompletion' => 'userscompletion',
'restore_general_logs' => 'logs',
'restore_general_histories' => 'grade_histories',
'restore_general_questionbank' => 'questionbank',
'restore_general_groups' => 'groups',
'restore_general_competencies' => 'competencies'
);
self::apply_admin_config_defaults($controller, $settings, true);
$target = $controller->get_target();
if ($target == backup::TARGET_EXISTING_ADDING || $target == backup::TARGET_CURRENT_ADDING) {
$settings = array(
'restore_merge_overwrite_conf' => 'overwrite_conf',
'restore_merge_course_fullname' => 'course_fullname',
'restore_merge_course_shortname' => 'course_shortname',
'restore_merge_course_startdate' => 'course_startdate',
);
self::apply_admin_config_defaults($controller, $settings, true);
}
if ($target == backup::TARGET_EXISTING_DELETING || $target == backup::TARGET_CURRENT_DELETING) {
$settings = array(
'restore_replace_overwrite_conf' => 'overwrite_conf',
'restore_replace_course_fullname' => 'course_fullname',
'restore_replace_course_shortname' => 'course_shortname',
'restore_replace_course_startdate' => 'course_startdate',
'restore_replace_keep_roles_and_enrolments' => 'keep_roles_and_enrolments',
'restore_replace_keep_groups_and_groupings' => 'keep_groups_and_groupings',
);
self::apply_admin_config_defaults($controller, $settings, true);
}
// Add some dependencies.
$plan = $controller->get_plan();
if ($plan->setting_exists('overwrite_conf')) {
/** @var restore_course_overwrite_conf_setting $overwriteconf */
$overwriteconf = $plan->get_setting('overwrite_conf');
if ($overwriteconf->get_visibility()) {
foreach (['course_fullname', 'course_shortname', 'course_startdate'] as $settingname) {
if ($plan->setting_exists($settingname)) {
$setting = $plan->get_setting($settingname);
$overwriteconf->add_dependency($setting, setting_dependency::DISABLED_FALSE,
array('defaultvalue' => $setting->get_value()));
}
}
}
}
}
/**
* Returns the default value to be used for a setting from the admin restore config
*
* @param string $config
* @param backup_setting $setting
* @return mixed
*/
private static function get_setting_default($config, $setting) {
$value = get_config('restore', $config);
if (in_array($setting->get_name(), ['course_fullname', 'course_shortname', 'course_startdate']) &&
$setting->get_ui() instanceof backup_setting_ui_defaultcustom) {
// Special case - admin config settings course_fullname, etc. are boolean and the restore settings are strings.
$value = (bool)$value;
if ($value) {
$attributes = $setting->get_ui()->get_attributes();
$value = $attributes['customvalue'];
}
}
if ($setting->get_ui() instanceof backup_setting_ui_select) {
// Make sure the value is a valid option in the select element, otherwise just pick the first from the options list.
// Example: enrolments dropdown may not have the "enrol_withusers" option because users info can not be restored.
$options = array_keys($setting->get_ui()->get_values());
if (!in_array($value, $options)) {
$value = reset($options);
}
}
return $value;
}
/**
* Sets the controller settings default values from the admin config.
*
* @param restore_controller $controller
* @param array $settings a map from admin config names to setting names (Config name => Setting name)
* @param boolean $uselocks whether "locked" admin settings should be honoured
*/
private static function apply_admin_config_defaults(restore_controller $controller, array $settings, $uselocks) {
$plan = $controller->get_plan();
foreach ($settings as $config => $settingname) {
if ($plan->setting_exists($settingname)) {
$setting = $plan->get_setting($settingname);
$value = self::get_setting_default($config, $setting);
$locked = (get_config('restore', $config . '_locked') == true);
// We can only update the setting if it isn't already locked by config or permission.
if ($setting->get_status() != base_setting::LOCKED_BY_CONFIG
&& $setting->get_status() != base_setting::LOCKED_BY_PERMISSION
&& $setting->get_ui()->is_changeable()) {
$setting->set_value($value);
if ($uselocks && $locked) {
$setting->set_status(base_setting::LOCKED_BY_CONFIG);
}
}
} else {
$controller->log('Unknown setting: ' . $settingname, BACKUP::LOG_DEBUG);
}
}
}
}

View File

@ -113,6 +113,7 @@ abstract class base_plan implements checksumable, executable {
* that are, by definition, unique by name.
*
* @param string $name name of the setting
* @return base_setting
* @throws base_plan_exception if setting name is not found.
*/
public function get_setting($name) {

View File

@ -70,7 +70,12 @@ abstract class restore_step extends base_step {
$original = $this->task->get_info()->original_course_startdate;
$setting = 0;
if ($this->setting_exists('course_startdate')) { // Seting may not exist (MDL-25019).
$setting = $this->get_setting_value('course_startdate');
$settingobject = $this->task->get_setting('course_startdate');
if (method_exists($settingobject, 'get_normalized_value')) {
$setting = $settingobject->get_normalized_value();
} else {
$setting = $settingobject->get_value();
}
}
if (empty($original) || empty($setting)) {
@ -81,11 +86,6 @@ abstract class restore_step extends base_step {
// Less than 24h of difference, offset = 0 (this avoids some problems with timezones).
$cache[$this->get_restoreid()] = 0;
} else if (!has_capability('moodle/restore:rolldates',
context_course::instance($this->get_courseid()), $this->task->get_userid())) {
// Re-enforce 'moodle/restore:rolldates' capability for the user in the course, just in case.
$cache[$this->get_restoreid()] = 0;
} else {
// Arrived here, let's calculate the real offset.
$cache[$this->get_restoreid()] = $setting - $original;

View File

@ -644,6 +644,14 @@ class backup_setting_ui_select extends backup_setting_ui {
return parent::is_changeable();
}
}
/**
* Returns the list of available values
* @return array
*/
public function get_values() {
return $this->values;
}
}
/**
@ -683,6 +691,58 @@ class backup_setting_ui_dateselector extends backup_setting_ui_text {
}
}
/**
* A wrapper for defaultcustom form element - can have either text or date_selector type
*
* @package core_backup
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_setting_ui_defaultcustom extends backup_setting_ui_text {
/**
* Constructor
*
* @param backup_setting $setting
* @param string $label The label to display with the setting ui
* @param array $attributes Array of HTML attributes to apply to the element
* @param array $options Array of options to apply to the setting ui object
*/
public function __construct(backup_setting $setting, $label = null, array $attributes = null, array $options = null) {
if (!is_array($attributes)) {
$attributes = [];
}
$attributes += ['customlabel' => get_string('overwrite', 'backup'),
'type' => 'text'];
parent::__construct($setting, $label, $attributes, $options);
}
/**
* Returns an array of properties suitable for generating a quickforms element
* @param base_task $task
* @param renderer_base $output
* @return array (element, name, label, options, attributes)
*/
public function get_element_properties(base_task $task = null, renderer_base $output = null) {
return ['element' => 'defaultcustom'] + parent::get_element_properties($task, $output);
}
/**
* Gets the static value for this select element
* @return string
*/
public function get_static_value() {
$value = $this->get_value();
if ($value === false) {
$value = $this->attributes['defaultvalue'];
}
if (!empty($value) && $this->attributes['type'] === 'date_selector') {
return userdate($value);
}
return $value;
}
}
/**
* Base setting UI exception class.
*

View File

@ -317,7 +317,7 @@ abstract class base_ui {
* Gets the requested setting
* @param string $name
* @param bool $default
* @return mixed
* @return base_setting
*/
public function get_setting($name, $default = false) {
try {

View File

@ -20,27 +20,16 @@ class editsection_form extends moodleform {
$mform = $this->_form;
$course = $this->_customdata['course'];
$sectioninfo = $this->_customdata['cs'];
$mform->addElement('header', 'generalhdr', get_string('general'));
$elementgroup = array();
$elementgroup[] = $mform->createElement('text', 'name', '', array('size' => '30', 'maxlength' => '255'));
// Get default section name.
$defaultsectionname = $this->_customdata['defaultsectionname'];
if ($defaultsectionname) {
$defaultsectionname = ' [' . $defaultsectionname . ']';
}
$elementgroup[] = $mform->createElement('checkbox', 'usedefaultname', '',
get_string('sectionusedefaultname') . $defaultsectionname);
$mform->addGroup($elementgroup, 'name_group', get_string('sectionname'), ' ', false);
$mform->addGroupRule('name_group', array('name' => array(array(get_string('maximumchars', '', 255), 'maxlength', 255))));
$mform->setDefault('usedefaultname', true);
$mform->setType('name', PARAM_TEXT);
$mform->disabledIf('name','usedefaultname','checked');
$mform->addElement('defaultcustom', 'name', get_string('sectionname'), [
'defaultvalue' => $this->_customdata['defaultsectionname'],
'customvalue' => $sectioninfo->name,
], ['size' => 30, 'maxlength' => 255]);
$mform->setDefault('name', false);
$mform->addGroupRule('name', array('name' => array(array(get_string('maximumchars', '', 255), 'maxlength', 255))));
/// Prepare course and the editor
@ -98,7 +87,9 @@ class editsection_form extends moodleform {
$editoroptions = $this->_customdata['editoroptions'];
$default_values = file_prepare_standard_editor($default_values, 'summary', $editoroptions,
$editoroptions['context'], 'course', 'section', $default_values->id);
$default_values->usedefaultname = (strval($default_values->name) === '');
if (strval($default_values->name) === '') {
$default_values->name = false;
}
parent::set_data($default_values);
}
@ -113,7 +104,7 @@ class editsection_form extends moodleform {
if ($data !== null) {
$editoroptions = $this->_customdata['editoroptions'];
// Set name as an empty string if use default section name is checked.
if (!empty($data->usedefaultname)) {
if ($data->name === false) {
$data->name = '';
}
$data = file_postupdate_standard_editor($data, 'summary', $editoroptions,

View File

@ -26,19 +26,21 @@ Feature: Sections can be edited and deleted in topics format
Scenario: View the default name of the general section in topics format
When I click on "Edit section" "link" in the "li#section-0" "css_element"
Then I should see "Use default section name [General]"
Then the field "Custom" matches value "0"
And the field "New value for Section name" matches value "General"
Scenario: Edit the default name of the general section in topics format
When I click on "Edit section" "link" in the "li#section-0" "css_element"
And I set the following fields to these values:
| Use default section name | 0 |
| name | This is the general section |
| Custom | 1 |
| New value for Section name | This is the general section |
And I press "Save changes"
Then I should see "This is the general section" in the "li#section-0" "css_element"
Scenario: View the default name of the second section in topics format
When I click on "Edit topic" "link" in the "li#section-2" "css_element"
Then I should see "Use default section name [Topic 2]"
Then the field "Custom" matches value "0"
And the field "New value for Section name" matches value "Topic 2"
Scenario: Edit section summary in topics format
When I edit the section "2" and I fill the form with:
@ -47,8 +49,8 @@ Feature: Sections can be edited and deleted in topics format
Scenario: Edit section default name in topics format
When I edit the section "2" and I fill the form with:
| Use default section name | 0 |
| name | This is the second topic |
| Custom | 1 |
| New value for Section name | This is the second topic |
Then I should see "This is the second topic" in the "li#section-2" "css_element"
And I should not see "Topic 2" in the "li#section-2" "css_element"

View File

@ -26,19 +26,21 @@ Feature: Sections can be edited and deleted in weeks format
Scenario: View the default name of the general section in weeks format
When I click on "Edit section" "link" in the "li#section-0" "css_element"
Then I should see "Use default section name [General]"
Then the field "Custom" matches value "0"
And the field "New value for Section name" matches value "General"
Scenario: Edit the default name of the general section in weeks format
When I click on "Edit section" "link" in the "li#section-0" "css_element"
And I set the following fields to these values:
| Use default section name | 0 |
| name | This is the general section |
| Custom | 1 |
| New value for Section name | This is the general section |
And I press "Save changes"
Then I should see "This is the general section" in the "li#section-0" "css_element"
Scenario: View the default name of the second section in weeks format
When I click on "Edit week" "link" in the "li#section-2" "css_element"
Then I should see "Use default section name [8 May - 14 May]"
Then the field "Custom" matches value "0"
And the field "New value for Section name" matches value "8 May - 14 May"
Scenario: Edit section summary in weeks format
When I click on "Edit week" "link" in the "li#section-2" "css_element"
@ -51,8 +53,8 @@ Feature: Sections can be edited and deleted in weeks format
Given I should see "8 May - 14 May" in the "li#section-2" "css_element"
When I click on "Edit week" "link" in the "li#section-2" "css_element"
And I set the following fields to these values:
| Use default section name | 0 |
| name | This is the second week |
| Custom | 1 |
| New value for Section name | This is the second week |
And I press "Save changes"
Then I should see "This is the second week" in the "li#section-2" "css_element"
And I should not see "8 May - 14 May" in the "li#section-2" "css_element"

View File

@ -98,7 +98,7 @@ class core_course_restore_testcase extends advanced_testcase {
$rc = new restore_controller($backupid, $courseid, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $userid, $target);
$target == backup::TARGET_NEW_COURSE ?: $rc->get_plan()->get_setting('overwrite_conf')->set_value(true);
$rc->execute_precheck();
$this->assertTrue($rc->execute_precheck());
$rc->execute_plan();
$course = $DB->get_record('course', array('id' => $rc->get_courseid()));
@ -222,7 +222,10 @@ class core_course_restore_testcase extends advanced_testcase {
$this->resetAfterTest();
$dg = $this->getDataGenerator();
$c1 = $dg->create_course(['shortname' => 'SN', 'fullname' => 'FN', 'summary' => 'DESC', 'summaryformat' => FORMAT_MOODLE]);
$startdate = mktime(12, 0, 0, 7, 1, 2016); // 01-Jul-2016.
$c1 = $dg->create_course(['shortname' => 'SN', 'fullname' => 'FN', 'startdate' => $startdate,
'summary' => 'DESC', 'summaryformat' => FORMAT_MOODLE]);
$backupid = $this->backup_course($c1->id);
// The information is restored but adapted because names are already taken.
@ -231,6 +234,7 @@ class core_course_restore_testcase extends advanced_testcase {
$this->assertEquals('FN copy 1', $c2->fullname);
$this->assertEquals('DESC', $c2->summary);
$this->assertEquals(FORMAT_MOODLE, $c2->summaryformat);
$this->assertEquals($startdate, $c2->startdate);
}
public function test_restore_course_info_in_existing_course() {
@ -238,8 +242,19 @@ class core_course_restore_testcase extends advanced_testcase {
$this->resetAfterTest();
$dg = $this->getDataGenerator();
$c1 = $dg->create_course(['shortname' => 'SN', 'fullname' => 'FN', 'summary' => 'DESC', 'summaryformat' => FORMAT_MOODLE]);
$c2 = $dg->create_course(['shortname' => 'A', 'fullname' => 'B', 'summary' => 'C', 'summaryformat' => FORMAT_PLAIN]);
$this->assertEquals(1, get_config('restore', 'restore_merge_course_shortname'));
$this->assertEquals(1, get_config('restore', 'restore_merge_course_fullname'));
$this->assertEquals(1, get_config('restore', 'restore_merge_course_startdate'));
$startdate = mktime(12, 0, 0, 7, 1, 2016); // 01-Jul-2016.
// Create two courses with different start dates,in each course create a chat that opens 1 week after the course start date.
$c1 = $dg->create_course(['shortname' => 'SN', 'fullname' => 'FN', 'summary' => 'DESC', 'summaryformat' => FORMAT_MOODLE,
'startdate' => $startdate]);
$chat1 = $dg->create_module('chat', ['name' => 'First', 'course' => $c1->id, 'chattime' => $c1->startdate + 1 * WEEKSECS]);
$c2 = $dg->create_course(['shortname' => 'A', 'fullname' => 'B', 'summary' => 'C', 'summaryformat' => FORMAT_PLAIN,
'startdate' => $startdate + 2 * WEEKSECS]);
$chat2 = $dg->create_module('chat', ['name' => 'Second', 'course' => $c2->id, 'chattime' => $c2->startdate + 1 * WEEKSECS]);
$backupid = $this->backup_course($c1->id);
// The information is restored but adapted because names are already taken.
@ -248,6 +263,14 @@ class core_course_restore_testcase extends advanced_testcase {
$this->assertEquals('FN copy 1', $c2->fullname);
$this->assertEquals('DESC', $c2->summary);
$this->assertEquals(FORMAT_MOODLE, $c2->summaryformat);
$this->assertEquals($startdate, $c2->startdate);
// Now course c2 has two chats - one ('Second') was already there and one ('First') was restored from the backup.
// Their dates are exactly the same as they were in the original modules.
$restoredchat1 = $DB->get_record('chat', ['name' => 'First', 'course' => $c2->id]);
$restoredchat2 = $DB->get_record('chat', ['name' => 'Second', 'course' => $c2->id]);
$this->assertEquals($chat1->chattime, $restoredchat1->chattime);
$this->assertEquals($chat2->chattime, $restoredchat2->chattime);
}
public function test_restore_course_shortname_in_existing_course_without_permissions() {
@ -321,4 +344,44 @@ class core_course_restore_testcase extends advanced_testcase {
$this->assertEquals($c2->summary, $restored->summary);
$this->assertEquals($c2->summaryformat, $restored->summaryformat);
}
public function test_restore_course_startdate_in_existing_course_without_permissions() {
global $DB;
$this->resetAfterTest();
$dg = $this->getDataGenerator();
$u1 = $dg->create_user();
$managers = get_archetype_roles('manager');
$manager = array_shift($managers);
$roleid = $this->create_role_with_caps('moodle/restore:rolldates', CAP_PROHIBIT);
$dg->role_assign($manager->id, $u1->id);
$dg->role_assign($roleid, $u1->id);
// Create two courses with different start dates,in each course create a chat that opens 1 week after the course start date.
$startdate1 = mktime(12, 0, 0, 7, 1, 2016); // 01-Jul-2016.
$startdate2 = mktime(12, 0, 0, 1, 13, 2000); // 13-Jan-2000.
$c1 = $dg->create_course(['shortname' => 'SN', 'fullname' => 'FN', 'summary' => 'DESC', 'summaryformat' => FORMAT_MOODLE,
'startdate' => $startdate1]);
$chat1 = $dg->create_module('chat', ['name' => 'First', 'course' => $c1->id, 'chattime' => $c1->startdate + 1 * WEEKSECS]);
$c2 = $dg->create_course(['shortname' => 'A', 'fullname' => 'B', 'summary' => 'C', 'summaryformat' => FORMAT_PLAIN,
'startdate' => $startdate2]);
$chat2 = $dg->create_module('chat', ['name' => 'Second', 'course' => $c2->id, 'chattime' => $c2->startdate + 1 * WEEKSECS]);
// The startdate does not change.
$backupid = $this->backup_course($c1->id);
$restored = $this->restore_to_existing_course($backupid, $c2->id, $u1->id);
$this->assertEquals('SN_1', $restored->shortname);
$this->assertEquals('FN copy 1', $restored->fullname);
$this->assertEquals('DESC', $restored->summary);
$this->assertEquals(FORMAT_MOODLE, $restored->summaryformat);
$this->assertEquals($startdate2, $restored->startdate);
// Now course c2 has two chats - one ('Second') was already there and one ('First') was restored from the backup.
// Start date of the restored chat ('First') was changed to be 1 week after the c2 start date.
$restoredchat1 = $DB->get_record('chat', ['name' => 'First', 'course' => $c2->id]);
$restoredchat2 = $DB->get_record('chat', ['name' => 'Second', 'course' => $c2->id]);
$this->assertNotEquals($chat1->chattime, $restoredchat1->chattime);
$this->assertEquals($chat2->chattime, $restoredchat2->chattime);
$this->assertEquals($c2->startdate + 1 * WEEKSECS, $restoredchat2->chattime);
}
}

View File

@ -83,6 +83,12 @@ $string['choosefilefromactivitybackup'] = 'Activity backup area';
$string['choosefilefromactivitybackup_help'] = 'Activity backups made using default settings are stored here.';
$string['choosefilefromautomatedbackup'] = 'Automated backups';
$string['choosefilefromautomatedbackup_help'] = 'Contains automatically generated backups.';
$string['config_keep_groups_and_groupings'] = 'By default keep current roles and enrolments';
$string['config_keep_roles_and_enrolments'] = 'By default keep current groups and groupings';
$string['config_overwrite_conf'] = 'Allows user to overwrite the current course configuration';
$string['config_overwrite_course_fullname'] = 'By default overwrite course full name with the one from the backup file. This requires "Overwrite course configuration" to be checked and current user to have the capability to change course full name (moodle/course:changefullname)';
$string['config_overwrite_course_shortname'] = 'By default overwrite course short name with the one from the backup file. This requires "Overwrite course configuration" to be checked and current user to have the capability to change course short name (moodle/course:changeshortname)';
$string['config_overwrite_course_startdate'] = 'By default overwrite course start date with the one from the backup file. This requires "Overwrite course configuration" to be checked and current user to have the capability to roll course dates on restore (moodle/restore:rolldates)';
$string['configgeneralactivities'] = 'Sets the default for including activities in a backup.';
$string['configgeneralbadges'] = 'Sets the default for including badges in a backup.';
$string['configgeneralanonymize'] = 'If enabled all information pertaining to users will be anonymised by default.';
@ -99,6 +105,20 @@ $string['configgeneralroleassignments'] = 'If enabled by default roles assignmen
$string['configgeneraluserscompletion'] = 'If enabled user completion information will be included in backups by default.';
$string['configgeneralusers'] = 'Sets the default for whether to include users in backups.';
$string['configloglifetime'] = 'This specifies the length of time you want to keep backup logs information. Logs that are older than this age are automatically deleted. It is recommended to keep this value small, because backup logged information can be huge.';
$string['configrestoreactivities'] = 'Sets the default for restoring activities.';
$string['configrestorebadges'] = 'Sets the default for restoring badges.';
$string['configrestoreblocks'] = 'Sets the default for restoring blocks.';
$string['configrestorecalendarevents'] = 'Sets the default for restoring calendar events.';
$string['configrestorecomments'] = 'Sets the default for restoring comments.';
$string['configrestorecompetencies'] = 'Sets the default for restoring competencies.';
$string['configrestoreenrolments'] = 'Sets the default for restoring enrolment methods.';
$string['configrestorefilters'] = 'Sets the default for restoring filters.';
$string['configrestorehistories'] = 'Sets the default for restoring user history if it was included in the backup.';
$string['configrestorelogs'] = 'If enabled logs will be restored by default if they were included in the backup.';
$string['configrestoregroups'] = 'Sets the default for restoring groups and groupings if they were included in the backup.';
$string['configrestoreroleassignments'] = 'If enabled by default roles assignments will be restored if they were included in the backup.';
$string['configrestoreuserscompletion'] = 'If enabled user completion information will be restored by default if it was included in the backup.';
$string['configrestoreusers'] = 'Sets the default for whether to restore users if they were included in the backup.';
$string['confirmcancel'] = 'Cancel backup';
$string['confirmcancelquestion'] = 'Are you sure you wish to cancel?
Any information you have entered will be lost.';
@ -144,12 +164,17 @@ $string['generalblocks'] = 'Include blocks';
$string['generalcalendarevents'] = 'Include calendar events';
$string['generalcomments'] = 'Include comments';
$string['generalcompetencies'] = 'Include competencies';
$string['generalenrolments'] = 'Include enrolment methods';
$string['generalfilters'] = 'Include filters';
$string['generalhistories'] = 'Include histories';
$string['generalgradehistories'] = 'Include histories';
$string['generallogs'] = 'Include logs';
$string['generalquestionbank'] = 'Include question bank';
$string['generalgroups'] = 'Include groups and groupings';
$string['generalrestoredefaults'] = 'General restore defaults';
$string['mergerestoredefaults'] = 'Restore defaults when merging into another course';
$string['replacerestoredefaults'] = 'Restore defaults when restoring into another course deleting contents';
$string['generalrestoresettings'] = 'General restore settings';
$string['generalroleassignments'] = 'Include role assignments';
$string['generalsettings'] = 'General backup settings';
$string['generaluserscompletion'] = 'Include user completion information';
@ -180,6 +205,7 @@ $string['includesection'] = 'Section {$a}';
$string['includeuserinfo'] = 'User data';
$string['includefilereferences'] = 'File references to external contents';
$string['jumptofinalstep'] = 'Jump to final step';
$string['keep'] = 'Keep';
$string['locked'] = 'Locked';
$string['lockedbypermission'] = 'You don\'t have sufficient permissions to change this setting';
$string['lockedbyconfig'] = 'This setting has been locked by the default backup settings';
@ -192,6 +218,7 @@ $string['moreresults'] = 'There are too many results, enter a more specific sear
$string['nomatchingcourses'] = 'There are no courses to display';
$string['norestoreoptions'] = 'There are no categories or existing courses you can restore to.';
$string['originalwwwroot'] = 'URL of backup';
$string['overwrite'] = 'Overwrite';
$string['previousstage'] = 'Previous';
$string['preparingui'] = 'Preparing to display page';
$string['preparingdata'] = 'Preparing data';
@ -267,12 +294,15 @@ $string['sectioninc'] = 'Included in backup (no user information)';
$string['sectionactivities'] = 'Activities';
$string['selectacategory'] = 'Select a category';
$string['selectacourse'] = 'Select a course';
$string['setting_overwriteconf'] = 'Overwrite course configuration';
$string['setting_course_fullname'] = 'Course name';
$string['setting_course_shortname'] = 'Course short name';
$string['setting_course_startdate'] = 'Course start date';
$string['setting_keep_roles_and_enrolments'] = 'Keep current roles and enrolments';
$string['setting_keep_groups_and_groupings'] = 'Keep current groups and groupings';
$string['setting_overwrite_conf'] = 'Overwrite course configuration';
$string['setting_overwrite_course_fullname'] = 'Overwrite course full name';
$string['setting_overwrite_course_shortname'] = 'Overwrite course short name';
$string['setting_overwrite_course_startdate'] = 'Overwrite course start date';
$string['showtypes'] = 'Show type options';
$string['skiphidden'] = 'Skip hidden courses';
$string['skiphiddenhelp'] = 'Choose whether or not to skip hidden courses';

View File

@ -68,4 +68,5 @@ error:noassertion,core_badges
error:personaneedsjs,core_badges
error:badjson,core_badges
error:backpackloginfailed,core_badges
signinwithyouremail,core_badges
signinwithyouremail,core_badges
sectionusedefaultname,core

View File

@ -26,7 +26,9 @@
$string['addfields'] = 'Add {$a} field(s) to form';
$string['advancedelement'] = 'Advanced element';
$string['close'] = 'Close';
$string['custom'] = 'Custom';
$string['day'] = 'Day';
$string['default'] = 'Default';
$string['display'] = 'Display';
$string['err_alphanumeric'] = 'You must enter only letters or numbers here.';
$string['err_email'] = 'You must enter a valid email address here.';
@ -47,6 +49,7 @@ $string['miscellaneoussettings'] = 'Miscellaneous settings';
$string['modstandardels'] = 'Common module settings';
$string['month'] = 'Month';
$string['mustbeoverriden'] = 'Abstract form_definition() method in class {$a} must be overridden, please fix the code.';
$string['newvaluefor'] = 'New value for {$a}';
$string['nomethodforaddinghelpbutton'] = 'There is no method for adding a help button to form element {$a->name} (class {$a->classname})';
$string['nonexistentformelements'] = 'Trying to add help buttons to non-existent form elements : {$a}';
$string['noselection'] = 'No selection';

View File

@ -1649,7 +1649,6 @@ $string['section'] = 'Section';
$string['sectionactionnotsupported'] = 'Section action "{$a}" is not supported here';
$string['sectionname'] = 'Section name';
$string['sections'] = 'Sections';
$string['sectionusedefaultname'] = 'Use default section name';
$string['seealsostats'] = 'See also: stats';
$string['select'] = 'Select';
$string['selectacategory'] = 'Please select a category';
@ -2063,4 +2062,5 @@ $string['modchooserenable'] = 'Activity chooser on';
$string['modchooserdisable'] = 'Activity chooser off';
// Deprecated since Moodle 3.3.
$string['deletecomment'] = 'Delete this comment';
$string['deletecomment'] = 'Delete this comment';
$string['sectionusedefaultname'] = 'Use default section name';

View File

@ -5125,6 +5125,28 @@ class admin_setting_configselect_with_advanced extends admin_setting_configselec
}
/**
* Select with an advanced checkbox that controls an additional $name.'_locked' config setting.
*
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class admin_setting_configselect_with_lock extends admin_setting_configselect {
/**
* Constructor
* @param string $name unique ascii name, either 'mysetting' for settings that in config,
* or 'myplugin/mysetting' for ones in config_plugins.
* @param string $visiblename localised
* @param string $description long localised info
* @param array $defaultsetting ('value'=>string, 'locked'=>bool)
* @param array $choices array of $value=>$label for each selection
*/
public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
$this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
}
}
/**
* Graded roles in gradebook

View File

@ -0,0 +1 @@
define(["jquery"],function(a){var b=function(b){var c=a(b.target),d=JSON.parse(c.attr("data-defaultvalue")),e=JSON.parse(c.attr("data-customvalue")),f=c.attr("data-type"),g=c.closest("form"),h=c.attr("name").replace(/\[customize\]$/,"[value]"),i=c.prop("checked")?e:d;"text"===f?g.find('[name="'+h+'"]').val(i):"date_selector"===f&&(g.find('[name="'+h+'[day]"]').val(i.day),g.find('[name="'+h+'[month]"]').val(i.month),g.find('[name="'+h+'[year]"]').val(i.year))},c="input[data-defaultcustom=true]";a("body").on("change",c,b)});

View File

@ -0,0 +1,47 @@
// 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/>.
/**
* Functionality for the form element defaultcustom
*
* @module core_form/defaultcustom
* @package core_form
* @class defaultcustom
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.3
*/
define(['jquery'], function($) {
var onChangeSelect = function(event) {
var element = $(event.target),
defaultvalue = JSON.parse(element.attr('data-defaultvalue')),
customvalue = JSON.parse(element.attr('data-customvalue')),
type = element.attr('data-type'),
form = element.closest('form'),
elementName = element.attr('name').replace(/\[customize\]$/, '[value]'),
newvalue = element.prop('checked') ? customvalue : defaultvalue;
if (type === 'text') {
form.find('[name="' + elementName + '"]').val(newvalue);
} else if (type === 'date_selector') {
form.find('[name="' + elementName + '[day]"]').val(newvalue.day);
form.find('[name="' + elementName + '[month]"]').val(newvalue.month);
form.find('[name="' + elementName + '[year]"]').val(newvalue.year);
}
};
var selector = 'input[data-defaultcustom=true]';
$('body').on('change', selector, onChangeSelect);
});

View File

@ -274,7 +274,7 @@ class MoodleQuickForm_date_selector extends MoodleQuickForm_group {
$valuearray += $thisexport;
}
}
if (count($valuearray)){
if (count($valuearray) && isset($valuearray['year'])) {
if($this->_options['optional']) {
// If checkbox is on, the value is zero, so go no further
if(empty($valuearray['enabled'])) {

265
lib/form/defaultcustom.php Normal file
View File

@ -0,0 +1,265 @@
<?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/>.
/**
* Creates an element with a dropdown Default/Custom and an input for the value (text or date_selector)
*
* @package core_form
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
global $CFG;
require_once($CFG->libdir . '/form/group.php');
require_once($CFG->libdir . '/formslib.php');
/**
* Creates an element with a dropdown Default/Custom and an input for the value (text or date_selector)
*
* @package core_form
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class MoodleQuickForm_defaultcustom extends MoodleQuickForm_group {
/**
* @var array These complement separators, they are appended to the resultant HTML.
*/
protected $_wrap = array('', '');
/**
* @var null|bool Keeps track of whether the date selector was initialised using createElement
* or addElement. If true, createElement was used signifying the element has been
* added to a group - see MDL-39187.
*/
protected $_usedcreateelement = true;
/** @var array */
protected $_options;
/**
* Constructor
*
* @param string $elementname Element's name
* @param mixed $elementlabel Label(s) for an element
* @param array $options Options to control the element's display
* @param mixed $attributes Either a typical HTML attribute string or an associative array
*/
public function __construct($elementname = null, $elementlabel = null, $options = array(), $attributes = null) {
parent::__construct($elementname, $elementlabel);
$this->setAttributes($attributes);
$this->_appendName = true;
$this->_type = 'defaultcustom';
$calendartype = \core_calendar\type_factory::get_calendar_instance();
$this->_options = [
'type' => 'text', // Type of the element. Supported are 'text' and 'date_selector'.
'defaultvalue' => null, // Value to be used when not overridden.
'customvalue' => null, // Value to be used when overwriting.
'customlabel' => get_string('custom', 'form'), // Label for 'customize' checkbox
// Other options are the same as the ones that can be passed to 'date_selector' element.
'timezone' => 99,
'startyear' => $calendartype->get_min_year(),
'stopyear' => $calendartype->get_max_year(),
'defaulttime' => 0,
'step' => 5,
'optional' => false,
];
if (is_array($options)) {
foreach ($options as $name => $value) {
if (array_key_exists($name, $this->_options)) {
if ($name === 'type' && !in_array($value, ['text', 'date_selector'])) {
throw new coding_exception('Only text and date_selector elements are supported in ' . $this->_type);
}
if ($name === 'optional' && $value) {
throw new coding_exception('Date selector can not be optional in ' . $this->_type);
}
$this->_options[$name] = $value;
}
}
}
}
/**
* Converts timestamp to the day/month/year array in the current calendar format
* @param int $value
* @return array
*/
protected function timestamp_to_date_array($value) {
$calendartype = \core_calendar\type_factory::get_calendar_instance();
$currentdate = $calendartype->timestamp_to_date_array($value, $this->_options['timezone']);
return array(
'day' => $currentdate['mday'],
'month' => $currentdate['mon'],
'year' => $currentdate['year']);
}
/**
* Should this element have default/custom switch?
*
* @return bool
*/
protected function has_customize_switch() {
return $this->_options['defaultvalue'] !== null;
}
/**
* This will create all elements in the group
*/
public function _createElements() {
if (!$this->has_customize_switch()) {
$element = $this->createFormElement('hidden', 'customize', 1);
} else {
$element = $this->createFormElement('checkbox', 'customize', '', $this->_options['customlabel']);
}
$this->_elements[] = $element;
if ($this->_options['type'] === 'text') {
$element = $this->createFormElement($this->_options['type'], 'value',
get_string('newvaluefor', 'form', $this->getLabel()), $this->getAttributes());
$element->setHiddenLabel(true);
} else if ($this->_options['type'] === 'date_selector') {
$element = $this->createFormElement($this->_options['type'], 'value', '', $this->_options,
$this->getAttributes());
}
$this->_elements[] = $element;
}
/**
* Called by HTML_QuickForm whenever form event is made on this element
*
* @param string $event Name of event
* @param mixed $arg event arguments
* @param object $caller calling object
* @return bool
*/
public function onQuickFormEvent($event, $arg, &$caller) {
$this->setMoodleForm($caller);
switch ($event) {
case 'updateValue':
// Constant values override both default and submitted ones
// default values are overriden by submitted.
$value = $this->_findValue($caller->_constantValues);
if (null === $value) {
// If no boxes were checked, then there is no value in the array
// yet we don't want to display default value in this case.
if ($caller->isSubmitted()) {
$value = $this->_findValue($caller->_submitValues);
} else {
$value = $this->_findValue($caller->_defaultValues);
}
}
if (!is_array($value)) {
$customize = ($value !== false || !$this->has_customize_switch());
if ($this->_options['type'] === 'text') {
$elementvalue = $customize ? $value : $this->_options['defaultvalue'];
} else {
$elementvalue = $this->timestamp_to_date_array($customize ? $value : $this->_options['defaultvalue']);
}
$value = [
'customize' => $customize,
'value' => $elementvalue
];
}
$this->setValue($value);
break;
case 'createElement':
$rv = parent::onQuickFormEvent($event, $arg, $caller);
if ($this->_options['type'] === 'text') {
$caller->disabledIf($arg[0] . '[value]', $arg[0] . '[customize]', 'notchecked');
} else {
$caller->disabledIf($arg[0] . '[value][day]', $arg[0] . '[customize]', 'notchecked');
$caller->disabledIf($arg[0] . '[value][month]', $arg[0] . '[customize]', 'notchecked');
$caller->disabledIf($arg[0] . '[value][year]', $arg[0] . '[customize]', 'notchecked');
}
return $rv;
case 'addElement':
$this->_usedcreateelement = false;
return parent::onQuickFormEvent($event, $arg, $caller);
break;
default:
return parent::onQuickFormEvent($event, $arg, $caller);
}
}
public function freeze() {
parent::freeze();
$this->setPersistantFreeze(true);
}
public function toHtml() {
include_once('HTML/QuickForm/Renderer/Default.php');
$renderer = new HTML_QuickForm_Renderer_Default();
$renderer->setElementTemplate('{element}');
parent::accept($renderer);
$html = $this->_wrap[0];
if ($this->_usedcreateelement) {
$html .= html_writer::tag('span', $renderer->toHtml(), array('class' => 'fdefaultcustom'));
} else {
$html .= $renderer->toHtml();
}
$html .= $this->_wrap[1];
return $html;
}
public function accept(&$renderer, $required = false, $error = null) {
global $PAGE;
if (!$this->_flagFrozen && $this->has_customize_switch()) {
// Add JS to the default/custom switch.
$firstelement = reset($this->_elements);
$defaultvalue = $this->_options['defaultvalue'];
$customvalue = $this->_options['customvalue'];
if ($this->_options['type'] === 'date_selector') {
$defaultvalue = $this->timestamp_to_date_array($defaultvalue);
$customvalue = $this->timestamp_to_date_array($customvalue);
}
$firstelement->updateAttributes(['data-defaultcustom' => 'true',
'data-type' => $this->_options['type'],
'data-defaultvalue' => json_encode($defaultvalue),
'data-customvalue' => json_encode($customvalue)]);
$PAGE->requires->js_amd_inline("require(['core_form/defaultcustom'], function() {});");
}
$renderer->renderElement($this, $required, $error);
}
/**
* Output a value. Give it the name of the group. In case of "default" return false.
*
* @param array $submitvalues values submitted.
* @param bool $assoc specifies if returned array is associative
* @return array
*/
public function exportValue(&$submitvalues, $assoc = false) {
$valuearray = array();
foreach ($this->_elements as $element) {
$thisexport = $element->exportValue($submitvalues[$this->getName()], true);
if ($thisexport != null) {
$valuearray += $thisexport;
}
}
if (empty($valuearray['customize'])) {
return $this->_prepareValue(false, $assoc);
}
return array_key_exists('value', $valuearray) ? $this->_prepareValue($valuearray['value'], $assoc) : [];
}
}

View File

@ -123,13 +123,13 @@ class MoodleQuickForm_text extends HTML_QuickForm_text implements templatable {
}
}
$this->_generateId();
if ($this->_flagFrozen) {
return $this->getFrozenHtml();
}
$html = $this->_getTabs() . '<input' . $this->_getAttrString($this->_attributes) . ' />';
if ($this->_hiddenLabel){
$this->_generateId();
return '<label class="accesshide" for="'.$this->getAttribute('id').'" >'.
$this->getLabel() . '</label>' . $html;
} else {

View File

@ -2452,8 +2452,14 @@ require(["core/event", "jquery"], function(Event, $) {
$elNames = array();
foreach ($elsInGroup as $elInGroup){
if (is_a($elInGroup, 'HTML_QuickForm_group')) {
// not sure if this would work - groups nested in groups
// Groups nested in groups: append the group name to the element and then change it back.
// We will be appending group name again in MoodleQuickForm_group::export_for_template().
$oldname = $elInGroup->getName();
if ($element->_appendName) {
$elInGroup->setName($element->getName() . '[' . $oldname . ']');
}
$elNames = array_merge($elNames, $this->_getElNamesRecursive($elInGroup));
$elInGroup->setName($oldname);
} else {
$elNames[] = $element->getElementName($elInGroup->getName());
}
@ -3105,6 +3111,7 @@ MoodleQuickForm::registerElementType('header', "$CFG->libdir/form/header.php", '
MoodleQuickForm::registerElementType('hidden', "$CFG->libdir/form/hidden.php", 'MoodleQuickForm_hidden');
MoodleQuickForm::registerElementType('htmleditor', "$CFG->libdir/form/htmleditor.php", 'MoodleQuickForm_htmleditor');
MoodleQuickForm::registerElementType('listing', "$CFG->libdir/form/listing.php", 'MoodleQuickForm_listing');
MoodleQuickForm::registerElementType('defaultcustom', "$CFG->libdir/form/defaultcustom.php", 'MoodleQuickForm_defaultcustom');
MoodleQuickForm::registerElementType('modgrade', "$CFG->libdir/form/modgrade.php", 'MoodleQuickForm_modgrade');
MoodleQuickForm::registerElementType('modvisible', "$CFG->libdir/form/modvisible.php", 'MoodleQuickForm_modvisible');
MoodleQuickForm::registerElementType('password', "$CFG->libdir/form/password.php", 'MoodleQuickForm_password');

View File

@ -0,0 +1,43 @@
{{!
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/>.
}}
{{!
@template core_form/element-defaultcustom-inline
Defaultcustom form element template.
Example context (json):
{
"element": {
"elements": [
{"html": "<select><option>Default</option><option>Custom</option></select>",
"separator": ""},
{"html": "<input type=text>",
"separator": ""}
]
}
}
}}
{{< core_form/element-template-inline }}
{{$element}}
<span class="fdefaultcustom">
{{#element.elements}}
{{{separator}}}
{{{html}}}
{{/element.elements}}
</span>
{{/element}}
{{/ core_form/element-template-inline }}

View File

@ -0,0 +1,43 @@
{{!
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/>.
}}
{{!
@template core_form/element-defaultcustom
Defaultcustom form element template.
Example context (json):
{
"element": {
"elements": [
{"html": "<select><option>Default</option><option>Custom</option></select>",
"separator": ""},
{"html": "<input type=text>",
"separator": ""}
]
}
}
}}
{{< core_form/element-template }}
{{$element}}
<span class="fdefaultcustom">
{{#element.elements}}
{{{separator}}}
{{{html}}}
{{/element.elements}}
</span>
{{/element}}
{{/ core_form/element-template }}