mirror of
https://github.com/moodle/moodle.git
synced 2025-04-14 13:02:07 +02:00
MDL-40956 Course: AJAX course duplication
This commit is contained in:
parent
d28aeeddc0
commit
6982618e89
107
course/lib.php
107
course/lib.php
@ -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);
|
||||
|
46
course/yui/toolboxes/toolboxes.js
vendored
46
course/yui/toolboxes/toolboxes.js
vendored
@ -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.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user