Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
integration
prechecker
Commits
6982618e
Commit
6982618e
authored
Aug 07, 2013
by
Andrew Nicols
Browse files
MDL-40956 Course: AJAX course duplication
parent
d28aeedd
Changes
3
Hide whitespace changes
Inline
Side-by-side
course/lib.php
View file @
6982618e
...
...
@@ -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.
...
...
course/rest.php
View file @
6982618e
...
...
@@ -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
);
...
...
course/yui/toolboxes/toolboxes.js
View file @
6982618e
...
...
@@ -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.
*
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment