2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Mandatory public API of imscp module
21 * @copyright 2009 Petr Skoda {@link http://skodak.org}
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') ||
die();
28 * List of features supported in IMS CP module
29 * @param string $feature FEATURE_xx constant for requested feature
30 * @return mixed True if module supports feature, false if not, null if doesn't know or string for the module purpose.
32 function imscp_supports($feature) {
34 case FEATURE_MOD_ARCHETYPE
: return MOD_ARCHETYPE_RESOURCE
;
35 case FEATURE_GROUPS
: return false;
36 case FEATURE_GROUPINGS
: return false;
37 case FEATURE_MOD_INTRO
: return true;
38 case FEATURE_COMPLETION_TRACKS_VIEWS
: return true;
39 case FEATURE_GRADE_HAS_GRADE
: return false;
40 case FEATURE_GRADE_OUTCOMES
: return false;
41 case FEATURE_BACKUP_MOODLE2
: return true;
42 case FEATURE_SHOW_DESCRIPTION
: return true;
43 case FEATURE_MOD_PURPOSE
: return MOD_PURPOSE_CONTENT
;
50 * This function is used by the reset_course_userdata function in moodlelib.
52 * @param stdClass $data the data submitted from the reset course.
53 * @return array status array
55 function imscp_reset_userdata($data) {
57 // Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
64 * List the actions that correspond to a view of this module.
65 * This is used by the participation report.
67 * Note: This is not used by new logging system. Event with
68 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will
69 * be considered as view action.
73 function imscp_get_view_actions() {
74 return array('view', 'view all');
78 * List the actions that correspond to a post of this module.
79 * This is used by the participation report.
81 * Note: This is not used by new logging system. Event with
82 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
83 * will be considered as post action.
87 function imscp_get_post_actions() {
88 return array('update', 'add');
94 * @param object $mform
95 * @return int new imscp instance id
97 function imscp_add_instance($data, $mform) {
99 require_once("$CFG->dirroot/mod/imscp/locallib.php");
101 $cmid = $data->coursemodule
;
103 $data->timemodified
= time();
105 $data->structure
= null;
107 $data->id
= $DB->insert_record('imscp', $data);
109 // We need to use context now, so we need to make sure all needed info is already in db.
110 $DB->set_field('course_modules', 'instance', $data->id
, array('id' => $cmid));
111 $context = context_module
::instance($cmid);
112 $imscp = $DB->get_record('imscp', array('id' => $data->id
), '*', MUST_EXIST
);
114 if (!empty($data->package
)) {
115 // Save uploaded files to 'backup' filearea.
116 $fs = get_file_storage();
117 $fs->delete_area_files($context->id
, 'mod_imscp', 'backup', 1);
118 file_save_draft_area_files($data->package
, $context->id
, 'mod_imscp', 'backup',
119 1, array('subdirs' => 0, 'maxfiles' => 1));
120 // Get filename of zip that was uploaded.
121 $files = $fs->get_area_files($context->id
, 'mod_imscp', 'backup', 1, '', false);
123 // Extract package content to 'content' filearea.
124 $package = reset($files);
125 $packer = get_file_packer('application/zip');
126 $package->extract_to_storage($packer, $context->id
, 'mod_imscp', 'content', 1, '/');
127 $structure = imscp_parse_structure($imscp, $context);
128 $imscp->structure
= is_array($structure) ?
serialize($structure) : null;
129 $DB->update_record('imscp', $imscp);
133 $completiontimeexpected = !empty($data->completionexpected
) ?
$data->completionexpected
: null;
134 \core_completion\api
::update_completion_date_event($cmid, 'imscp', $data->id
, $completiontimeexpected);
140 * Update imscp instance.
141 * @param object $data
142 * @param object $mform
145 function imscp_update_instance($data, $mform) {
147 require_once("$CFG->dirroot/mod/imscp/locallib.php");
149 $cmid = $data->coursemodule
;
151 $data->timemodified
= time();
152 $data->id
= $data->instance
;
153 $data->structure
= null; // Better reparse structure after each update.
155 $DB->update_record('imscp', $data);
157 $context = context_module
::instance($cmid);
158 $imscp = $DB->get_record('imscp', array('id' => $data->id
), '*', MUST_EXIST
);
160 if (!empty($data->package
) && ($draftareainfo = file_get_draft_area_info($data->package
)) &&
161 $draftareainfo['filecount']) {
162 $fs = get_file_storage();
165 $DB->update_record('imscp', $imscp);
167 // Get a list of existing packages before adding new package.
168 if ($imscp->keepold
> -1) {
169 $packages = $fs->get_area_files($context->id
, 'mod_imscp', 'backup', false, "itemid ASC", false);
174 file_save_draft_area_files($data->package
, $context->id
, 'mod_imscp', 'backup',
175 $imscp->revision
, array('subdirs' => 0, 'maxfiles' => 1));
176 $files = $fs->get_area_files($context->id
, 'mod_imscp', 'backup', $imscp->revision
, '', false);
177 $package = reset($files);
179 // Purge all extracted content.
180 $fs->delete_area_files($context->id
, 'mod_imscp', 'content');
182 // Extract package content.
184 $packer = get_file_packer('application/zip');
185 $package->extract_to_storage($packer, $context->id
, 'mod_imscp', 'content', $imscp->revision
, '/');
188 // Cleanup old package files, keep current + keep old.
189 while ($packages and (count($packages) > $imscp->keepold
)) {
190 $package = array_shift($packages);
191 $fs->delete_area_files($context->id
, 'mod_imscp', 'backup', $package->get_itemid());
195 $structure = imscp_parse_structure($imscp, $context);
196 $imscp->structure
= is_array($structure) ?
serialize($structure) : null;
197 $DB->update_record('imscp', $imscp);
199 $completiontimeexpected = !empty($data->completionexpected
) ?
$data->completionexpected
: null;
200 \core_completion\api
::update_completion_date_event($cmid, 'imscp', $imscp->id
, $completiontimeexpected);
206 * Delete imscp instance.
210 function imscp_delete_instance($id) {
213 if (!$imscp = $DB->get_record('imscp', array('id' => $id))) {
217 $cm = get_coursemodule_from_instance('imscp', $id);
218 \core_completion\api
::update_completion_date_event($cm->id
, 'imscp', $id, null);
220 // Note: all context files are deleted automatically.
222 $DB->delete_records('imscp', array('id' => $imscp->id
));
228 * Lists all browsable file areas
232 * @param stdClass $course course object
233 * @param stdClass $cm course module object
234 * @param stdClass $context context object
237 function imscp_get_file_areas($course, $cm, $context) {
240 $areas['content'] = get_string('areacontent', 'imscp');
241 $areas['backup'] = get_string('areabackup', 'imscp');
247 * File browsing support for imscp module ontent area.
251 * @param file_browser $browser file browser
252 * @param stdClass $areas file areas
253 * @param stdClass $course course object
254 * @param stdClass $cm course module object
255 * @param stdClass $context context object
256 * @param string $filearea file area
257 * @param int $itemid item ID
258 * @param string $filepath file path
259 * @param string $filename file name
260 * @return file_info instance or null if not found
262 function imscp_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
265 // Note: imscp_intro handled in file_browser automatically.
267 if (!has_capability('moodle/course:managefiles', $context)) {
268 // No peeking here for students!
272 if ($filearea !== 'content' and $filearea !== 'backup') {
276 require_once("$CFG->dirroot/mod/imscp/locallib.php");
278 if (is_null($itemid)) {
279 return new imscp_file_info($browser, $course, $cm, $context, $areas, $filearea, $itemid);
282 $fs = get_file_storage();
283 $filepath = is_null($filepath) ?
'/' : $filepath;
284 $filename = is_null($filename) ?
'.' : $filename;
285 if (!$storedfile = $fs->get_file($context->id
, 'mod_imscp', $filearea, $itemid, $filepath, $filename)) {
289 // Do not allow manual modification of any files!
290 $urlbase = $CFG->wwwroot
.'/pluginfile.php';
291 return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false); // No writing here!
295 * Serves the imscp files.
299 * @param stdClass $course course object
300 * @param stdClass $cm course module object
301 * @param stdClass $context context object
302 * @param string $filearea file area
303 * @param array $args extra arguments
304 * @param bool $forcedownload whether or not force download
305 * @param array $options additional options affecting the file serving
306 * @return bool false if file not found, does not return if found - justsend the file
308 function imscp_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
311 if ($context->contextlevel
!= CONTEXT_MODULE
) {
315 require_login($course, true, $cm);
317 if ($filearea === 'content') {
318 if (!has_capability('mod/imscp:view', $context)) {
321 $revision = array_shift($args);
322 $fs = get_file_storage();
323 $relativepath = implode('/', $args);
324 if ($relativepath === 'imsmanifest.xml') {
325 if (!has_capability('moodle/course:managefiles', $context)) {
326 // No stealing of detailed package info.
330 $fullpath = "/$context->id/mod_imscp/$filearea/$revision/$relativepath";
331 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
335 // Finally send the file.
336 send_stored_file($file, null, 0, $forcedownload, $options);
338 } else if ($filearea === 'backup') {
339 if (!has_capability('moodle/course:managefiles', $context)) {
340 // No stealing of package backups.
343 $revision = array_shift($args);
344 $fs = get_file_storage();
345 $relativepath = implode('/', $args);
346 $fullpath = "/$context->id/mod_imscp/$filearea/$revision/$relativepath";
347 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
351 // Finally send the file.
352 send_stored_file($file, null, 0, $forcedownload, $options);
360 * Return a list of page types
361 * @param string $pagetype current page type
362 * @param stdClass $parentcontext Block's parent context
363 * @param stdClass $currentcontext Current context of block
364 * @return array $modulepagetype list
366 function imscp_page_type_list($pagetype, $parentcontext, $currentcontext) {
367 $modulepagetype = array('mod-imscp-*' => get_string('page-mod-imscp-x', 'imscp'));
368 return $modulepagetype;
372 * Export imscp resource contents
374 * @param stdClass $cm Course module object
375 * @param string $baseurl Base URL for file downloads
376 * @return array of file content
378 function imscp_export_contents($cm, $baseurl) {
382 $context = context_module
::instance($cm->id
);
384 $imscp = $DB->get_record('imscp', array('id' => $cm->instance
), '*', MUST_EXIST
);
386 // We export the IMSCP structure as json encoded string.
387 $structure = array();
388 $structure['type'] = 'content';
389 $structure['filename'] = 'structure';
390 $structure['filepath'] = '/';
391 $structure['filesize'] = 0;
392 $structure['fileurl'] = null;
393 $structure['timecreated'] = $imscp->timemodified
;
394 $structure['timemodified'] = $imscp->timemodified
;
395 $structure['content'] = json_encode(unserialize($imscp->structure
));
396 $structure['sortorder'] = 0;
397 $structure['userid'] = null;
398 $structure['author'] = null;
399 $structure['license'] = null;
400 $contents[] = $structure;
403 $fs = get_file_storage();
404 $files = $fs->get_area_files($context->id
, 'mod_imscp', 'content', $imscp->revision
, 'id ASC', false);
405 foreach ($files as $fileinfo) {
407 $file['type'] = 'file';
408 $file['filename'] = $fileinfo->get_filename();
409 $file['filepath'] = $fileinfo->get_filepath();
410 $file['filesize'] = $fileinfo->get_filesize();
411 $file['fileurl'] = moodle_url
::make_webservice_pluginfile_url(
412 $context->id
, 'mod_imscp', 'content', $imscp->revision
,
413 $fileinfo->get_filepath(), $fileinfo->get_filename())->out(false);
414 $file['timecreated'] = $fileinfo->get_timecreated();
415 $file['timemodified'] = $fileinfo->get_timemodified();
416 $file['sortorder'] = $fileinfo->get_sortorder();
417 $file['userid'] = $fileinfo->get_userid();
418 $file['author'] = $fileinfo->get_author();
419 $file['license'] = $fileinfo->get_license();
420 $file['mimetype'] = $fileinfo->get_mimetype();
421 $file['isexternalfile'] = $fileinfo->is_external_file();
422 if ($file['isexternalfile']) {
423 $file['repositorytype'] = $fileinfo->get_repository_type();
432 * Mark the activity completed (if required) and trigger the course_module_viewed event.
434 * @param stdClass $imscp imscp object
435 * @param stdClass $course course object
436 * @param stdClass $cm course module object
437 * @param stdClass $context context object
440 function imscp_view($imscp, $course, $cm, $context) {
442 // Trigger course_module_viewed event.
444 'context' => $context,
445 'objectid' => $imscp->id
448 $event = \mod_imscp\event\course_module_viewed
::create($params);
449 $event->add_record_snapshot('course_modules', $cm);
450 $event->add_record_snapshot('course', $course);
451 $event->add_record_snapshot('imscp', $imscp);
455 $completion = new completion_info($course);
456 $completion->set_module_viewed($cm);
460 * Check if the module has any update that affects the current user since a given time.
462 * @param cm_info $cm course module data
463 * @param int $from the time to check updates from
464 * @param array $filter if we need to check only specific updates
465 * @return stdClass an object with the different type of areas indicating if they were updated or not
468 function imscp_check_updates_since(cm_info
$cm, $from, $filter = array()) {
469 $updates = course_check_module_updates_since($cm, $from, array('content'), $filter);
474 * This function receives a calendar event and returns the action associated with it, or null if there is none.
476 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event
477 * is not displayed on the block.
479 * @param calendar_event $event
480 * @param \core_calendar\action_factory $factory
481 * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default).
482 * @return \core_calendar\local\event\entities\action_interface|null
484 function mod_imscp_core_calendar_provide_event_action(calendar_event
$event,
485 \core_calendar\action_factory
$factory,
487 $cm = get_fast_modinfo($event->courseid
, $userid)->instances
['imscp'][$event->instance
];
489 if (!$cm->uservisible
) {
490 // The module is not visible to the user for any reason.
494 $completion = new \
completion_info($cm->get_course());
496 $completiondata = $completion->get_data($cm, false, $userid);
498 if ($completiondata->completionstate
!= COMPLETION_INCOMPLETE
) {
502 return $factory->create_instance(
504 new \
moodle_url('/mod/imscp/view.php', ['id' => $cm->id
]),