diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index 84cf4de3161..de35cc37cfb 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -503,6 +503,14 @@ class backup_course_structure_step extends backup_structure_step { */ class backup_enrolments_structure_step extends backup_structure_step { + /** + * Skip enrolments on the front page. + * @return bool + */ + protected function execute_condition() { + return ($this->get_courseid() != SITEID); + } + protected function define_structure() { // To know if we are including users @@ -921,7 +929,12 @@ class backup_gradebook_structure_step extends backup_structure_step { * the module gradeitems have been already included in backup */ protected function execute_condition() { - return backup_plan_dbops::require_gradebook_backup($this->get_courseid(), $this->get_backupid()); + $courseid = $this->get_courseid(); + if ($courseid == SITEID) { + return false; + } + + return backup_plan_dbops::require_gradebook_backup($courseid, $this->get_backupid()); } protected function define_structure() { @@ -1035,7 +1048,12 @@ class backup_grade_history_structure_step extends backup_structure_step { * because we do not want to save the history of items which are not backed up. At least for now. */ protected function execute_condition() { - return backup_plan_dbops::require_gradebook_backup($this->get_courseid(), $this->get_backupid()); + $courseid = $this->get_courseid(); + if ($courseid == SITEID) { + return false; + } + + return backup_plan_dbops::require_gradebook_backup($courseid, $this->get_backupid()); } protected function define_structure() { @@ -1088,6 +1106,14 @@ class backup_grade_history_structure_step extends backup_structure_step { */ class backup_userscompletion_structure_step extends backup_structure_step { + /** + * Skip completion on the front page. + * @return bool + */ + protected function execute_condition() { + return ($this->get_courseid() != SITEID); + } + protected function define_structure() { // Define each element separated @@ -1617,6 +1643,7 @@ class backup_main_structure_step extends backup_structure_step { $info['original_site_identifier_hash'] = md5(get_site_identifier()); $info['original_course_id'] = $this->get_courseid(); $originalcourseinfo = backup_controller_dbops::backup_get_original_course_info($this->get_courseid()); + $info['original_course_format'] = $originalcourseinfo->format; $info['original_course_fullname'] = $originalcourseinfo->fullname; $info['original_course_shortname'] = $originalcourseinfo->shortname; $info['original_course_startdate'] = $originalcourseinfo->startdate; @@ -1634,7 +1661,7 @@ class backup_main_structure_step extends backup_structure_step { $information = new backup_nested_element('information', null, array( 'name', 'moodle_version', 'moodle_release', 'backup_version', 'backup_release', 'backup_date', 'mnet_remoteusers', 'include_files', 'include_file_references_to_external_content', 'original_wwwroot', - 'original_site_identifier_hash', 'original_course_id', + 'original_site_identifier_hash', 'original_course_id', 'original_course_format', 'original_course_fullname', 'original_course_shortname', 'original_course_startdate', 'original_course_contextid', 'original_system_contextid')); @@ -2102,6 +2129,12 @@ class backup_activity_grading_structure_step extends backup_structure_step { * Include the grading.xml only if the module supports advanced grading */ protected function execute_condition() { + + // No grades on the front page. + if ($this->get_courseid() == SITEID) { + return false; + } + return plugin_supports('mod', $this->get_task()->get_modulename(), FEATURE_ADVANCED_GRADING, false); } @@ -2175,6 +2208,14 @@ class backup_activity_grading_structure_step extends backup_structure_step { */ class backup_activity_grades_structure_step extends backup_structure_step { + /** + * No grades on the front page. + * @return bool + */ + protected function execute_condition() { + return ($this->get_courseid() != SITEID); + } + protected function define_structure() { // To know if we are including userinfo @@ -2259,6 +2300,14 @@ class backup_activity_grades_structure_step extends backup_structure_step { */ class backup_activity_grade_history_structure_step extends backup_structure_step { + /** + * No grades on the front page. + * @return bool + */ + protected function execute_condition() { + return ($this->get_courseid() != SITEID); + } + protected function define_structure() { // Settings to use. @@ -2306,6 +2355,12 @@ class backup_activity_grade_history_structure_step extends backup_structure_step class backup_course_completion_structure_step extends backup_structure_step { protected function execute_condition() { + + // No completion on front page. + if ($this->get_courseid() == SITEID) { + return false; + } + // Check that all activities have been included if ($this->task->is_excluding_activities()) { return false; diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index fc4fe154ca6..ee4fa9d2c83 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -89,6 +89,10 @@ class restore_gradebook_structure_step extends restore_structure_step { protected function execute_condition() { global $CFG, $DB; + if ($this->get_courseid() == SITEID) { + return false; + } + // No gradebook info found, don't execute $fullpath = $this->task->get_taskbasepath(); $fullpath = rtrim($fullpath, '/') . '/' . $this->filename; @@ -464,6 +468,10 @@ class restore_grade_history_structure_step extends restore_structure_step { protected function execute_condition() { global $CFG, $DB; + if ($this->get_courseid() == SITEID) { + return false; + } + // No gradebook info found, don't execute. $fullpath = $this->task->get_taskbasepath(); $fullpath = rtrim($fullpath, '/') . '/' . $this->filename; @@ -1814,9 +1822,15 @@ class restore_ras_and_caps_structure_step extends restore_structure_step { * If no instances yet add default enrol methods the same way as when creating new course in UI. */ class restore_default_enrolments_step extends restore_execution_step { + public function define_execution() { global $DB; + // No enrolments in front page. + if ($this->get_courseid() == SITEID) { + return; + } + $course = $DB->get_record('course', array('id'=>$this->get_courseid()), '*', MUST_EXIST); if ($DB->record_exists('enrol', array('courseid'=>$this->get_courseid(), 'enrol'=>'manual'))) { @@ -1853,6 +1867,10 @@ class restore_enrolments_structure_step extends restore_structure_step { */ protected function execute_condition() { + if ($this->get_courseid() == SITEID) { + return false; + } + // Check it is included in the backup $fullpath = $this->task->get_taskbasepath(); $fullpath = rtrim($fullpath, '/') . '/' . $this->filename; @@ -2444,6 +2462,11 @@ class restore_course_completion_structure_step extends restore_structure_step { return false; } + // No course completion on the front page. + if ($this->get_courseid() == SITEID) { + return false; + } + // Check it is included in the backup $fullpath = $this->task->get_taskbasepath(); $fullpath = rtrim($fullpath, '/') . '/' . $this->filename; @@ -2791,6 +2814,10 @@ class restore_activity_grading_structure_step extends restore_structure_step { */ protected function execute_condition() { + if ($this->get_courseid() == SITEID) { + return false; + } + $fullpath = $this->task->get_taskbasepath(); $fullpath = rtrim($fullpath, '/') . '/' . $this->filename; if (!file_exists($fullpath)) { @@ -2925,6 +2952,14 @@ class restore_activity_grading_structure_step extends restore_structure_step { */ class restore_activity_grades_structure_step extends restore_structure_step { + /** + * No grades in front page. + * @return bool + */ + protected function execute_condition() { + return ($this->get_courseid() != SITEID); + } + protected function define_structure() { $paths = array(); @@ -3061,6 +3096,11 @@ class restore_activity_grade_history_structure_step extends restore_structure_st * This step is executed only if the grade history file is present. */ protected function execute_condition() { + + if ($this->get_courseid() == SITEID) { + return false; + } + $fullpath = $this->task->get_taskbasepath(); $fullpath = rtrim($fullpath, '/') . '/' . $this->filename; if (!file_exists($fullpath)) { @@ -3447,6 +3487,11 @@ class restore_userscompletion_structure_step extends restore_structure_step { return false; } + // No completion on the front page. + if ($this->get_courseid() == SITEID) { + return false; + } + // No user completion info found, don't execute $fullpath = $this->task->get_taskbasepath(); $fullpath = rtrim($fullpath, '/') . '/' . $this->filename; diff --git a/backup/moodle2/tests/moodle2_test.php b/backup/moodle2/tests/moodle2_test.php index 960c9eca09d..4842d4a8d18 100644 --- a/backup/moodle2/tests/moodle2_test.php +++ b/backup/moodle2/tests/moodle2_test.php @@ -391,6 +391,81 @@ class core_backup_moodle2_testcase extends advanced_testcase { 'assign', 'allowsubmissionsfromdate', array('id' => $newassign->instance))); } + /** + * Test front page backup/restore and duplicate activities + * @return void + */ + public function test_restore_frontpage() { + global $DB, $CFG, $USER; + + $this->resetAfterTest(true); + $this->setAdminUser(); + $generator = $this->getDataGenerator(); + + $frontpage = $DB->get_record('course', array('id' => SITEID)); + $forum = $generator->create_module('forum', array('course' => $frontpage->id)); + + // Activities can be duplicated. + $this->duplicate($frontpage, $forum->cmid); + + $modinfo = get_fast_modinfo($frontpage); + $this->assertEquals(2, count($modinfo->get_instances_of('forum'))); + + // Front page backup. + $frontpagebc = new backup_controller(backup::TYPE_1COURSE, $frontpage->id, + backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, + $USER->id); + $frontpagebackupid = $frontpagebc->get_backupid(); + $frontpagebc->execute_plan(); + $frontpagebc->destroy(); + + $course = $generator->create_course(); + $newcourseid = restore_dbops::create_new_course( + $course->fullname . ' 2', $course->shortname . '_2', $course->category); + + // Other course backup. + $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, + backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, + $USER->id); + $otherbackupid = $bc->get_backupid(); + $bc->execute_plan(); + $bc->destroy(); + + // We can only restore a front page over the front page. + $rc = new restore_controller($frontpagebackupid, $course->id, + backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, + backup::TARGET_CURRENT_ADDING); + $this->assertFalse($rc->execute_precheck()); + $rc->destroy(); + + $rc = new restore_controller($frontpagebackupid, $newcourseid, + backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, + backup::TARGET_NEW_COURSE); + $this->assertFalse($rc->execute_precheck()); + $rc->destroy(); + + $rc = new restore_controller($frontpagebackupid, $frontpage->id, + backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, + backup::TARGET_CURRENT_ADDING); + $this->assertTrue($rc->execute_precheck()); + $rc->execute_plan(); + $rc->destroy(); + + // We can't restore a non-front page course on the front page course. + $rc = new restore_controller($otherbackupid, $frontpage->id, + backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, + backup::TARGET_CURRENT_ADDING); + $this->assertFalse($rc->execute_precheck()); + $rc->destroy(); + + $rc = new restore_controller($otherbackupid, $newcourseid, + backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, + backup::TARGET_NEW_COURSE); + $this->assertTrue($rc->execute_precheck()); + $rc->execute_plan(); + $rc->destroy(); + } + /** * Backs a course up and restores it. * diff --git a/backup/util/dbops/backup_controller_dbops.class.php b/backup/util/dbops/backup_controller_dbops.class.php index 41ff9d394bd..e7456ce4663 100644 --- a/backup/util/dbops/backup_controller_dbops.class.php +++ b/backup/util/dbops/backup_controller_dbops.class.php @@ -524,7 +524,7 @@ abstract class backup_controller_dbops extends backup_dbops { */ public static function backup_get_original_course_info($courseid) { global $DB; - return $DB->get_record('course', array('id' => $courseid), 'fullname, shortname, startdate'); + return $DB->get_record('course', array('id' => $courseid), 'fullname, shortname, startdate, format'); } /** diff --git a/backup/util/helper/backup_general_helper.class.php b/backup/util/helper/backup_general_helper.class.php index d9ad80d5b28..b0425a33094 100644 --- a/backup/util/helper/backup_general_helper.class.php +++ b/backup/util/helper/backup_general_helper.class.php @@ -155,6 +155,11 @@ abstract class backup_general_helper extends backup_helper { } else { $info->include_file_references_to_external_content = 0; } + // Introduced in Moodle 2.9. + $info->original_course_format = ''; + if (!empty($infoarr['original_course_format'])) { + $info->original_course_format = $infoarr['original_course_format']; + } // include_files is a new setting in 2.6. if (isset($infoarr['include_files'])) { $info->include_files = $infoarr['include_files']; diff --git a/backup/util/helper/restore_prechecks_helper.class.php b/backup/util/helper/restore_prechecks_helper.class.php index a80a80b3055..803cd77903d 100644 --- a/backup/util/helper/restore_prechecks_helper.class.php +++ b/backup/util/helper/restore_prechecks_helper.class.php @@ -105,10 +105,24 @@ abstract class restore_prechecks_helper { $warnings[] = get_string('noticenewerbackup','',$message); } - // Error if restoring over frontpage - // TODO: Review the whole restore process in order to transform this into one warning (see 1.9) - if ($controller->get_courseid() == SITEID) { - $errors[] = get_string('errorrestorefrontpage', 'backup'); + // The original_course_format var was introduced in Moodle 2.9. + $originalcourseformat = null; + if (!empty($controller->get_info()->original_course_format)) { + $originalcourseformat = $controller->get_info()->original_course_format; + } + + // We can't restore other course's backups on the front page. + if ($controller->get_courseid() == SITEID && + $originalcourseformat != 'site' && + $controller->get_type() == backup::TYPE_1COURSE) { + $errors[] = get_string('errorrestorefrontpagebackup', 'backup'); + } + + // We can't restore front pages over other courses. + if ($controller->get_courseid() != SITEID && + $originalcourseformat == 'site' && + $controller->get_type() == backup::TYPE_1COURSE) { + $errors[] = get_string('errorrestorefrontpagebackup', 'backup'); } // If restoring to different site and restoring users and backup has mnet users warn/error diff --git a/backup/util/ui/renderer.php b/backup/util/ui/renderer.php index 98ed652d360..d5d0f11d006 100644 --- a/backup/util/ui/renderer.php +++ b/backup/util/ui/renderer.php @@ -265,7 +265,9 @@ class core_backup_renderer extends plugin_renderer_base { $hasrestoreoption = false; $html = html_writer::start_tag('div', array('class'=>'backup-course-selector backup-restore')); - if ($wholecourse && !empty($categories) && ($categories->get_count() > 0 || $categories->get_search())) { + if ($wholecourse && !empty($categories) && ($categories->get_count() > 0 || $categories->get_search()) && + $currentcourse != SITEID) { + // New course $hasrestoreoption = true; $html .= $form; @@ -306,7 +308,7 @@ class core_backup_renderer extends plugin_renderer_base { $courses->invalidate_results(); // Clean list of courses. $courses->set_include_currentcourse(); } - if (!empty($courses) && ($courses->get_count() > 0 || $courses->get_search())) { + if (!empty($courses) && ($courses->get_count() > 0 || $courses->get_search()) && $currentcourse != SITEID) { // Existing course $hasrestoreoption = true; $html .= $form; diff --git a/backup/util/ui/restore_ui_components.php b/backup/util/ui/restore_ui_components.php index f892b8cf108..cbd4c7041f8 100644 --- a/backup/util/ui/restore_ui_components.php +++ b/backup/util/ui/restore_ui_components.php @@ -280,13 +280,13 @@ class restore_course_search extends restore_search_base { $params = array( 'contextlevel' => CONTEXT_COURSE, 'fullnamesearch' => '%'.$this->get_search().'%', - 'shortnamesearch' => '%'.$this->get_search().'%', - 'siteid' => SITEID + 'shortnamesearch' => '%'.$this->get_search().'%' ); $select = " SELECT c.id,c.fullname,c.shortname,c.visible,c.sortorder "; $from = " FROM {course} c "; - $where = " WHERE (".$DB->sql_like('c.fullname', ':fullnamesearch', false)." OR ".$DB->sql_like('c.shortname', ':shortnamesearch', false).") AND c.id <> :siteid"; + $where = " WHERE (".$DB->sql_like('c.fullname', ':fullnamesearch', false)." OR ". + $DB->sql_like('c.shortname', ':shortnamesearch', false).")"; $orderby = " ORDER BY c.sortorder"; if ($this->currentcourseid !== null && !$this->includecurrentcourse) { diff --git a/course/lib.php b/course/lib.php index cd83b66a4b9..07cd6ed218d 100644 --- a/course/lib.php +++ b/course/lib.php @@ -2054,8 +2054,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) { } // Duplicate (require both target import caps to be able to duplicate and backup2 support, see modduplicate.php) - // Note that restoring on front page is never allowed. - if ($mod->course != SITEID && has_all_capabilities($dupecaps, $coursecontext) && + if (has_all_capabilities($dupecaps, $coursecontext) && plugin_supports('mod', $mod->modname, FEATURE_BACKUP_MOODLE2)) { $actions['duplicate'] = new action_menu_link_secondary( new moodle_url($baseurl, array('duplicate' => $mod->id)), diff --git a/lang/en/backup.php b/lang/en/backup.php index f2173e38289..a449f120dfb 100644 --- a/lang/en/backup.php +++ b/lang/en/backup.php @@ -114,9 +114,9 @@ $string['error_course_module_not_found'] = 'Orphan course module (id: {$a}) foun $string['errorfilenamerequired'] = 'You must enter a valid filename for this backup'; $string['errorfilenamemustbezip'] = 'The filename you enter must be a ZIP file and have the .mbz extension'; $string['errorminbackup20version'] = 'This backup file has been created with one development version of Moodle backup ({$a->backup}). Minimum required is {$a->min}. Cannot be restored.'; -$string['errorrestorefrontpage'] = 'Restoring over front page is not allowed.'; $string['errorinvalidformat'] = 'Unknown backup format'; $string['errorinvalidformatinfo'] = 'The selected file is not a valid Moodle backup file and can\'t be restored.'; +$string['errorrestorefrontpagebackup'] = 'You can only restore front page backups on the front page'; $string['executionsuccess'] = 'The backup file was successfully created.'; $string['filename'] = 'Filename'; $string['filealiasesrestorefailures'] = 'Aliases restore failures';