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/>.
23 * @copyright 2010 Dongsheng Cai
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 use core_external\external_api
;
28 use core_external\external_function_parameters
;
29 use core_external\external_multiple_structure
;
30 use core_external\external_single_structure
;
31 use core_external\external_value
;
33 defined('MOODLE_INTERNAL') ||
die();
35 require_once("{$CFG->libdir}/filelib.php");
38 * Files external functions
42 * @copyright 2011 Jerome Mouneyrac
43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
46 class core_files_external
extends external_api
{
49 * Returns description of get_files parameters
51 * @return external_function_parameters
54 public static function get_files_parameters() {
55 return new external_function_parameters([
56 'contextid' => new external_value(PARAM_INT
, 'context id Set to -1 to use contextlevel and instanceid.'),
57 'component' => new external_value(PARAM_TEXT
, 'component'),
58 'filearea' => new external_value(PARAM_TEXT
, 'file area'),
59 'itemid' => new external_value(PARAM_INT
, 'associated id'),
60 'filepath' => new external_value(PARAM_PATH
, 'file path'),
61 'filename' => new external_value(PARAM_TEXT
, 'file name'),
62 'modified' => new external_value(
64 'timestamp to return files changed after this time.',
68 'contextlevel' => new external_value(PARAM_ALPHA
, 'The context level for the file location.', VALUE_DEFAULT
, null),
69 'instanceid' => new external_value(PARAM_INT
, 'The instance id for where the file is located.', VALUE_DEFAULT
, null),
74 * Return moodle files listing
76 * @param int $contextid context id
77 * @param int $component component
78 * @param int $filearea file area
79 * @param int $itemid item id
80 * @param string $filepath file path
81 * @param string $filename file name
82 * @param int $modified timestamp to return files changed after this time.
83 * @param string $contextlevel The context level for the file location.
84 * @param int $instanceid The instance id for where the file is located.
86 * @since Moodle 2.9 Returns additional fields (timecreated, filesize, author, license)
89 public static function get_files($contextid, $component, $filearea, $itemid, $filepath, $filename, $modified = null,
90 $contextlevel = null, $instanceid = null) {
93 'contextid' => $contextid,
94 'component' => $component,
95 'filearea' => $filearea,
97 'filepath' => $filepath,
98 'filename' => $filename,
99 'modified' => $modified,
100 'contextlevel' => $contextlevel,
101 'instanceid' => $instanceid,
103 $fileinfo = self
::validate_parameters(self
::get_files_parameters(), $parameters);
105 $browser = get_file_browser();
107 // We need to preserve backwards compatibility. Zero will use the system context and minus one will
108 // use the addtional parameters to determine the context.
109 // TODO MDL-40489 get_context_from_params should handle this logic.
110 if ($fileinfo['contextid'] == 0) {
111 $context = context_system
::instance();
113 if ($fileinfo['contextid'] == -1) {
114 $fileinfo['contextid'] = null;
116 $context = self
::get_context_from_params($fileinfo);
118 self
::validate_context($context);
120 if (empty($fileinfo['component'])) {
121 $fileinfo['component'] = null;
123 if (empty($fileinfo['filearea'])) {
124 $fileinfo['filearea'] = null;
126 if (empty($fileinfo['filename'])) {
127 $fileinfo['filename'] = null;
129 if (empty($fileinfo['filepath'])) {
130 $fileinfo['filepath'] = null;
134 $return['parents'] = array();
135 $return['files'] = array();
138 if ($file = $browser->get_file_info(
139 $context, $fileinfo['component'], $fileinfo['filearea'], $fileinfo['itemid'],
140 $fileinfo['filepath'], $fileinfo['filename'])) {
141 $level = $file->get_parent();
143 $params = $level->get_params();
144 $params['filename'] = $level->get_visible_name();
145 array_unshift($return['parents'], $params);
146 $level = $level->get_parent();
148 $children = $file->get_children();
149 foreach ($children as $child) {
151 $params = $child->get_params();
152 $timemodified = $child->get_timemodified();
153 $timecreated = $child->get_timecreated();
155 if ($child->is_directory()) {
156 if ((is_null($modified)) or ($modified < $timemodified)) {
158 'contextid' => $params['contextid'],
159 'component' => $params['component'],
160 'filearea' => $params['filearea'],
161 'itemid' => $params['itemid'],
162 'filepath' => $params['filepath'],
163 'filename' => $child->get_visible_name(),
166 'timemodified' => $timemodified,
167 'timecreated' => $timecreated,
175 if ((is_null($modified)) or ($modified < $timemodified)) {
177 'contextid' => $params['contextid'],
178 'component' => $params['component'],
179 'filearea' => $params['filearea'],
180 'itemid' => $params['itemid'],
181 'filepath' => $params['filepath'],
182 'filename' => $child->get_visible_name(),
183 'url' => $child->get_url(),
185 'timemodified' => $timemodified,
186 'timecreated' => $timecreated,
187 'filesize' => $child->get_filesize(),
188 'author' => $child->get_author(),
189 'license' => $child->get_license(),
196 $return['files'] = $list;
201 * Returns description of get_files returns
203 * @return external_single_structure
204 * @since Moodle 2.9 Returns additional fields for files (timecreated, filesize, author, license)
207 public static function get_files_returns() {
208 return new external_single_structure([
209 'parents' => new external_multiple_structure(
210 new external_single_structure([
211 'contextid' => new external_value(PARAM_INT
, ''),
212 'component' => new external_value(PARAM_COMPONENT
, ''),
213 'filearea' => new external_value(PARAM_AREA
, ''),
214 'itemid' => new external_value(PARAM_INT
, ''),
215 'filepath' => new external_value(PARAM_TEXT
, ''),
216 'filename' => new external_value(PARAM_TEXT
, ''),
219 'files' => new external_multiple_structure(
220 new external_single_structure([
221 'contextid' => new external_value(PARAM_INT
, ''),
222 'component' => new external_value(PARAM_COMPONENT
, ''),
223 'filearea' => new external_value(PARAM_AREA
, ''),
224 'itemid' => new external_value(PARAM_INT
, ''),
225 'filepath' => new external_value(PARAM_TEXT
, ''),
226 'filename' => new external_value(PARAM_TEXT
, ''),
227 'isdir' => new external_value(PARAM_BOOL
, ''),
228 'url' => new external_value(PARAM_TEXT
, ''),
229 'timemodified' => new external_value(PARAM_INT
, ''),
230 'timecreated' => new external_value(PARAM_INT
, 'Time created', VALUE_OPTIONAL
),
231 'filesize' => new external_value(PARAM_INT
, 'File size', VALUE_OPTIONAL
),
232 'author' => new external_value(PARAM_TEXT
, 'File owner', VALUE_OPTIONAL
),
233 'license' => new external_value(PARAM_TEXT
, 'File license', VALUE_OPTIONAL
),
240 * Returns description of upload parameters
242 * @return external_function_parameters
245 public static function upload_parameters() {
246 return new external_function_parameters([
247 'contextid' => new external_value(PARAM_INT
, 'context id', VALUE_DEFAULT
, null),
248 'component' => new external_value(PARAM_COMPONENT
, 'component'),
249 'filearea' => new external_value(PARAM_AREA
, 'file area'),
250 'itemid' => new external_value(PARAM_INT
, 'associated id'),
251 'filepath' => new external_value(PARAM_PATH
, 'file path'),
252 'filename' => new external_value(PARAM_FILE
, 'file name'),
253 'filecontent' => new external_value(PARAM_TEXT
, 'file content'),
254 'contextlevel' => new external_value(
256 'The context level to put the file in,
257 (block, course, coursecat, system, user, module)',
261 'instanceid' => new external_value(PARAM_INT
, 'The Instance id of item associated
262 with the context level', VALUE_DEFAULT
, null),
267 * Uploading a file to moodle
269 * @param int $contextid context id
270 * @param string $component component
271 * @param string $filearea file area
272 * @param int $itemid item id
273 * @param string $filepath file path
274 * @param string $filename file name
275 * @param string $filecontent file content
276 * @param string $contextlevel Context level (block, course, coursecat, system, user or module)
277 * @param int $instanceid Instance id of the item associated with the context level
281 public static function upload(
294 $fileinfo = self
::validate_parameters(self
::upload_parameters(), [
295 'contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid,
296 'filepath' => $filepath, 'filename' => $filename, 'filecontent' => $filecontent, 'contextlevel' => $contextlevel,
297 'instanceid' => $instanceid,
300 if (!isset($fileinfo['filecontent'])) {
301 throw new moodle_exception('nofile');
304 $dir = make_temp_directory('wsupload');
306 if (empty($fileinfo['filename'])) {
307 $filename = uniqid('wsupload', true).'_'.time().'.tmp';
309 $filename = $fileinfo['filename'];
312 if (file_exists($dir.$filename)) {
313 $savedfilepath = $dir.uniqid('m').$filename;
315 $savedfilepath = $dir.$filename;
318 file_put_contents($savedfilepath, base64_decode($fileinfo['filecontent']));
319 @chmod
($savedfilepath, $CFG->filepermissions
);
320 unset($fileinfo['filecontent']);
322 if (!empty($fileinfo['filepath'])) {
323 $filepath = $fileinfo['filepath'];
328 // Only allow uploads to draft area.
329 if (!($fileinfo['component'] == 'user' and $fileinfo['filearea'] == 'draft')) {
330 throw new coding_exception('File can be uploaded to user draft area only');
333 $filearea = $fileinfo['filearea'];
337 if (isset($fileinfo['itemid'])) {
338 $itemid = $fileinfo['itemid'];
340 if ($filearea == 'draft' && $itemid <= 0) {
341 // Generate a draft area for the files.
342 $itemid = file_get_unused_draft_itemid();
343 } else if ($filearea == 'private') {
344 // TODO MDL-31116 in user private area, itemid is always 0.
348 // We need to preserve backword compatibility. Context id is no more a required.
349 if (empty($fileinfo['contextid'])) {
350 unset($fileinfo['contextid']);
353 // Get and validate context.
354 $context = self
::get_context_from_params($fileinfo);
355 self
::validate_context($context);
356 if (($fileinfo['component'] == 'user' and $fileinfo['filearea'] == 'private')) {
357 throw new moodle_exception('privatefilesupload');
360 $browser = get_file_browser();
362 // Check existing file.
363 if ($file = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) {
364 throw new moodle_exception('fileexist');
367 // Move file to filepool.
368 if ($dir = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, '.')) {
369 $info = $dir->create_file_from_pathname($filename, $savedfilepath);
370 $params = $info->get_params();
371 unlink($savedfilepath);
373 'contextid' => $params['contextid'],
374 'component' => $params['component'],
375 'filearea' => $params['filearea'],
376 'itemid' => $params['itemid'],
377 'filepath' => $params['filepath'],
378 'filename' => $params['filename'],
379 'url' => $info->get_url(),
382 throw new moodle_exception('nofile');
387 * Returns description of upload returns
389 * @return external_single_structure
392 public static function upload_returns() {
393 return new external_single_structure([
394 'contextid' => new external_value(PARAM_INT
, ''),
395 'component' => new external_value(PARAM_COMPONENT
, ''),
396 'filearea' => new external_value(PARAM_AREA
, ''),
397 'itemid' => new external_value(PARAM_INT
, ''),
398 'filepath' => new external_value(PARAM_TEXT
, ''),
399 'filename' => new external_value(PARAM_FILE
, ''),
400 'url' => new external_value(PARAM_TEXT
, ''),