Commit 6982618e authored by Andrew Nicols's avatar Andrew Nicols
Browse files

MDL-40956 Course: AJAX course duplication

parent d28aeedd
......@@ -2039,7 +2039,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('duplicate' => $mod->id)),
new pix_icon('t/copy', $str->duplicate, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->duplicate,
array('class' => 'editing_duplicate', 'data-action' => 'duplicate')
array('class' => 'editing_duplicate', 'data-action' => 'duplicate', 'data-sr' => $sr)
);
}
......@@ -3380,6 +3380,111 @@ function update_module($moduleinfo) {
return $moduleinfo;
}
/**
* Duplicate a module on the course.
*
* @param object $course The course
* @param object $cm The course module to duplicate
* @throws moodle_exception if the plugin doesn't support duplication
* @return Object containing:
* - fullcontent: The HTML markup for the created CM
* - cmid: The CMID of the newly created CM
* - redirect: Whether to trigger a redirect following this change
*/
function mod_duplicate_activity($course, $cm, $sr = null) {
global $CFG, $USER, $PAGE, $DB;
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');
$a = new stdClass();
$a->modtype = get_string('modulename', $cm->modname);
$a->modname = format_string($cm->name);
if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) {
throw new moodle_exception('duplicatenosupport', 'error');
}
// 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, $course->id,
backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
$cmcontext = context_module::instance($cm->id);
if (!$rc->execute_precheck()) {
$precheckresults = $rc->get_precheck_results();
if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
if (empty($CFG->keeptempdirectoriesonbackup)) {
fulldelete($backupbasepath);
}
}
}
$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) {
error_log("Looking at a task");
if (is_subclass_of($task, 'restore_activity_task')) {
error_log("Looking at a restore_activity_task task");
if ($task->get_old_contextid() == $cmcontext->id) {
error_log("Contexts match");
$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) {
$info = get_fast_modinfo($course);
$newcm = $info->get_cm($newcmid);
$section = $DB->get_record('course_sections', array('id' => $cm->section, 'course' => $cm->course));
moveto_module($newcm, $section, $cm);
moveto_module($cm, $section, $newcm);
}
rebuild_course_cache($cm->course);
$rc->destroy();
if (empty($CFG->keeptempdirectoriesonbackup)) {
fulldelete($backupbasepath);
}
$resp = new stdClass();
if ($newcm) {
$courserenderer = $PAGE->get_renderer('core', 'course');
$completioninfo = new completion_info($course);
$modulehtml = $courserenderer->course_section_cm($course, $completioninfo,
$newcm, null, array());
$resp->fullcontent = $courserenderer->course_section_cm_list_item($course, $completioninfo, $newcm, $sr);
$resp->cmid = $newcm->id;
} else {
// Trigger a redirect
$resp->redirect = true;
}
return $resp;
}
/**
* Compare two objects to find out their correct order based on timestamp (to be used by usort).
* Sorts by descending order of time.
......
......@@ -106,6 +106,18 @@ switch($requestmethod) {
set_coursemodule_visible($cm->id, $value);
break;
case 'duplicate':
require_capability('moodle/course:manageactivities', $modcontext);
require_capability('moodle/backup:backuptargetimport', $modcontext);
require_capability('moodle/restore:restoretargetimport', $modcontext);
if (!course_allowed_module($course, $cm->modname)) {
throw new moodle_exception('No permission to create that activity');
}
$sr = optional_param('sr', null, PARAM_INT);
$result = mod_duplicate_activity($course, $cm, $sr);
echo json_encode($result);
break;
case 'groupmode':
require_capability('moodle/course:manageactivities', $modcontext);
set_coursemodule_groupmode($cm->id, $value);
......
......@@ -265,6 +265,10 @@ YUI.add('moodle-course-toolboxes', function(Y) {
// The user is deleting the activity.
this.delete_with_confirmation(ev, node, activity, action);
break;
case 'duplicate' :
// The user is duplicating the activity.
this.duplicate(ev, node, activity, action);
break;
case 'hide' :
case 'show' :
// The user is changing the visibility of the activity.
......@@ -416,6 +420,48 @@ YUI.add('moodle-course-toolboxes', function(Y) {
}
},
/**
* Duplicates the activity
*
* @protected
* @method duplicate
* @param {EventFacade} ev The event that was fired.
* @param {Node} button The button that triggered this action.
* @param {Node} activity The activity node that this action will be performed on.
* @return Boolean
*/
duplicate : function(ev, button, activity) {
// Prevent the default button action
ev.preventDefault();
// Get the element we're working on
var element = activity;
// Add the lightbox.
var section = activity.ancestor(M.course.format.get_section_selector(Y)),
lightbox = M.util.add_lightbox(Y, section).show();
// Build and send the request.
var data = {
'class' : 'resource',
'field' : 'duplicate',
'id' : Y.Moodle.core_course.util.cm.getId(element),
'sr' : button.getData('sr')
};
this.send_request(data, lightbox, function(response) {
var newcm = Y.Node.create(response.fullcontent);
// Append to the section?
activity.insert(newcm, 'after');
Y.use('moodle-course-coursebase', function() {
M.course.coursebase.invoke_function('setup_for_resource', newcm);
});
if (M.core.actionmenu && M.core.actionmenu.newDOMNode) {
M.core.actionmenu.newDOMNode(newcm);
}
});
},
/**
* Changes the visibility of this activity or resource.
*
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment