From 424c9cb1bdc686f6ff8b98ca39648802c2ddac1f Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Fri, 27 Oct 2023 13:17:03 +0200 Subject: [PATCH] MDL-79912 backup: Create Course reuse index page The backup/view.php page has been created to display the list of course reuse actions available for users. That way, the "Course reuse" option in the secondary navigation will display this page instead of redirecting to the first action available. Apart from that, the headers for all the Course reuse actions have been homogenised and the tertiary navigation selector has been included, to replace headers and make it easier navigate between actions. --- backup/backup.php | 4 +- backup/backupfilesedit.php | 1 + backup/copy.php | 6 +- backup/copyprogress.php | 4 +- backup/import.php | 13 +- backup/restorefile.php | 2 + backup/util/helper/backup_helper.class.php | 56 ++++++++ backup/util/ui/classes/output/copy_form.php | 2 +- backup/view.php | 73 +++++++++++ course/reset.php | 11 +- lang/en/backup.php | 8 +- lang/en/deprecated.txt | 2 + lang/en/moodle.php | 6 +- lib/classes/navigation/views/secondary.php | 12 -- lib/navigationlib.php | 120 ++++++++++++------ lib/tests/navigation/views/secondary_test.php | 11 +- theme/boost/tests/behat/course_reuse.feature | 24 ++-- 17 files changed, 269 insertions(+), 86 deletions(-) create mode 100644 backup/view.php diff --git a/backup/backup.php b/backup/backup.php index 364e715f5ef..4f2519572ad 100644 --- a/backup/backup.php +++ b/backup/backup.php @@ -81,7 +81,7 @@ require_login($course, false, $cm); switch ($type) { case backup::TYPE_1COURSE : require_capability('moodle/backup:backupcourse', $coursecontext); - $heading = get_string('backupcourse', 'backup', $course->shortname); + $heading = $course->fullname; $PAGE->set_secondary_active_tab('coursereuse'); break; case backup::TYPE_1SECTION : @@ -112,6 +112,8 @@ $PAGE->activityheader->disable(); if (empty($cancel)) { // Do not print the header if user cancelled the process, as we are going to redirect the user. echo $OUTPUT->header(); + \backup_helper::print_coursereuse_selector('backup'); + echo html_writer::tag('div', get_string('backupinfo'), ['class' => 'pb-3']); } // Only let user perform a backup if we aren't in async mode, or if we are diff --git a/backup/backupfilesedit.php b/backup/backupfilesedit.php index c2733f042e4..2474b96696f 100644 --- a/backup/backupfilesedit.php +++ b/backup/backupfilesedit.php @@ -88,6 +88,7 @@ if ($data) { } echo $OUTPUT->header(); +\backup_helper::print_coursereuse_selector('restore'); echo $OUTPUT->container_start(); echo $OUTPUT->heading($title); diff --git a/backup/copy.php b/backup/copy.php index 8c7465dafed..f1f35550384 100644 --- a/backup/copy.php +++ b/backup/copy.php @@ -53,11 +53,10 @@ if ($returnurl != '') { // Setup the page. $title = get_string('copycoursetitle', 'backup', $course->shortname); -$heading = get_string('copycourseheading', 'backup'); $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); $PAGE->set_title($title); -$PAGE->set_heading($heading); +$PAGE->set_heading($course->fullname); $PAGE->set_secondary_active_tab('coursereuse'); // Get data ready for mform. @@ -91,7 +90,8 @@ if ($mform->is_cancelled()) { // Build the page output. echo $OUTPUT->header(); - echo $OUTPUT->heading($title); + \backup_helper::print_coursereuse_selector('copycourse'); + $mform->display(); echo $OUTPUT->footer(); } diff --git a/backup/copyprogress.php b/backup/copyprogress.php index 9d17cac7e6b..3242fd9ab89 100644 --- a/backup/copyprogress.php +++ b/backup/copyprogress.php @@ -45,13 +45,15 @@ $title = get_string('copyprogresstitle', 'backup'); $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); $PAGE->set_title($title); -$PAGE->set_heading($title); +$PAGE->set_heading($course->fullname); $PAGE->set_secondary_active_tab('coursereuse'); $PAGE->requires->js_call_amd('core_backup/async_backup', 'asyncCopyAllStatus'); $PAGE->secondarynav->set_overflow_selected_node('copy'); // Build the page output. echo $OUTPUT->header(); +\backup_helper::print_coursereuse_selector('copycourse'); + echo $OUTPUT->heading_with_help(get_string('copyprogressheading', 'backup'), 'copyprogressheading', 'backup'); echo $OUTPUT->container_start(); $renderer = $PAGE->get_renderer('core', 'backup'); diff --git a/backup/import.php b/backup/import.php index 402a269b6fc..872250e8fec 100644 --- a/backup/import.php +++ b/backup/import.php @@ -69,6 +69,10 @@ if ($importcourseid === false || $searchcourses) { // show the course selector echo $OUTPUT->header(); + \backup_helper::print_coursereuse_selector('import'); + + echo html_writer::tag('div', get_string('importinfo'), ['class' => 'pb-3']); + $backup = new import_ui(false, array()); echo $renderer->progress_bar($backup->get_progress_bar()); $html = $renderer->import_course_selector($url, $search); @@ -118,6 +122,9 @@ if ($backup->get_stage() == backup_ui::STAGE_CONFIRMATION) { // If it's the final stage process the import if ($backup->get_stage() == backup_ui::STAGE_FINAL) { echo $OUTPUT->header(); + \backup_helper::print_coursereuse_selector('import'); + + echo html_writer::tag('div', get_string('importinfo'), ['class' => 'pb-3']); // Display an extra progress bar so that we can show the current stage. echo html_writer::start_div('', array('id' => 'executionprogress')); @@ -233,8 +240,12 @@ if ($backup->get_stage() == backup_ui::STAGE_FINAL) { $backup->save_controller(); } -// Display the current stage +// Display the current stage. echo $OUTPUT->header(); +\backup_helper::print_coursereuse_selector('import'); + +echo html_writer::tag('div', get_string('importinfo'), ['class' => 'pb-3']); + if ($backup->enforce_changed_dependencies()) { debugging('Your settings have been altered due to unmet dependencies', DEBUG_DEVELOPER); } diff --git a/backup/restorefile.php b/backup/restorefile.php index 1140dbe7746..22184ad1c6c 100644 --- a/backup/restorefile.php +++ b/backup/restorefile.php @@ -138,6 +138,8 @@ if ($data && has_capability('moodle/restore:uploadfile', $context)) { } echo $OUTPUT->header(); +\backup_helper::print_coursereuse_selector('restore'); +echo html_writer::tag('div', get_string('restoreinfo'), ['class' => 'pb-3']); // require uploadfile cap to use file picker if (has_capability('moodle/restore:uploadfile', $context)) { diff --git a/backup/util/helper/backup_helper.class.php b/backup/util/helper/backup_helper.class.php index 9f1c1e32a33..fba5fa863b9 100644 --- a/backup/util/helper/backup_helper.class.php +++ b/backup/util/helper/backup_helper.class.php @@ -380,6 +380,62 @@ abstract class backup_helper { public static function get_inforef_itemnames() { return array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item', 'question_category'); } + + /** + * Print the course reuse dropdown. + * + * @param string $current The current course reuse option where the header is modified + */ + public static function print_coursereuse_selector(string $current):void { + global $OUTPUT, $PAGE; + + if ($coursereusenode = $PAGE->settingsnav->find('coursereuse', \navigation_node::TYPE_CONTAINER)) { + + $menuarray = \core\navigation\views\secondary::create_menu_element([$coursereusenode]); + if (empty($menuarray)) { + return; + } + + $coursereuse = get_string('coursereuse'); + $activeurl = ''; + if (isset($menuarray[0])) { + // Remove the "Course reuse" entry. + $result = array_search($coursereuse, $menuarray[0][$coursereuse]); + unset($menuarray[0][$coursereuse][$result]); + + // Find the active node. + foreach ($menuarray[0] as $key => $value) { + $check = array_search($current, $value); + if ($check !== false) { + $activeurl = $check; + } + } + } else { + $result = array_search($coursereuse, $menuarray); + unset($menuarray[$result]); + + $check = array_search(get_string($current), $menuarray); + if ($check !== false) { + $activeurl = $check; + } + + } + + $selectmenu = new \core\output\select_menu('coursereusetype', $menuarray, $activeurl); + $selectmenu->set_label(get_string('coursereusenavigationmenu'), ['class' => 'sr-only']); + $options = \html_writer::tag( + 'div', + $OUTPUT->render_from_template('core/tertiary_navigation_selector', $selectmenu->export_for_template($OUTPUT)), + ['class' => 'row pb-3'] + ); + echo \html_writer::tag( + 'div', + $options, + ['class' => 'tertiary-navigation full-width-bottom-border ml-0', 'id' => 'tertiary-navigation']); + } else { + echo $OUTPUT->heading($current, 2, 'mb-3'); + } + } } /* diff --git a/backup/util/ui/classes/output/copy_form.php b/backup/util/ui/classes/output/copy_form.php index dfcafb03b8c..ae24b6028e8 100644 --- a/backup/util/ui/classes/output/copy_form.php +++ b/backup/util/ui/classes/output/copy_form.php @@ -86,7 +86,7 @@ class copy_form extends \moodleform { $mform->setConstant('returnurl', $returnurl); // Form heading. - $mform->addElement('html', \html_writer::div(get_string('copycoursedesc', 'backup'), 'form-description mb-3')); + $mform->addElement('html', \html_writer::div(get_string('copycoursedesc', 'backup'), 'form-description mb-6')); // Course fullname. $mform->addElement('text', 'fullname', get_string('fullnamecourse'), 'maxlength="254" size="50"'); diff --git a/backup/view.php b/backup/view.php new file mode 100644 index 00000000000..1ff7af82d87 --- /dev/null +++ b/backup/view.php @@ -0,0 +1,73 @@ +. + +/** + * Page to view the course reuse actions. + * + * @package core_backup + * @copyright 2023 Sara Arjona + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../config.php'); + +// Course id. +$courseid = required_param('id', PARAM_INT); + +$PAGE->set_url(new moodle_url('/backup/view.php', ['id' => $courseid])); + +// Basic access checks. +if (!$course = $DB->get_record('course', ['id' => $courseid])) { + throw new \moodle_exception('invalidcourseid'); +} +require_login($course); + +$title = get_string('coursereuse'); +// Only append the course name if the course ID is not the site ID. +if ($courseid != SITEID) { + $title .= moodle_page::TITLE_SEPARATOR . $course->fullname; +} +// Otherwise, output the page with a notification stating that there are no available course reuse actions. +$PAGE->set_title($title); +$PAGE->set_pagelayout('incourse'); +$PAGE->set_heading($course->fullname); +$PAGE->set_pagetype('course-view-' . $course->format); +$PAGE->add_body_class('limitedwidth'); + +echo $OUTPUT->header(); +echo $OUTPUT->heading(get_string('coursereuse')); + +// Check if there is at least one displayable course reuse action. +$hasactions = false; +if ($coursereusenode = $PAGE->settingsnav->find('coursereuse', \navigation_node::TYPE_CONTAINER)) { + foreach ($coursereusenode->children as $child) { + if ($child->display) { + $hasactions = true; + break; + } + } +} + +if ($hasactions) { + echo $OUTPUT->render_from_template('core/report_link_page', ['node' => $coursereusenode]); +} else { + throw new \moodle_exception( + 'accessdenied', + 'admin', + new moodle_url('/course/view.php', ['id' => $courseid]) + ); +} +echo $OUTPUT->footer(); diff --git a/course/reset.php b/course/reset.php index dba445de3b1..fc042ed6a86 100644 --- a/course/reset.php +++ b/course/reset.php @@ -29,6 +29,9 @@ require('../config.php'); require_once('reset_form.php'); +require_once($CFG->dirroot . '/backup/util/interfaces/checksumable.class.php'); +require_once($CFG->dirroot . '/backup/backup.class.php'); +require_once($CFG->dirroot . '/backup/util/helper/backup_helper.class.php'); $id = required_param('id', PARAM_INT); @@ -47,7 +50,7 @@ $strresetcourse = get_string('resetcourse'); $strremove = get_string('remove'); $PAGE->set_title($course->fullname.': '.$strresetcourse); -$PAGE->set_heading($course->fullname.': '.$strresetcourse); +$PAGE->set_heading($course->fullname); $PAGE->set_secondary_active_tab('coursereuse'); $mform = new course_reset_form(); @@ -68,7 +71,7 @@ if ($mform->is_cancelled()) { } else { echo $OUTPUT->header(); - echo $OUTPUT->heading($strresetcourse); + \backup_helper::print_coursereuse_selector('reset'); $data->reset_start_date_old = $course->startdate; $data->reset_end_date_old = $course->enddate; @@ -98,11 +101,9 @@ if ($mform->is_cancelled()) { } echo $OUTPUT->header(); -echo $OUTPUT->heading($strresetcourse); +\backup_helper::print_coursereuse_selector('reset'); echo $OUTPUT->box(get_string('resetinfo')); $mform->display(); echo $OUTPUT->footer(); - - diff --git a/lang/en/backup.php b/lang/en/backup.php index 061aab53ff6..d727ed7c5bc 100644 --- a/lang/en/backup.php +++ b/lang/en/backup.php @@ -71,7 +71,6 @@ $string['automatedstorage'] = 'Automated backup storage'; $string['automatedstoragehelp'] = 'Choose the location where you want backups to be stored when they are automatically created.'; $string['backupactivity'] = 'Backup activity: {$a}'; $string['backupautoactivitiesdescription'] = 'Sets the default for including activities in a backup. For the recycle bin to work, this setting must be enabled.'; -$string['backupcourse'] = 'Backup course: {$a}'; $string['backupcoursedetails'] = 'Course details'; $string['backupcoursesection'] = 'Section: {$a}'; $string['backupcoursesections'] = 'Course sections'; @@ -170,8 +169,7 @@ $string['confirmcancelno'] = 'Do not cancel'; $string['confirmnewcoursecontinue'] = 'New course warning'; $string['confirmnewcoursecontinuequestion'] = 'A temporary (hidden) course will be created by the course restoration process. To abort restoration click cancel. Do not close the browser while restoring.'; $string['copiesinprogress'] = 'This course has copies in progress. View in progress copies.'; -$string['copycoursedesc'] = 'This course will be duplicated and put into the selected course category.'; -$string['copycourseheading'] = 'Copy a course'; +$string['copycoursedesc'] = 'Create a copy of this course in any course category.'; $string['copycoursetitle'] = 'Copy course: {$a}'; $string['copydest'] = 'Destination'; $string['copyingcourse'] = 'Course copying in progress'; @@ -421,3 +419,7 @@ $string['userinfo'] = 'Userinfo'; $string['module'] = 'Module'; $string['morecoursesearchresults'] = 'More than {$a} courses found, showing first {$a} results'; $string['recyclebin_desc'] = 'Note that these settings will also be used for the recycle bin.'; + +// Deprecated since Moodle 4.4. +$string['copycourseheading'] = 'Copy a course'; +$string['backupcourse'] = 'Backup course: {$a}'; diff --git a/lang/en/deprecated.txt b/lang/en/deprecated.txt index b48331e0e83..7926f321095 100644 --- a/lang/en/deprecated.txt +++ b/lang/en/deprecated.txt @@ -112,3 +112,5 @@ passwordconfirmchange,core passwordnohelp,core grade,core_grades taskdeletecachetext,core_admin +copycourseheading,core_backup +backupcourse,core_backup diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 034831e4034..4897bc2a2c0 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -199,6 +199,7 @@ $string['backupfromthissite'] = 'Backup was made on this site?'; $string['backupgradebookhistoryhelp'] = 'If enabled then gradebook history will be included in automated backups. Note that grade history must not be disabled in server settings (disablegradehistory) in order for this to work'; $string['backupincludemoduleshelp'] = 'Choose whether you want to include course modules, with or without user data, in automated backups'; $string['backupincludemoduleuserdatahelp'] = 'Choose whether you want to include module user data in automated backups.'; +$string['backupinfo'] = 'Make a copy of this course, including all content and activity up to date. You can use the backup file to restore this course or to use in a different site.'; $string['backuplogdetailed'] = 'Detailed execution log'; $string['backuploglaststatus'] = 'Last execution log'; $string['backupmissinguserinfoperms'] = 'Note: This backup contains no user data. Exercise and Workshop activities will not be included in the backup, since these modules are not compatible with this type of backup.'; @@ -419,6 +420,7 @@ $string['courserequestsupport'] = 'Supporting information to help the administra $string['courserequestwarning'] = 'The user requesting this course will be automatically enrolled and assigned the role of {$a}.'; $string['courserestore'] = 'Course restore'; $string['coursereuse'] = 'Course reuse'; +$string['coursereusenavigationmenu'] = 'Course reuse navigation menu'; $string['courses'] = 'Courses'; $string['coursesectionsummaries'] = 'Course section summaries'; $string['coursesectiontitle'] = '{$a->sectionname}: {$a->sectiontitle} | {$a->course}'; @@ -1113,6 +1115,7 @@ $string['importdata'] = 'Import course data'; $string['importdataexported'] = 'Exported data from \'from\' course successfully.
Continue to import into your \'to\' course.'; $string['importdatafinished'] = 'Import complete! Continue to your course'; $string['importdatafrom'] = 'Find a course to import data from:'; +$string['importinfo'] = 'Import items from other courses, such as activities, blocks or calendar events. No user data will be imported.'; $string['inactive'] = 'Inactive'; $string['include'] = 'Include'; $string['includeallusers'] = 'Include all users'; @@ -1861,7 +1864,7 @@ $string['resendemail'] = 'Resend email'; $string['reset'] = 'Reset'; $string['resetcomponent'] = 'Component'; $string['resetcourse'] = 'Reset course'; -$string['resetinfo'] = 'This page allows you to empty a course of user data, while retaining the activities and other settings. Please be warned that by choosing items below and submitting this page you will delete your chosen user data from this course forever!'; +$string['resetinfo'] = 'Delete all user data and reset this course to its original state, keeping activities and settings intact.'; $string['resetnotimplemented'] = 'Reset not implemented'; $string['resetrecordexpired'] = 'The password reset link you used is more than {$a} minutes old and has expired. Please initiate a new password reset.'; $string['resetstartdate'] = 'Reset start date'; @@ -1894,6 +1897,7 @@ $string['restorecoursenow'] = 'Restore this course now!'; $string['restoredaccount'] = 'Restored account'; $string['restoredaccountinfo'] = 'This account was imported from another server and the password has been lost. To set a new password by email, please click "Continue"'; $string['restorefinished'] = 'Restore completed successfully'; +$string['restoreinfo'] = 'Use a backup file to restore a previous version of a course, or to create a new course.'; $string['restoreto'] = 'Restore to'; $string['restoretositeadding'] = 'Warning: You are about to restore to the site home, adding data to it!'; $string['restoretositedeleting'] = 'Warning: You are about to restore to the site home, deleting data from it first!'; diff --git a/lib/classes/navigation/views/secondary.php b/lib/classes/navigation/views/secondary.php index b533fe58818..d9461681165 100644 --- a/lib/classes/navigation/views/secondary.php +++ b/lib/classes/navigation/views/secondary.php @@ -425,18 +425,6 @@ class secondary extends view { } } - // Move some nodes into a 'course reuse' node. - $overflownode = $this->get_course_overflow_nodes($rootnode); - if (!is_null($overflownode)) { - $actionnode = $this->get_first_action_for_node($overflownode); - if ($actionnode) { - // All additional nodes will be available under the 'Course reuse' page. - $text = get_string('coursereuse'); - $rootnode->add($text, $actionnode->action, navigation_node::TYPE_COURSE, null, 'coursereuse', - new \pix_icon('t/edit', $text)); - } - } - // Add the respective first node, provided there are other nodes included. if (!empty($nodekeys = $rootnode->children->get_key_list())) { $rootnode->add_node( diff --git a/lib/navigationlib.php b/lib/navigationlib.php index 4aed6e06bca..f56a6ef65c7 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -4747,36 +4747,6 @@ class settings_navigation extends navigation_node { badges_add_course_navigation($coursenode, $course); } - // Import data from other courses. - if ($adminoptions->import) { - $url = new moodle_url('/backup/import.php', array('id' => $course->id)); - $coursenode->add(get_string('import'), $url, self::TYPE_SETTING, null, 'import', new pix_icon('i/import', '')); - } - - // Backup this course - if ($adminoptions->backup) { - $url = new moodle_url('/backup/backup.php', array('id'=>$course->id)); - $coursenode->add(get_string('backup'), $url, self::TYPE_SETTING, null, 'backup', new pix_icon('i/backup', '')); - } - - // Restore to this course - if ($adminoptions->restore) { - $url = new moodle_url('/backup/restorefile.php', array('contextid'=>$coursecontext->id)); - $coursenode->add(get_string('restore'), $url, self::TYPE_SETTING, null, 'restore', new pix_icon('i/restore', '')); - } - - // Copy this course. - if ($adminoptions->copy) { - $url = new moodle_url('/backup/copy.php', array('id' => $course->id)); - $coursenode->add(get_string('copycourse'), $url, self::TYPE_SETTING, null, 'copy', new pix_icon('t/copy', '')); - } - - // Reset this course - if ($adminoptions->reset) { - $url = new moodle_url('/course/reset.php', array('id'=>$course->id)); - $coursenode->add(get_string('reset'), $url, self::TYPE_SETTING, null, 'reset', new pix_icon('i/return', '')); - } - // Questions require_once($CFG->libdir . '/questionlib.php'); question_extend_settings_navigation($coursenode, $coursecontext)->trim_if_empty(); @@ -4828,6 +4798,56 @@ class settings_navigation extends navigation_node { $coursenode->get('download')->set_force_into_more_menu(true); } + // Course reuse options. + if ($adminoptions->import + || $adminoptions->backup + || $adminoptions->restore + || $adminoptions->copy + || $adminoptions->reset) { + $coursereusenav = $coursenode->add( + get_string('coursereuse'), + new moodle_url('/backup/view.php', ['id' => $course->id]), + self::TYPE_CONTAINER, null, 'coursereuse', new pix_icon('t/edit', ''), + ); + + // Import data from other courses. + if ($adminoptions->import) { + $url = new moodle_url('/backup/import.php', ['id' => $course->id]); + $coursereusenav->add(get_string('import'), $url, self::TYPE_SETTING, null, 'import', new pix_icon('i/import', '')); + } + + // Backup this course. + if ($adminoptions->backup) { + $url = new moodle_url('/backup/backup.php', ['id' => $course->id]); + $coursereusenav->add(get_string('backup'), $url, self::TYPE_SETTING, null, 'backup', new pix_icon('i/backup', '')); + } + + // Restore to this course. + if ($adminoptions->restore) { + $url = new moodle_url('/backup/restorefile.php', ['contextid' => $coursecontext->id]); + $coursereusenav->add( + get_string('restore'), + $url, + self::TYPE_SETTING, + null, + 'restore', + new pix_icon('i/restore', ''), + ); + } + + // Copy this course. + if ($adminoptions->copy) { + $url = new moodle_url('/backup/copy.php', ['id' => $course->id]); + $coursereusenav->add(get_string('copycourse'), $url, self::TYPE_SETTING, null, 'copy', new pix_icon('t/copy', '')); + } + + // Reset this course. + if ($adminoptions->reset) { + $url = new moodle_url('/course/reset.php', ['id' => $course->id]); + $coursereusenav->add(get_string('reset'), $url, self::TYPE_SETTING, null, 'reset', new pix_icon('i/return', '')); + } + } + // Return we are done return $coursenode; } @@ -5721,18 +5741,6 @@ class settings_navigation extends navigation_node { } } - // Backup this course - if ($adminoptions->backup) { - $url = new moodle_url('/backup/backup.php', array('id'=>$course->id)); - $frontpage->add(get_string('backup'), $url, self::TYPE_SETTING, null, 'backup', new pix_icon('i/backup', '')); - } - - // Restore to this course - if ($adminoptions->restore) { - $url = new moodle_url('/backup/restorefile.php', array('contextid'=>$coursecontext->id)); - $frontpage->add(get_string('restore'), $url, self::TYPE_SETTING, null, 'restore', new pix_icon('i/restore', '')); - } - // Questions require_once($CFG->libdir . '/questionlib.php'); question_extend_settings_navigation($frontpage, $coursecontext)->trim_if_empty(); @@ -5752,6 +5760,34 @@ class settings_navigation extends navigation_node { } } + // Course reuse options. + if ($adminoptions->backup || $adminoptions->restore) { + $coursereusenav = $frontpage->add( + get_string('coursereuse'), + new moodle_url('/backup/view.php', ['id' => $course->id]), + self::TYPE_CONTAINER, null, 'coursereuse', new pix_icon('t/edit', ''), + ); + + // Backup this course. + if ($adminoptions->backup) { + $url = new moodle_url('/backup/backup.php', ['id' => $course->id]); + $coursereusenav->add(get_string('backup'), $url, self::TYPE_SETTING, null, 'backup', new pix_icon('i/backup', '')); + } + + // Restore to this course. + if ($adminoptions->restore) { + $url = new moodle_url('/backup/restorefile.php', ['contextid' => $coursecontext->id]); + $coursereusenav->add( + get_string('restore'), + $url, + self::TYPE_SETTING, + null, + 'restore', + new pix_icon('i/restore', ''), + ); + } + } + return $frontpage; } diff --git a/lib/tests/navigation/views/secondary_test.php b/lib/tests/navigation/views/secondary_test.php index e25e995d2f2..619f295293d 100644 --- a/lib/tests/navigation/views/secondary_test.php +++ b/lib/tests/navigation/views/secondary_test.php @@ -917,7 +917,6 @@ class secondary_test extends \advanced_testcase { $pageurl = new \moodle_url($url, [$contextidentifier => $id]); $PAGE->set_url($pageurl); navigation_node::override_active_url($pageurl); - $PAGE->set_course($pagecourse); $PAGE->set_context($contextrecord); $node = new secondary($PAGE); @@ -941,27 +940,27 @@ class secondary_test extends \advanced_testcase { "Backup page returns overflow" => [ '/backup/backup.php', 'id', - true + false, ], "Restore course page returns overflow" => [ '/backup/restorefile.php', 'contextid', - true + false, ], "Import course page returns overflow" => [ '/backup/import.php', 'id', - true + false, ], "Course copy page returns overflow" => [ '/backup/copy.php', 'id', - true + false, ], "Course reset page returns overflow" => [ '/course/reset.php', 'id', - true + false, ], // The following pages should not return the overflow menu. "Course page returns nothing" => [ diff --git a/theme/boost/tests/behat/course_reuse.feature b/theme/boost/tests/behat/course_reuse.feature index 948b071e3bd..58954281e7a 100644 --- a/theme/boost/tests/behat/course_reuse.feature +++ b/theme/boost/tests/behat/course_reuse.feature @@ -18,25 +18,29 @@ Feature: Course reuse navigation Given I log in as "teacher1" When I am on "Course 1" course homepage And I navigate to "Course reuse" in current page administration - Then I should see "Find a course to import data from:" + Then I should not see "Find a course to import data from:" + And I should see "Import" + And I should see "Backup" + And I should see "Restore" + And I should not see "Copy course" Scenario Outline: A Teacher can navigate to other Course reuse pages. Given I log in as "teacher1" When I am on "Course 1" course homepage And I navigate to "Course reuse" in current page administration - And I select "" from the "jump" singleselect - Then I should see "" + And I follow "<adminpage>" + Then I should see "<content>" Examples: - | adminpage | title | - | Backup | Backup settings | - | Restore | Import a backup file | - | Import | Find a course to import data from: | - | Reset | Reset course | + | adminpage | content | + | Backup | Backup settings | + | Restore | Import a backup file | + | Import | Find a course to import data from: | + | Reset | Delete all user data and reset this course to its original state | Scenario: An Administrator can view the course copy page. Given I log in as "admin" When I am on "Course 1" course homepage And I navigate to "Course reuse" in current page administration - And I select "Copy course" from the "jump" singleselect - Then I should see "This course will be duplicated and put into the selected course category" + And I follow "Copy course" + Then I should see "Create a copy of this course in any course category"