Merge branch 'MDL-62560-master'
[moodle.git] / mod / imscp / lib.php
blobb6378623a049f9a35b20e14127b9a5b99175f181
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.
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/>.
17 /**
18 * Mandatory public API of imscp module
20 * @package mod_imscp
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();
27 /**
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
32 function imscp_supports($feature) {
33 switch($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;
44 default: return null;
48 /**
49 * Returns all other caps used in module
50 * @return array
52 function imscp_get_extra_capabilities() {
53 return array('moodle/site:accessallgroups');
56 /**
57 * This function is used by the reset_course_userdata function in moodlelib.
59 * @param stdClass $data the data submitted from the reset course.
60 * @return array status array
62 function imscp_reset_userdata($data) {
64 // Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
65 // See MDL-9367.
67 return array();
70 /**
71 * List the actions that correspond to a view of this module.
72 * This is used by the participation report.
74 * Note: This is not used by new logging system. Event with
75 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will
76 * be considered as view action.
78 * @return array
80 function imscp_get_view_actions() {
81 return array('view', 'view all');
84 /**
85 * List the actions that correspond to a post of this module.
86 * This is used by the participation report.
88 * Note: This is not used by new logging system. Event with
89 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
90 * will be considered as post action.
92 * @return array
94 function imscp_get_post_actions() {
95 return array('update', 'add');
98 /**
99 * Add imscp instance.
100 * @param object $data
101 * @param object $mform
102 * @return int new imscp instance id
104 function imscp_add_instance($data, $mform) {
105 global $CFG, $DB;
106 require_once("$CFG->dirroot/mod/imscp/locallib.php");
108 $cmid = $data->coursemodule;
110 $data->timemodified = time();
111 $data->revision = 1;
112 $data->structure = null;
114 $data->id = $DB->insert_record('imscp', $data);
116 // We need to use context now, so we need to make sure all needed info is already in db.
117 $DB->set_field('course_modules', 'instance', $data->id, array('id' => $cmid));
118 $context = context_module::instance($cmid);
119 $imscp = $DB->get_record('imscp', array('id' => $data->id), '*', MUST_EXIST);
121 if (!empty($data->package)) {
122 // Save uploaded files to 'backup' filearea.
123 $fs = get_file_storage();
124 $fs->delete_area_files($context->id, 'mod_imscp', 'backup', 1);
125 file_save_draft_area_files($data->package, $context->id, 'mod_imscp', 'backup',
126 1, array('subdirs' => 0, 'maxfiles' => 1));
127 // Get filename of zip that was uploaded.
128 $files = $fs->get_area_files($context->id, 'mod_imscp', 'backup', 1, '', false);
129 if ($files) {
130 // Extract package content to 'content' filearea.
131 $package = reset($files);
132 $packer = get_file_packer('application/zip');
133 $package->extract_to_storage($packer, $context->id, 'mod_imscp', 'content', 1, '/');
134 $structure = imscp_parse_structure($imscp, $context);
135 $imscp->structure = is_array($structure) ? serialize($structure) : null;
136 $DB->update_record('imscp', $imscp);
140 $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
141 \core_completion\api::update_completion_date_event($cmid, 'imscp', $data->id, $completiontimeexpected);
143 return $data->id;
147 * Update imscp instance.
148 * @param object $data
149 * @param object $mform
150 * @return bool true
152 function imscp_update_instance($data, $mform) {
153 global $CFG, $DB;
154 require_once("$CFG->dirroot/mod/imscp/locallib.php");
156 $cmid = $data->coursemodule;
158 $data->timemodified = time();
159 $data->id = $data->instance;
160 $data->structure = null; // Better reparse structure after each update.
162 $DB->update_record('imscp', $data);
164 $context = context_module::instance($cmid);
165 $imscp = $DB->get_record('imscp', array('id' => $data->id), '*', MUST_EXIST);
167 if (!empty($data->package) && ($draftareainfo = file_get_draft_area_info($data->package)) &&
168 $draftareainfo['filecount']) {
169 $fs = get_file_storage();
171 $imscp->revision++;
172 $DB->update_record('imscp', $imscp);
174 // Get a list of existing packages before adding new package.
175 if ($imscp->keepold > -1) {
176 $packages = $fs->get_area_files($context->id, 'mod_imscp', 'backup', false, "itemid ASC", false);
177 } else {
178 $packages = array();
181 file_save_draft_area_files($data->package, $context->id, 'mod_imscp', 'backup',
182 $imscp->revision, array('subdirs' => 0, 'maxfiles' => 1));
183 $files = $fs->get_area_files($context->id, 'mod_imscp', 'backup', $imscp->revision, '', false);
184 $package = reset($files);
186 // Purge all extracted content.
187 $fs->delete_area_files($context->id, 'mod_imscp', 'content');
189 // Extract package content.
190 if ($package) {
191 $packer = get_file_packer('application/zip');
192 $package->extract_to_storage($packer, $context->id, 'mod_imscp', 'content', $imscp->revision, '/');
195 // Cleanup old package files, keep current + keep old.
196 while ($packages and (count($packages) > $imscp->keepold)) {
197 $package = array_shift($packages);
198 $fs->delete_area_files($context->id, 'mod_imscp', 'backup', $package->get_itemid());
202 $structure = imscp_parse_structure($imscp, $context);
203 $imscp->structure = is_array($structure) ? serialize($structure) : null;
204 $DB->update_record('imscp', $imscp);
206 $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
207 \core_completion\api::update_completion_date_event($cmid, 'imscp', $imscp->id, $completiontimeexpected);
209 return true;
213 * Delete imscp instance.
214 * @param int $id
215 * @return bool true
217 function imscp_delete_instance($id) {
218 global $DB;
220 if (!$imscp = $DB->get_record('imscp', array('id' => $id))) {
221 return false;
224 $cm = get_coursemodule_from_instance('imscp', $id);
225 \core_completion\api::update_completion_date_event($cm->id, 'imscp', $id, null);
227 // Note: all context files are deleted automatically.
229 $DB->delete_records('imscp', array('id' => $imscp->id));
231 return true;
235 * Lists all browsable file areas
237 * @package mod_imscp
238 * @category files
239 * @param stdClass $course course object
240 * @param stdClass $cm course module object
241 * @param stdClass $context context object
242 * @return array
244 function imscp_get_file_areas($course, $cm, $context) {
245 $areas = array();
247 $areas['content'] = get_string('areacontent', 'imscp');
248 $areas['backup'] = get_string('areabackup', 'imscp');
250 return $areas;
254 * File browsing support for imscp module ontent area.
256 * @package mod_imscp
257 * @category files
258 * @param stdClass $browser file browser
259 * @param stdClass $areas file areas
260 * @param stdClass $course course object
261 * @param stdClass $cm course module object
262 * @param stdClass $context context object
263 * @param string $filearea file area
264 * @param int $itemid item ID
265 * @param string $filepath file path
266 * @param string $filename file name
267 * @return file_info instance or null if not found
269 function imscp_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
270 global $CFG, $DB;
272 // Note: imscp_intro handled in file_browser automatically.
274 if (!has_capability('moodle/course:managefiles', $context)) {
275 // No peeking here for students!
276 return null;
279 if ($filearea !== 'content' and $filearea !== 'backup') {
280 return null;
283 require_once("$CFG->dirroot/mod/imscp/locallib.php");
285 if (is_null($itemid)) {
286 return new imscp_file_info($browser, $course, $cm, $context, $areas, $filearea, $itemid);
289 $fs = get_file_storage();
290 $filepath = is_null($filepath) ? '/' : $filepath;
291 $filename = is_null($filename) ? '.' : $filename;
292 if (!$storedfile = $fs->get_file($context->id, 'mod_imscp', $filearea, $itemid, $filepath, $filename)) {
293 return null;
296 // Do not allow manual modification of any files!
297 $urlbase = $CFG->wwwroot.'/pluginfile.php';
298 return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false); // No writing here!
302 * Serves the imscp files.
304 * @package mod_imscp
305 * @category files
306 * @param stdClass $course course object
307 * @param stdClass $cm course module object
308 * @param stdClass $context context object
309 * @param string $filearea file area
310 * @param array $args extra arguments
311 * @param bool $forcedownload whether or not force download
312 * @param array $options additional options affecting the file serving
313 * @return bool false if file not found, does not return if found - justsend the file
315 function imscp_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
316 global $CFG, $DB;
318 if ($context->contextlevel != CONTEXT_MODULE) {
319 return false;
322 require_login($course, true, $cm);
324 if ($filearea === 'content') {
325 if (!has_capability('mod/imscp:view', $context)) {
326 return false;
328 $revision = array_shift($args);
329 $fs = get_file_storage();
330 $relativepath = implode('/', $args);
331 if ($relativepath === 'imsmanifest.xml') {
332 if (!has_capability('moodle/course:managefiles', $context)) {
333 // No stealing of detailed package info.
334 return false;
337 $fullpath = "/$context->id/mod_imscp/$filearea/$revision/$relativepath";
338 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
339 return false;
342 // Finally send the file.
343 send_stored_file($file, null, 0, $forcedownload, $options);
345 } else if ($filearea === 'backup') {
346 if (!has_capability('moodle/course:managefiles', $context)) {
347 // No stealing of package backups.
348 return false;
350 $revision = array_shift($args);
351 $fs = get_file_storage();
352 $relativepath = implode('/', $args);
353 $fullpath = "/$context->id/mod_imscp/$filearea/$revision/$relativepath";
354 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
355 return false;
358 // Finally send the file.
359 send_stored_file($file, null, 0, $forcedownload, $options);
361 } else {
362 return false;
367 * Return a list of page types
368 * @param string $pagetype current page type
369 * @param stdClass $parentcontext Block's parent context
370 * @param stdClass $currentcontext Current context of block
371 * @return array $modulepagetype list
373 function imscp_page_type_list($pagetype, $parentcontext, $currentcontext) {
374 $modulepagetype = array('mod-imscp-*' => get_string('page-mod-imscp-x', 'imscp'));
375 return $modulepagetype;
379 * Export imscp resource contents
381 * @param stdClass $cm Course module object
382 * @param string $baseurl Base URL for file downloads
383 * @return array of file content
385 function imscp_export_contents($cm, $baseurl) {
386 global $DB;
388 $contents = array();
389 $context = context_module::instance($cm->id);
391 $imscp = $DB->get_record('imscp', array('id' => $cm->instance), '*', MUST_EXIST);
393 // We export the IMSCP structure as json encoded string.
394 $structure = array();
395 $structure['type'] = 'content';
396 $structure['filename'] = 'structure';
397 $structure['filepath'] = '/';
398 $structure['filesize'] = 0;
399 $structure['fileurl'] = null;
400 $structure['timecreated'] = $imscp->timemodified;
401 $structure['timemodified'] = $imscp->timemodified;
402 $structure['content'] = json_encode(unserialize($imscp->structure));
403 $structure['sortorder'] = 0;
404 $structure['userid'] = null;
405 $structure['author'] = null;
406 $structure['license'] = null;
407 $contents[] = $structure;
409 // Area files.
410 $fs = get_file_storage();
411 $files = $fs->get_area_files($context->id, 'mod_imscp', 'content', $imscp->revision, 'id ASC', false);
412 foreach ($files as $fileinfo) {
413 $file = array();
414 $file['type'] = 'file';
415 $file['filename'] = $fileinfo->get_filename();
416 $file['filepath'] = $fileinfo->get_filepath();
417 $file['filesize'] = $fileinfo->get_filesize();
418 $file['fileurl'] = moodle_url::make_webservice_pluginfile_url(
419 $context->id, 'mod_imscp', 'content', $imscp->revision,
420 $fileinfo->get_filepath(), $fileinfo->get_filename())->out(false);
421 $file['timecreated'] = $fileinfo->get_timecreated();
422 $file['timemodified'] = $fileinfo->get_timemodified();
423 $file['sortorder'] = $fileinfo->get_sortorder();
424 $file['userid'] = $fileinfo->get_userid();
425 $file['author'] = $fileinfo->get_author();
426 $file['license'] = $fileinfo->get_license();
427 $file['mimetype'] = $fileinfo->get_mimetype();
428 $file['isexternalfile'] = $fileinfo->is_external_file();
429 if ($file['isexternalfile']) {
430 $file['repositorytype'] = $fileinfo->get_repository_type();
432 $contents[] = $file;
435 return $contents;
439 * Mark the activity completed (if required) and trigger the course_module_viewed event.
441 * @param stdClass $imscp imscp object
442 * @param stdClass $course course object
443 * @param stdClass $cm course module object
444 * @param stdClass $context context object
445 * @since Moodle 3.0
447 function imscp_view($imscp, $course, $cm, $context) {
449 // Trigger course_module_viewed event.
450 $params = array(
451 'context' => $context,
452 'objectid' => $imscp->id
455 $event = \mod_imscp\event\course_module_viewed::create($params);
456 $event->add_record_snapshot('course_modules', $cm);
457 $event->add_record_snapshot('course', $course);
458 $event->add_record_snapshot('imscp', $imscp);
459 $event->trigger();
461 // Completion.
462 $completion = new completion_info($course);
463 $completion->set_module_viewed($cm);
467 * Check if the module has any update that affects the current user since a given time.
469 * @param cm_info $cm course module data
470 * @param int $from the time to check updates from
471 * @param array $filter if we need to check only specific updates
472 * @return stdClass an object with the different type of areas indicating if they were updated or not
473 * @since Moodle 3.2
475 function imscp_check_updates_since(cm_info $cm, $from, $filter = array()) {
476 $updates = course_check_module_updates_since($cm, $from, array('content'), $filter);
477 return $updates;
481 * This function receives a calendar event and returns the action associated with it, or null if there is none.
483 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event
484 * is not displayed on the block.
486 * @param calendar_event $event
487 * @param \core_calendar\action_factory $factory
488 * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default).
489 * @return \core_calendar\local\event\entities\action_interface|null
491 function mod_imscp_core_calendar_provide_event_action(calendar_event $event,
492 \core_calendar\action_factory $factory,
493 int $userid = 0) {
494 $cm = get_fast_modinfo($event->courseid, $userid)->instances['imscp'][$event->instance];
496 if (!$cm->uservisible) {
497 // The module is not visible to the user for any reason.
498 return null;
501 $completion = new \completion_info($cm->get_course());
503 $completiondata = $completion->get_data($cm, false, $userid);
505 if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {
506 return null;
509 return $factory->create_instance(
510 get_string('view'),
511 new \moodle_url('/mod/imscp/view.php', ['id' => $cm->id]),
513 true