Skip to content

Commit

Permalink
MDL-11288 Added ability to duplicate module in the course
Browse files Browse the repository at this point in the history
Internally, the new script modduplicate.php performs a single activity
backup and restore and then moves the newly created copy right below the
original.
  • Loading branch information
mudrd8mz committed Mar 31, 2011
1 parent dae6b38 commit fa82056
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 1 deletion.
4 changes: 4 additions & 0 deletions course/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -3160,6 +3160,10 @@ function make_editing_buttons($mod, $absolute=false, $moveselect=true, $indent=-
'&amp;sesskey='.$sesskey.$section.'"><img'.
' src="'.$OUTPUT->pix_url('t/edit') . '" class="iconsmall" '.
' alt="'.$str->update.'" /></a>'."\n".
'<a class="editing_duplicate" title="'.$str->duplicate.'" href="'.$path.'/mod.php?duplicate='.$mod->id.
'&amp;sesskey='.$sesskey.$section.'"><img'.
' src="'.$OUTPUT->pix_url('t/copy') . '" class="iconsmall" '.
' alt="'.$str->duplicate.'" /></a>'."\n".
'<a class="editing_delete" title="'.$str->delete.'" href="'.$path.'/mod.php?delete='.$mod->id.
'&amp;sesskey='.$sesskey.$section.'"><img'.
' src="'.$OUTPUT->pix_url('t/delete') . '" class="iconsmall" '.
Expand Down
37 changes: 37 additions & 0 deletions course/mod.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
$type = optional_param('type', '', PARAM_ALPHA);
$indent = optional_param('indent', 0, PARAM_INT);
$update = optional_param('update', 0, PARAM_INT);
$duplicate = optional_param('duplicate', 0, PARAM_INT);
$hide = optional_param('hide', 0, PARAM_INT);
$show = optional_param('show', 0, PARAM_INT);
$copy = optional_param('copy', 0, PARAM_INT);
Expand Down Expand Up @@ -81,6 +82,42 @@
$returntomod = optional_param('return', 0, PARAM_BOOL);
redirect("$CFG->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');
Expand Down
145 changes: 145 additions & 0 deletions course/modduplicate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?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/>.

/**
* 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 <[email protected]>
* @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();
5 changes: 4 additions & 1 deletion lang/en/moodle.php
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,11 @@
$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 <strong>aspell 0.50</strong> 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 <strong>/usr/bin/aspell</strong>, but it might be something else.';
$string['edhelpbgcolor'] = 'Define the edit area\'s background color.<br />Valid values are, for example: #FFFFFF or white';
$string['edhelpcleanword'] = 'This setting enables or disables Word-specific format filtering.';
Expand Down
7 changes: 7 additions & 0 deletions lib/ajax/section_classes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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') {
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit fa82056

Please sign in to comment.