MDL-50015 mod_imscp: New WS mod_imscp_view_imscp
[moodle.git] / mod / imscp / lib.php
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/>.
17 /**
18  * Mandatory public API of imscp module
19  *
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
23  */
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
31  */
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;
45     }
46 }
48 /**
49  * Returns all other caps used in module
50  * @return array
51  */
52 function imscp_get_extra_capabilities() {
53     return array('moodle/site:accessallgroups');
54 }
56 /**
57  * This function is used by the reset_course_userdata function in moodlelib.
58  *
59  * @param stdClass $data the data submitted from the reset course.
60  * @return array status array
61  */
62 function imscp_reset_userdata($data) {
63     return array();
64 }
66 /**
67  * List the actions that correspond to a view of this module.
68  * This is used by the participation report.
69  *
70  * Note: This is not used by new logging system. Event with
71  *       crud = 'r' and edulevel = LEVEL_PARTICIPATING will
72  *       be considered as view action.
73  *
74  * @return array
75  */
76 function imscp_get_view_actions() {
77     return array('view', 'view all');
78 }
80 /**
81  * List the actions that correspond to a post of this module.
82  * This is used by the participation report.
83  *
84  * Note: This is not used by new logging system. Event with
85  *       crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
86  *       will be considered as post action.
87  *
88  * @return array
89  */
90 function imscp_get_post_actions() {
91     return array('update', 'add');
92 }
94 /**
95  * Add imscp instance.
96  * @param object $data
97  * @param object $mform
98  * @return int new imscp instance id
99  */
100 function imscp_add_instance($data, $mform) {
101     global $CFG, $DB;
102     require_once("$CFG->dirroot/mod/imscp/locallib.php");
104     $cmid = $data->coursemodule;
106     $data->timemodified = time();
107     $data->revision     = 1;
108     $data->structure    = null;
110     $data->id = $DB->insert_record('imscp', $data);
112     // We need to use context now, so we need to make sure all needed info is already in db.
113     $DB->set_field('course_modules', 'instance', $data->id, array('id' => $cmid));
114     $context = context_module::instance($cmid);
115     $imscp = $DB->get_record('imscp', array('id' => $data->id), '*', MUST_EXIST);
117     if (!empty($data->package)) {
118         // Save uploaded files to 'backup' filearea.
119         $fs = get_file_storage();
120         $fs->delete_area_files($context->id, 'mod_imscp', 'backup', 1);
121         file_save_draft_area_files($data->package, $context->id, 'mod_imscp', 'backup',
122             1, array('subdirs' => 0, 'maxfiles' => 1));
123         // Get filename of zip that was uploaded.
124         $files = $fs->get_area_files($context->id, 'mod_imscp', 'backup', 1, '', false);
125         if ($files) {
126             // Extract package content to 'content' filearea.
127             $package = reset($files);
128             $packer = get_file_packer('application/zip');
129             $package->extract_to_storage($packer, $context->id, 'mod_imscp', 'content', 1, '/');
130             $structure = imscp_parse_structure($imscp, $context);
131             $imscp->structure = is_array($structure) ? serialize($structure) : null;
132             $DB->update_record('imscp', $imscp);
133         }
134     }
136     return $data->id;
139 /**
140  * Update imscp instance.
141  * @param object $data
142  * @param object $mform
143  * @return bool true
144  */
145 function imscp_update_instance($data, $mform) {
146     global $CFG, $DB;
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();
164         $imscp->revision++;
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);
170         } else {
171             $packages = array();
172         }
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.
183         if ($package) {
184             $packer = get_file_packer('application/zip');
185             $package->extract_to_storage($packer, $context->id, 'mod_imscp', 'content', $imscp->revision, '/');
186         }
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());
192         }
193     }
195     $structure = imscp_parse_structure($imscp, $context);
196     $imscp->structure = is_array($structure) ? serialize($structure) : null;
197     $DB->update_record('imscp', $imscp);
199     return true;
202 /**
203  * Delete imscp instance.
204  * @param int $id
205  * @return bool true
206  */
207 function imscp_delete_instance($id) {
208     global $DB;
210     if (!$imscp = $DB->get_record('imscp', array('id' => $id))) {
211         return false;
212     }
214     // Note: all context files are deleted automatically.
216     $DB->delete_records('imscp', array('id' => $imscp->id));
218     return true;
221 /**
222  * Lists all browsable file areas
223  *
224  * @package  mod_imscp
225  * @category files
226  * @param stdClass $course course object
227  * @param stdClass $cm course module object
228  * @param stdClass $context context object
229  * @return array
230  */
231 function imscp_get_file_areas($course, $cm, $context) {
232     $areas = array();
234     $areas['content'] = get_string('areacontent', 'imscp');
235     $areas['backup']  = get_string('areabackup', 'imscp');
237     return $areas;
240 /**
241  * File browsing support for imscp module ontent area.
242  *
243  * @package  mod_imscp
244  * @category files
245  * @param stdClass $browser file browser
246  * @param stdClass $areas file areas
247  * @param stdClass $course course object
248  * @param stdClass $cm course module object
249  * @param stdClass $context context object
250  * @param string $filearea file area
251  * @param int $itemid item ID
252  * @param string $filepath file path
253  * @param string $filename file name
254  * @return file_info instance or null if not found
255  */
256 function imscp_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
257     global $CFG, $DB;
259     // Note: imscp_intro handled in file_browser automatically.
261     if (!has_capability('moodle/course:managefiles', $context)) {
262         // No peeking here for students!
263         return null;
264     }
266     if ($filearea !== 'content' and $filearea !== 'backup') {
267         return null;
268     }
270     require_once("$CFG->dirroot/mod/imscp/locallib.php");
272     if (is_null($itemid)) {
273         return new imscp_file_info($browser, $course, $cm, $context, $areas, $filearea, $itemid);
274     }
276     $fs = get_file_storage();
277     $filepath = is_null($filepath) ? '/' : $filepath;
278     $filename = is_null($filename) ? '.' : $filename;
279     if (!$storedfile = $fs->get_file($context->id, 'mod_imscp', $filearea, $itemid, $filepath, $filename)) {
280         return null;
281     }
283     // Do not allow manual modification of any files!
284     $urlbase = $CFG->wwwroot.'/pluginfile.php';
285     return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false); // No writing here!
288 /**
289  * Serves the imscp files.
290  *
291  * @package  mod_imscp
292  * @category files
293  * @param stdClass $course course object
294  * @param stdClass $cm course module object
295  * @param stdClass $context context object
296  * @param string $filearea file area
297  * @param array $args extra arguments
298  * @param bool $forcedownload whether or not force download
299  * @param array $options additional options affecting the file serving
300  * @return bool false if file not found, does not return if found - justsend the file
301  */
302 function imscp_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
303     global $CFG, $DB;
305     if ($context->contextlevel != CONTEXT_MODULE) {
306         return false;
307     }
309     require_login($course, true, $cm);
311     if ($filearea === 'content') {
312         if (!has_capability('mod/imscp:view', $context)) {
313             return false;
314         }
315         $revision = array_shift($args);
316         $fs = get_file_storage();
317         $relativepath = implode('/', $args);
318         if ($relativepath === 'imsmanifest.xml') {
319             if (!has_capability('moodle/course:managefiles', $context)) {
320                 // No stealing of detailed package info.
321                 return false;
322             }
323         }
324         $fullpath = "/$context->id/mod_imscp/$filearea/$revision/$relativepath";
325         if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
326             return false;
327         }
329         // Finally send the file.
330         send_stored_file($file, null, 0, $forcedownload, $options);
332     } else if ($filearea === 'backup') {
333         if (!has_capability('moodle/course:managefiles', $context)) {
334             // No stealing of package backups.
335             return false;
336         }
337         $revision = array_shift($args);
338         $fs = get_file_storage();
339         $relativepath = implode('/', $args);
340         $fullpath = "/$context->id/mod_imscp/$filearea/$revision/$relativepath";
341         if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
342             return false;
343         }
345         // Finally send the file.
346         send_stored_file($file, null, 0, $forcedownload, $options);
348     } else {
349         return false;
350     }
353 /**
354  * Return a list of page types
355  * @param string $pagetype current page type
356  * @param stdClass $parentcontext Block's parent context
357  * @param stdClass $currentcontext Current context of block
358  * @return array $modulepagetype list
359  */
360 function imscp_page_type_list($pagetype, $parentcontext, $currentcontext) {
361     $modulepagetype = array('mod-imscp-*' => get_string('page-mod-imscp-x', 'imscp'));
362     return $modulepagetype;
365 /**
366  * Export imscp resource contents
367  *
368  * @param  stdClass $cm     Course module object
369  * @param  string $baseurl  Base URL for file downloads
370  * @return array of file content
371  */
372 function imscp_export_contents($cm, $baseurl) {
373     global $DB;
375     $contents = array();
376     $context = context_module::instance($cm->id);
378     $imscp = $DB->get_record('imscp', array('id' => $cm->instance), '*', MUST_EXIST);
380     // We export the IMSCP structure as json encoded string.
381     $structure = array();
382     $structure['type']         = 'content';
383     $structure['filename']     = 'structure';
384     $structure['filepath']     = '/';
385     $structure['filesize']     = 0;
386     $structure['fileurl']      = null;
387     $structure['timecreated']  = $imscp->timemodified;
388     $structure['timemodified'] = $imscp->timemodified;
389     $structure['content']      = json_encode(unserialize($imscp->structure));
390     $structure['sortorder']    = 0;
391     $structure['userid']       = null;
392     $structure['author']       = null;
393     $structure['license']      = null;
394     $contents[] = $structure;
396     // Area files.
397     $fs = get_file_storage();
398     $files = $fs->get_area_files($context->id, 'mod_imscp', 'content', $imscp->revision, 'id ASC', false);
399     foreach ($files as $fileinfo) {
400         $file = array();
401         $file['type']         = 'file';
402         $file['filename']     = $fileinfo->get_filename();
403         $file['filepath']     = $fileinfo->get_filepath();
404         $file['filesize']     = $fileinfo->get_filesize();
405         $file['fileurl']      = moodle_url::make_webservice_pluginfile_url(
406                                     $context->id, 'mod_imscp', 'content', $imscp->revision,
407                                     $fileinfo->get_filepath(), $fileinfo->get_filename())->out(false);
408         $file['timecreated']  = $fileinfo->get_timecreated();
409         $file['timemodified'] = $fileinfo->get_timemodified();
410         $file['sortorder']    = $fileinfo->get_sortorder();
411         $file['userid']       = $fileinfo->get_userid();
412         $file['author']       = $fileinfo->get_author();
413         $file['license']      = $fileinfo->get_license();
414         $contents[] = $file;
415     }
417     return $contents;
420 /**
421  * Mark the activity completed (if required) and trigger the course_module_viewed event.
422  *
423  * @param  stdClass $imscp   imscp object
424  * @param  stdClass $course     course object
425  * @param  stdClass $cm         course module object
426  * @param  stdClass $context    context object
427  * @since Moodle 3.0
428  */
429 function imscp_view($imscp, $course, $cm, $context) {
431     // Trigger course_module_viewed event.
432     $params = array(
433         'context' => $context,
434         'objectid' => $imscp->id
435     );
437     $event = \mod_imscp\event\course_module_viewed::create($params);
438     $event->add_record_snapshot('course_modules', $cm);
439     $event->add_record_snapshot('course', $course);
440     $event->add_record_snapshot('imscp', $imscp);
441     $event->trigger();
443     // Completion.
444     $completion = new completion_info($course);
445     $completion->set_module_viewed($cm);