From fa820563289464bc638aa8cfe8fcb7056b33ea1e Mon Sep 17 00:00:00 2001 From: David Mudrak Date: Thu, 24 Mar 2011 14:39:14 +0100 Subject: [PATCH] MDL-11288 Added ability to duplicate module in the course Internally, the new script modduplicate.php performs a single activity backup and restore and then moves the newly created copy right below the original. --- course/lib.php | 4 + course/mod.php | 37 +++++++++ course/modduplicate.php | 145 ++++++++++++++++++++++++++++++++++++ lang/en/moodle.php | 5 +- lib/ajax/section_classes.js | 7 ++ 5 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 course/modduplicate.php diff --git a/course/lib.php b/course/lib.php index 377a27575ee..10fd77ded29 100644 --- a/course/lib.php +++ b/course/lib.php @@ -3160,6 +3160,10 @@ function make_editing_buttons($mod, $absolute=false, $moveselect=true, $indent=- '&sesskey='.$sesskey.$section.'">'."\n". + ''."\n". 'wwwroot/course/modedit.php?update=$update&return=$returntomod"); +} else if (!empty($duplicate)) { + $cm = get_coursemodule_from_id('', $duplicate, 0, true, MUST_EXIST); + $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); + + require_login($course->id); + $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); + $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); + require_capability('moodle/course:manageactivities', $coursecontext); + + if (!$confirm or !confirm_sesskey()) { + $PAGE->set_title(get_string('duplicate')); + $PAGE->set_heading($course->fullname); + $PAGE->navbar->add(get_string('duplicatinga', 'core', format_string($cm->name))); + $PAGE->set_pagelayout('incourse'); + + $a = new stdClass(); + $a->modtype = get_string('modulename', $cm->modname); + $a->modname = format_string($cm->name); + $a->modid = $cm->id; + + echo $OUTPUT->header(); + echo $OUTPUT->confirm( + get_string('duplicateconfirm', 'core', $a), + new single_button( + new moodle_url('/course/modduplicate.php', array('cmid' => $cm->id, 'course' => $course->id)), + get_string('continue'), + 'post'), + new single_button( + new moodle_url('/course/view.php#section-' . $cm->sectionnum, array('id' => $cm->course)), + get_string('cancel'), + 'get') + ); + echo $OUTPUT->footer(); + die(); + } + } else if (!empty($delete)) { if (!$cm = get_coursemodule_from_id('', $delete, 0, true)) { print_error('invalidcoursemodule'); diff --git a/course/modduplicate.php b/course/modduplicate.php new file mode 100644 index 00000000000..632f191853e --- /dev/null +++ b/course/modduplicate.php @@ -0,0 +1,145 @@ +. + +/** + * Duplicates a given course module + * + * The script backups and restores a single activity as if it was imported + * from the same course, using the default import settings. The newly created + * copy of the activity is then moved right below the original one. + * + * @package core + * @subpackage course + * @copyright 2011 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(dirname(dirname(__FILE__)) . '/config.php'); +require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); +require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); +require_once($CFG->libdir . '/filelib.php'); + +$cmid = required_param('cmid', PARAM_INT); +$courseid = required_param('course', PARAM_INT); + +$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); +$cm = get_coursemodule_from_id('', $cmid, $course->id, true, MUST_EXIST); +$cmcontext = get_context_instance(CONTEXT_MODULE, $cm->id); +$context = get_context_instance(CONTEXT_COURSE, $courseid); +$section = $DB->get_record('course_sections', array('id' => $cm->section, 'course' => $cm->course)); + +require_login($course); +require_sesskey(); +require_capability('moodle/course:manageactivities', $context); +require_capability('moodle/backup:backuptargetimport', $context); +require_capability('moodle/restore:restoretargetimport', $context); + +$PAGE->set_title(get_string('duplicate')); +$PAGE->set_heading($course->fullname); +$PAGE->set_url(new moodle_url('/course/modduplicate.php', array('cmid' => $cm->id, 'courseid' => $course->id))); +$PAGE->set_pagelayout('incourse'); + +$output = $PAGE->get_renderer('core', 'backup'); + +// backup the activity + +$bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE, + backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); + +$backupid = $bc->get_backupid(); +$backupbasepath = $bc->get_plan()->get_basepath(); + +$bc->execute_plan(); + +$bc->destroy(); + +// restore the backup immediately + +$rc = new restore_controller($backupid, $courseid, + backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING); + +if (!$rc->execute_precheck()) { + $precheckresults = $rc->get_precheck_results(); + if (is_array($precheckresults) && !empty($precheckresults['errors'])) { + if (empty($CFG->keeptempdirectoriesonbackup)) { + fulldelete($backupbasepath); + } + + echo $output->header(); + echo $output->precheck_notices($precheckresults); + echo $output->continue_button(new moodle_url('/course/view.php', array('id' => $course->id))); + echo $output->footer(); + die(); + } +} + +$rc->execute_plan(); + +// now a bit hacky part follows - we try to get the cmid of the newly +// restored copy of the module +$newcmid = null; +$tasks = $rc->get_plan()->get_tasks(); +foreach ($tasks as $task) { + if (is_subclass_of($task, 'restore_activity_task')) { + if ($task->get_old_contextid() == $cmcontext->id) { + $newcmid = $task->get_moduleid(); + break; + } + } +} + +// if we know the cmid of the new course module, let us move it +// right below the original one. otherwise it will stay at the +// end of the section +if ($newcmid) { + $newcm = get_coursemodule_from_id('', $newcmid, $course->id, true, MUST_EXIST); + moveto_module($newcm, $section, $cm); + moveto_module($cm, $section, $newcm); +} + +$rc->destroy(); + +if (empty($CFG->keeptempdirectoriesonbackup)) { + fulldelete($backupbasepath); +} + +$a = new stdClass(); +$a->modtype = get_string('modulename', $cm->modname); +$a->modname = format_string($cm->name); + +echo $output->header(); + +if ($newcmid) { + echo $output->confirm( + get_string('duplicatesuccess', 'core', $a), + new single_button( + new moodle_url('/course/modedit.php', array('update' => $newcmid)), + get_string('duplicatecontedit'), + 'get'), + new single_button( + new moodle_url('/course/view.php#section-' . $cm->sectionnum, array('id' => $cm->course)), + get_string('duplicatecontcourse'), + 'get') + ); + +} else { + echo $output->notification(get_string('duplicatesuccess', 'core', $a), 'notifysuccess'); + echo $output->continue_button( + new moodle_url('/course/view.php#section-' . $cm->sectionnum, array('id' => $course->id)) + ); +} + +echo $output->footer(); diff --git a/lang/en/moodle.php b/lang/en/moodle.php index faa6d9cbbb4..c520725506d 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -461,8 +461,11 @@ $string['downloadods'] = 'Download in ODS format'; $string['downloadtext'] = 'Download in text format'; $string['doyouagree'] = 'Have you read these conditions and understood them?'; $string['duplicate'] = 'Duplicate'; +$string['duplicateconfirm'] = 'Are you sure you want to duplicate {$a->modtype} \'{$a->modname}\' ?'; +$string['duplicatecontcourse'] = 'Return to the course'; +$string['duplicatecontedit'] = 'Edit the new copy'; +$string['duplicatesuccess'] = '{$a->modtype} \'{$a->modname}\' has been duplicated successfully'; $string['duplicatinga'] = 'Duplicating: {$a}'; -$string['duplicatingain'] = 'Duplicating {$a->what} in {$a->in}'; $string['edhelpaspellpath'] = 'To use spell-checking within the editor, you MUST have aspell 0.50 or later installed on your server, and you must specify the correct path to access the aspell binary. On Unix/Linux systems, this path is usually /usr/bin/aspell, but it might be something else.'; $string['edhelpbgcolor'] = 'Define the edit area\'s background color.
Valid values are, for example: #FFFFFF or white'; $string['edhelpcleanword'] = 'This setting enables or disables Word-specific format filtering.'; diff --git a/lib/ajax/section_classes.js b/lib/ajax/section_classes.js index bc52f9588cc..46420d2b459 100644 --- a/lib/ajax/section_classes.js +++ b/lib/ajax/section_classes.js @@ -631,6 +631,7 @@ resource_class.prototype.init_buttons = function() { var moveLeft = false; var moveRight = false; var updateButton = null; + var duplicateButton = null; var assignButton = null; // for RTL support @@ -643,6 +644,8 @@ resource_class.prototype.init_buttons = function() { moveRight = true; } else if (buttons[x].className == 'editing_update') { updateButton = buttons[x].cloneNode(true); + } else if (buttons[x].className == 'editing_duplicate') { + duplicateButton = buttons[x].cloneNode(true); } else if (buttons[x].className == 'editing_assign') { assignButton = buttons[x].cloneNode(true); } else if (buttons[x].className == 'editing_groupsnone') { @@ -691,6 +694,10 @@ resource_class.prototype.init_buttons = function() { // Add edit button back in. commandContainer.appendChild(updateButton); + if (duplicateButton) { + commandContainer.appendChild(duplicateButton); + } + // Add the delete button. var button = main.mk_button('a', main.portal.icons['delete'], main.portal.strings['delete'], null, [['class', 'iconsmall']]); YAHOO.util.Event.addListener(button, 'click', this.delete_button, this, true);