MDL-67264 core_course: Begin set up for Activity chooser
[moodle.git] / course / dnduploadlib.php
CommitLineData
32528f94
DS
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Library to handle drag and drop course uploads
19 *
20 * @package core
21 * @subpackage lib
22 * @copyright 2012 Davo smith
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28require_once($CFG->dirroot.'/repository/lib.php');
29require_once($CFG->dirroot.'/repository/upload/lib.php');
30require_once($CFG->dirroot.'/course/lib.php');
31
32/**
33 * Add the Javascript to enable drag and drop upload to a course page
34 *
35 * @param object $course The currently displayed course
5103b5e6
DS
36 * @param array $modnames The list of enabled (visible) modules on this site
37 * @return void
32528f94 38 */
5103b5e6 39function dndupload_add_to_course($course, $modnames) {
32528f94
DS
40 global $CFG, $PAGE;
41
3970a471 42 $showstatus = optional_param('notifyeditingon', false, PARAM_BOOL);
6c0ae99b 43
32528f94 44 // Get all handlers.
5103b5e6
DS
45 $handler = new dndupload_handler($course, $modnames);
46 $jsdata = $handler->get_js_data();
47 if (empty($jsdata->types) && empty($jsdata->filehandlers)) {
48 return; // No valid handlers - don't enable drag and drop.
49 }
32528f94
DS
50
51 // Add the javascript to the page.
52 $jsmodule = array(
33b24bdd 53 'name' => 'coursedndupload',
3f534371 54 'fullpath' => '/course/dndupload.js',
32528f94 55 'strings' => array(
33b24bdd 56 array('addfilehere', 'moodle'),
b64300fc
DS
57 array('dndworkingfiletextlink', 'moodle'),
58 array('dndworkingfilelink', 'moodle'),
59 array('dndworkingfiletext', 'moodle'),
60 array('dndworkingfile', 'moodle'),
61 array('dndworkingtextlink', 'moodle'),
62 array('dndworkingtext', 'moodle'),
63 array('dndworkinglink', 'moodle'),
296af14b 64 array('namedfiletoolarge', 'moodle'),
33b24bdd
DS
65 array('actionchoice', 'moodle'),
66 array('servererror', 'moodle'),
4e737cf3 67 array('filereaderror', 'moodle'),
33b24bdd 68 array('upload', 'moodle'),
0b245bf3
HN
69 array('cancel', 'moodle'),
70 array('changesmadereallygoaway', 'moodle')
32528f94 71 ),
66079e28 72 'requires' => array('node', 'event', 'json', 'anim')
32528f94
DS
73 );
74 $vars = array(
75 array('courseid' => $course->id,
6afbdf27 76 'maxbytes' => get_user_max_upload_file_size($PAGE->context, $CFG->maxbytes, $course->maxbytes),
6c0ae99b
DS
77 'handlers' => $handler->get_js_data(),
78 'showstatus' => $showstatus)
32528f94
DS
79 );
80
81 $PAGE->requires->js_init_call('M.course_dndupload.init', $vars, true, $jsmodule);
82}
83
84
85/**
86 * Stores all the information about the available dndupload handlers
87 *
88 * @package core
89 * @copyright 2012 Davo Smith
90 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
91 */
92class dndupload_handler {
93
94 /**
95 * @var array A list of all registered mime types that can be dropped onto a course
96 * along with the modules that will handle them.
97 */
98 protected $types = array();
99
100 /**
101 * @var array A list of the different file types (extensions) that different modules
102 * will handle.
103 */
104 protected $filehandlers = array();
105
e6a0f101
MG
106 /**
107 * @var context_course|null
108 */
109 protected $context = null;
110
32528f94
DS
111 /**
112 * Gather a list of dndupload handlers from the different mods
113 *
114 * @param object $course The course this is being added to (to check course_allowed_module() )
115 */
5103b5e6 116 public function __construct($course, $modnames = null) {
04348400 117 global $CFG, $PAGE;
32528f94
DS
118
119 // Add some default types to handle.
120 // Note: 'Files' type is hard-coded into the Javascript as this needs to be ...
121 // ... treated a little differently.
1c14b1c1 122 $this->register_type('url', array('url', 'text/uri-list', 'text/x-moz-url'), get_string('addlinkhere', 'moodle'),
66079e28 123 get_string('nameforlink', 'moodle'), get_string('whatforlink', 'moodle'), 10);
1c14b1c1 124 $this->register_type('text/html', array('text/html'), get_string('addpagehere', 'moodle'),
66079e28 125 get_string('nameforpage', 'moodle'), get_string('whatforpage', 'moodle'), 20);
1c14b1c1 126 $this->register_type('text', array('text', 'text/plain'), get_string('addpagehere', 'moodle'),
66079e28 127 get_string('nameforpage', 'moodle'), get_string('whatforpage', 'moodle'), 30);
32528f94 128
e6a0f101
MG
129 $this->context = context_course::instance($course->id);
130
32528f94 131 // Loop through all modules to find handlers.
abed5d65
DS
132 $mods = get_plugin_list_with_function('mod', 'dndupload_register');
133 foreach ($mods as $component => $funcname) {
56da374e 134 list($modtype, $modname) = core_component::normalize_component($component);
5103b5e6
DS
135 if ($modnames && !array_key_exists($modname, $modnames)) {
136 continue; // Module is deactivated (hidden) at the site level.
137 }
32528f94 138 if (!course_allowed_module($course, $modname)) {
5103b5e6 139 continue; // User does not have permission to add this module to the course.
32528f94 140 }
abed5d65 141 $resp = $funcname();
32528f94
DS
142 if (!$resp) {
143 continue;
144 }
145 if (isset($resp['files'])) {
146 foreach ($resp['files'] as $file) {
1c14b1c1 147 $this->register_file_handler($file['extension'], $modname, $file['message']);
32528f94
DS
148 }
149 }
150 if (isset($resp['addtypes'])) {
151 foreach ($resp['addtypes'] as $type) {
152 if (isset($type['priority'])) {
153 $priority = $type['priority'];
154 } else {
155 $priority = 100;
156 }
66079e28
DS
157 if (!isset($type['handlermessage'])) {
158 $type['handlermessage'] = '';
159 }
1c14b1c1 160 $this->register_type($type['identifier'], $type['datatransfertypes'],
66079e28 161 $type['addmessage'], $type['namemessage'], $type['handlermessage'], $priority);
32528f94
DS
162 }
163 }
164 if (isset($resp['types'])) {
165 foreach ($resp['types'] as $type) {
2748d8ef 166 $noname = !empty($type['noname']);
1c14b1c1 167 $this->register_type_handler($type['identifier'], $modname, $type['message'], $noname);
32528f94
DS
168 }
169 }
04348400 170 $PAGE->requires->string_for_js('pluginname', $modname);
32528f94
DS
171 }
172 }
173
174 /**
175 * Used to add a new mime type that can be drag and dropped onto a
176 * course displayed in a browser window
177 *
178 * @param string $identifier The name that this type will be known as
179 * @param array $datatransfertypes An array of the different types in the browser
180 * 'dataTransfer.types' object that will map to this type
181 * @param string $addmessage The message to display in the browser when this type is being
182 * dragged onto the page
183 * @param string $namemessage The message to pop up when asking for the name to give the
184 * course module instance when it is created
66079e28 185 * @param string $handlermessage The message to pop up when asking which module should handle this type
32528f94
DS
186 * @param int $priority Controls the order in which types are checked by the browser (mainly
187 * needed to check for 'text' last as that is usually given as fallback)
188 */
1c14b1c1 189 protected function register_type($identifier, $datatransfertypes, $addmessage, $namemessage, $handlermessage, $priority=100) {
32528f94
DS
190 if ($this->is_known_type($identifier)) {
191 throw new coding_exception("Type $identifier is already registered");
192 }
193
194 $add = new stdClass;
195 $add->identifier = $identifier;
196 $add->datatransfertypes = $datatransfertypes;
197 $add->addmessage = $addmessage;
198 $add->namemessage = $namemessage;
66079e28 199 $add->handlermessage = $handlermessage;
32528f94
DS
200 $add->priority = $priority;
201 $add->handlers = array();
202
abed5d65 203 $this->types[$identifier] = $add;
32528f94
DS
204 }
205
1c14b1c1
DW
206 /**
207 * Used to declare that a particular module will handle a particular type
208 * of dropped data
209 *
210 * @param string $type The name of the type (as declared in register_type)
211 * @param string $module The name of the module to handle this type
212 * @param string $message The message to show the user if more than one handler is registered
213 * for a type and the user needs to make a choice between them
214 * @param bool $noname If true, the 'name' dialog should be disabled in the pop-up.
215 * @throws coding_exception
216 */
217 protected function register_type_handler($type, $module, $message, $noname) {
abed5d65
DS
218 if (!$this->is_known_type($type)) {
219 throw new coding_exception("Trying to add handler for unknown type $type");
32528f94
DS
220 }
221
abed5d65
DS
222 $add = new stdClass;
223 $add->type = $type;
224 $add->module = $module;
225 $add->message = $message;
2748d8ef 226 $add->noname = $noname ? 1 : 0;
abed5d65
DS
227
228 $this->types[$type]->handlers[] = $add;
32528f94
DS
229 }
230
231 /**
232 * Used to declare that a particular module will handle a particular type
233 * of dropped file
234 *
235 * @param string $extension The file extension to handle ('*' for all types)
236 * @param string $module The name of the module to handle this type
237 * @param string $message The message to show the user if more than one handler is registered
238 * for a type and the user needs to make a choice between them
239 */
1c14b1c1 240 protected function register_file_handler($extension, $module, $message) {
abed5d65
DS
241 $extension = strtolower($extension);
242
32528f94
DS
243 $add = new stdClass;
244 $add->extension = $extension;
245 $add->module = $module;
246 $add->message = $message;
247
248 $this->filehandlers[] = $add;
249 }
250
251 /**
252 * Check to see if the type has been registered
253 *
254 * @param string $type The identifier of the type you are interested in
255 * @return bool True if the type is registered
256 */
257 public function is_known_type($type) {
abed5d65 258 return array_key_exists($type, $this->types);
32528f94
DS
259 }
260
261 /**
262 * Check to see if the module in question has registered to handle the
263 * type given
264 *
265 * @param string $module The name of the module
266 * @param string $type The identifier of the type
267 * @return bool True if the module has registered to handle that type
268 */
269 public function has_type_handler($module, $type) {
abed5d65
DS
270 if (!$this->is_known_type($type)) {
271 throw new coding_exception("Checking for handler for unknown type $type");
272 }
273 foreach ($this->types[$type]->handlers as $handler) {
274 if ($handler->module == $module) {
275 return true;
32528f94
DS
276 }
277 }
278 return false;
279 }
280
281 /**
282 * Check to see if the module in question has registered to handle files
283 * with the given extension (or to handle all file types)
284 *
285 * @param string $module The name of the module
286 * @param string $extension The extension of the uploaded file
287 * @return bool True if the module has registered to handle files with
288 * that extension (or to handle all file types)
289 */
290 public function has_file_handler($module, $extension) {
291 foreach ($this->filehandlers as $handler) {
292 if ($handler->module == $module) {
293 if ($handler->extension == '*' || $handler->extension == $extension) {
294 return true;
295 }
296 }
297 }
298 return false;
299 }
300
301 /**
302 * Gets a list of the file types that are handled by a particular module
303 *
304 * @param string $module The name of the module to check
305 * @return array of file extensions or string '*'
306 */
307 public function get_handled_file_types($module) {
308 $types = array();
309 foreach ($this->filehandlers as $handler) {
310 if ($handler->module == $module) {
311 if ($handler->extension == '*') {
312 return '*';
313 } else {
314 // Prepending '.' as otherwise mimeinfo fails.
315 $types[] = '.'.$handler->extension;
316 }
317 }
318 }
319 return $types;
320 }
321
322 /**
323 * Returns an object to pass onto the javascript code with data about all the
324 * registered file / type handlers
325 *
326 * @return object Data to pass on to Javascript code
327 */
328 public function get_js_data() {
f684250b
DS
329 global $CFG;
330
32528f94
DS
331 $ret = new stdClass;
332
333 // Sort the types by priority.
334 uasort($this->types, array($this, 'type_compare'));
335
336 $ret->types = array();
1ddc3d1f 337 if (!empty($CFG->dndallowtextandlinks)) {
f684250b
DS
338 foreach ($this->types as $type) {
339 if (empty($type->handlers)) {
340 continue; // Skip any types without registered handlers.
341 }
342 $ret->types[] = $type;
32528f94 343 }
32528f94
DS
344 }
345
346 $ret->filehandlers = $this->filehandlers;
e6a0f101 347 $uploadrepo = repository::get_instances(array('type' => 'upload', 'currentcontext' => $this->context));
32528f94
DS
348 if (empty($uploadrepo)) {
349 $ret->filehandlers = array(); // No upload repo => no file handlers.
350 }
351
352 return $ret;
353 }
354
355 /**
356 * Comparison function used when sorting types by priority
357 * @param object $type1 first type to compare
358 * @param object $type2 second type to compare
359 * @return integer -1 for $type1 < $type2; 1 for $type1 > $type2; 0 for equal
360 */
361 protected function type_compare($type1, $type2) {
362 if ($type1->priority < $type2->priority) {
363 return -1;
364 }
365 if ($type1->priority > $type2->priority) {
366 return 1;
367 }
368 return 0;
369 }
370
371}
372
373/**
374 * Processes the upload, creating the course module and returning the result
375 *
376 * @package core
377 * @copyright 2012 Davo Smith
378 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
379 */
abed5d65 380class dndupload_ajax_processor {
32528f94
DS
381
382 /** Returned when no error has occurred */
383 const ERROR_OK = 0;
384
385 /** @var object The course that we are uploading to */
386 protected $course = null;
387
388 /** @var context_course The course context for capability checking */
389 protected $context = null;
390
391 /** @var int The section number we are uploading to */
392 protected $section = null;
393
394 /** @var string The type of upload (e.g. 'Files', 'text/plain') */
395 protected $type = null;
396
397 /** @var object The details of the module type that will be created */
398 protected $module= null;
399
400 /** @var object The course module that has been created */
401 protected $cm = null;
402
403 /** @var dndupload_handler used to check the allowed file types */
404 protected $dnduploadhandler = null;
405
406 /** @var string The name to give the new activity instance */
407 protected $displayname = null;
408
409 /**
410 * Set up some basic information needed to handle the upload
411 *
412 * @param int $courseid The ID of the course we are uploading to
413 * @param int $section The section number we are uploading to
414 * @param string $type The type of upload (as reported by the browser)
415 * @param string $modulename The name of the module requested to handle this upload
416 */
417 public function __construct($courseid, $section, $type, $modulename) {
418 global $DB;
419
abed5d65
DS
420 if (!defined('AJAX_SCRIPT')) {
421 throw new coding_exception('dndupload_ajax_processor should only be used within AJAX requests');
422 }
423
74df2951 424 $this->course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
32528f94
DS
425
426 require_login($this->course, false);
427 $this->context = context_course::instance($this->course->id);
428
5103b5e6 429 if (!is_number($section) || $section < 0) {
32528f94
DS
430 throw new coding_exception("Invalid section number $section");
431 }
432 $this->section = $section;
433 $this->type = $type;
434
435 if (!$this->module = $DB->get_record('modules', array('name' => $modulename))) {
436 throw new coding_exception("Module $modulename does not exist");
437 }
438
439 $this->dnduploadhandler = new dndupload_handler($this->course);
440 }
441
442 /**
443 * Check if this upload is a 'file' upload
444 *
445 * @return bool true if it is a 'file' upload, false otherwise
446 */
447 protected function is_file_upload() {
448 return ($this->type == 'Files');
449 }
450
451 /**
452 * Process the upload - creating the module in the course and returning the result to the browser
453 *
454 * @param string $displayname optional the name (from the browser) to give the course module instance
455 * @param string $content optional the content of the upload (for non-file uploads)
456 */
457 public function process($displayname = null, $content = null) {
458 require_capability('moodle/course:manageactivities', $this->context);
459
460 if ($this->is_file_upload()) {
461 require_capability('moodle/course:managefiles', $this->context);
abed5d65 462 if ($content != null) {
33b24bdd 463 throw new moodle_exception('fileuploadwithcontent', 'moodle');
abed5d65 464 }
f684250b
DS
465 } else {
466 if (empty($content)) {
467 throw new moodle_exception('dnduploadwithoutcontent', 'moodle');
468 }
32528f94
DS
469 }
470
471 require_sesskey();
472
473 $this->displayname = $displayname;
474
475 if ($this->is_file_upload()) {
476 $this->handle_file_upload();
477 } else {
478 $this->handle_other_upload($content);
479 }
480 }
481
482 /**
483 * Handle uploads containing files - create the course module, ask the upload repository
484 * to process the file, ask the mod to set itself up, then return the result to the browser
485 */
486 protected function handle_file_upload() {
487 global $CFG;
488
489 // Add the file to a draft file area.
490 $draftitemid = file_get_unused_draft_itemid();
6afbdf27 491 $maxbytes = get_user_max_upload_file_size($this->context, $CFG->maxbytes, $this->course->maxbytes);
32528f94 492 $types = $this->dnduploadhandler->get_handled_file_types($this->module->name);
a80f5922 493 $repo = repository::get_instances(array('type' => 'upload', 'currentcontext' => $this->context));
32528f94 494 if (empty($repo)) {
33b24bdd 495 throw new moodle_exception('errornouploadrepo', 'moodle');
32528f94
DS
496 }
497 $repo = reset($repo); // Get the first (and only) upload repo.
498 $details = $repo->process_upload(null, $maxbytes, $types, '/', $draftitemid);
499 if (empty($this->displayname)) {
500 $this->displayname = $this->display_name_from_file($details['file']);
501 }
502
503 // Create a course module to hold the new instance.
504 $this->create_course_module();
505
506 // Ask the module to set itself up.
507 $moduledata = $this->prepare_module_data($draftitemid);
508 $instanceid = plugin_callback('mod', $this->module->name, 'dndupload', 'handle', array($moduledata), 'invalidfunction');
509 if ($instanceid === 'invalidfunction') {
510 throw new coding_exception("{$this->module->name} does not support drag and drop upload (missing {$this->module->name}_dndupload_handle function");
511 }
512
513 // Finish setting up the course module.
514 $this->finish_setup_course_module($instanceid);
515 }
516
517 /**
518 * Handle uploads not containing file - create the course module, ask the mod to
519 * set itself up, then return the result to the browser
520 *
521 * @param string $content the content uploaded to the browser
522 */
523 protected function handle_other_upload($content) {
abed5d65
DS
524 // Check this plugin is registered to handle this type of upload
525 if (!$this->dnduploadhandler->has_type_handler($this->module->name, $this->type)) {
526 $info = (object)array('modname' => $this->module->name, 'type' => $this->type);
33b24bdd 527 throw new moodle_exception('moddoesnotsupporttype', 'moodle', $info);
abed5d65
DS
528 }
529
32528f94
DS
530 // Create a course module to hold the new instance.
531 $this->create_course_module();
532
533 // Ask the module to set itself up.
534 $moduledata = $this->prepare_module_data(null, $content);
535 $instanceid = plugin_callback('mod', $this->module->name, 'dndupload', 'handle', array($moduledata), 'invalidfunction');
536 if ($instanceid === 'invalidfunction') {
537 throw new coding_exception("{$this->module->name} does not support drag and drop upload (missing {$this->module->name}_dndupload_handle function");
538 }
539
540 // Finish setting up the course module.
541 $this->finish_setup_course_module($instanceid);
542 }
543
544 /**
545 * Generate the name of the mod instance from the name of the file
546 * (remove the extension and convert underscore => space
547 *
548 * @param string $filename the filename of the uploaded file
549 * @return string the display name to use
550 */
551 protected function display_name_from_file($filename) {
2f1e464a 552 $pos = core_text::strrpos($filename, '.');
32528f94 553 if ($pos) { // Want to skip if $pos === 0 OR $pos === false.
2f1e464a 554 $filename = core_text::substr($filename, 0, $pos);
32528f94
DS
555 }
556 return str_replace('_', ' ', $filename);
557 }
558
559 /**
560 * Create the coursemodule to hold the file/content that has been uploaded
561 */
562 protected function create_course_module() {
ac1a2975 563 global $CFG;
7f53e8aa 564 require_once($CFG->dirroot.'/course/modlib.php');
b17ee682 565 list($module, $context, $cw, $cm, $data) = prepare_new_moduleinfo_data($this->course, $this->module->name, $this->section);
ac1a2975 566
7f53e8aa
MG
567 $data->coursemodule = $data->id = add_course_module($data);
568 $this->cm = $data;
32528f94
DS
569 }
570
571 /**
572 * Gather together all the details to pass on to the mod, so that it can initialise it's
573 * own database tables
574 *
575 * @param int $draftitemid optional the id of the draft area containing the file (for file uploads)
576 * @param string $content optional the content dropped onto the course (for non-file uploads)
577 * @return object data to pass on to the mod, containing:
578 * string $type the 'type' as registered with dndupload_handler (or 'Files')
579 * object $course the course the upload was for
580 * int $draftitemid optional the id of the draft area containing the files
581 * int $coursemodule id of the course module that has already been created
582 * string $displayname the name to use for this activity (can be overriden by the mod)
583 */
584 protected function prepare_module_data($draftitemid = null, $content = null) {
585 $data = new stdClass();
586 $data->type = $this->type;
587 $data->course = $this->course;
588 if ($draftitemid) {
589 $data->draftitemid = $draftitemid;
590 } else if ($content) {
591 $data->content = $content;
592 }
593 $data->coursemodule = $this->cm->id;
594 $data->displayname = $this->displayname;
595 return $data;
596 }
597
598 /**
599 * Called after the mod has set itself up, to finish off any course module settings
600 * (set instance id, add to correct section, set visibility, etc.) and send the response
601 *
602 * @param int $instanceid id returned by the mod when it was created
603 */
604 protected function finish_setup_course_module($instanceid) {
605 global $DB, $USER;
606
607 if (!$instanceid) {
608 // Something has gone wrong - undo everything we can.
a347aee3 609 course_delete_module($this->cm->id);
33b24bdd 610 throw new moodle_exception('errorcreatingactivity', 'moodle', '', $this->module->name);
32528f94
DS
611 }
612
0470a094
DS
613 // Note the section visibility
614 $visible = get_fast_modinfo($this->course)->get_section_info($this->section)->visible;
615
32528f94 616 $DB->set_field('course_modules', 'instance', $instanceid, array('id' => $this->cm->id));
38b19bbc
MG
617 // Rebuild the course cache after update action
618 rebuild_course_cache($this->course->id, true);
32528f94 619
722e6ba9 620 $sectionid = course_add_cm_to_section($this->course, $this->cm->id, $this->section);
32528f94 621
0470a094
DS
622 set_coursemodule_visible($this->cm->id, $visible);
623 if (!$visible) {
624 $DB->set_field('course_modules', 'visibleold', 1, array('id' => $this->cm->id));
625 }
32528f94 626
38b19bbc 627 // retrieve the final info about this module.
32528f94 628 $info = get_fast_modinfo($this->course);
5103b5e6
DS
629 if (!isset($info->cms[$this->cm->id])) {
630 // The course module has not been properly created in the course - undo everything.
a347aee3 631 course_delete_module($this->cm->id);
33b24bdd 632 throw new moodle_exception('errorcreatingactivity', 'moodle', '', $this->module->name);
5103b5e6 633 }
0470a094 634 $mod = $info->get_cm($this->cm->id);
32528f94 635
63deb5c3 636 // Trigger course module created event.
e20d3069 637 $event = \core\event\course_module_created::create_from_cm($mod);
63deb5c3
AA
638 $event->trigger();
639
32528f94
DS
640 $this->send_response($mod);
641 }
642
643 /**
644 * Send the details of the newly created activity back to the client browser
645 *
646 * @param cm_info $mod details of the mod just created
647 */
648 protected function send_response($mod) {
9a36be73 649 global $OUTPUT, $PAGE;
abed5d65 650
32528f94
DS
651 $resp = new stdClass();
652 $resp->error = self::ERROR_OK;
9b2ad813
AN
653 $resp->elementid = 'module-' . $mod->id;
654
655 $courserenderer = $PAGE->get_renderer('core', 'course');
656 $completioninfo = new completion_info($this->course);
657 $info = get_fast_modinfo($this->course);
658 $sr = null;
659 $modulehtml = $courserenderer->course_section_cm($this->course, $completioninfo,
660 $mod, null, array());
661 $resp->fullcontent = $courserenderer->course_section_cm_list_item($this->course, $completioninfo, $mod, $sr);
d022f632 662
abed5d65 663 echo $OUTPUT->header();
32528f94
DS
664 echo json_encode($resp);
665 die();
666 }
3970a471 667}