Development:Using the File API: Difference between revisions
(New page: {{Moodle 2.0}} {{Stub}} Well, in the absence of other information, here is my guess: ==If you already have a file, move it into a file area== For example, if you have just built a file ...) |
Sun Sunner (talk | contribs) (→File areas: Fix a typo) |
||
(42 intermediate revisions by 10 users not shown) | |||
Line 1: | Line 1: | ||
{{Moodle 2.0}} | {{Moodle 2.0}} | ||
The File API is for managing all the files stored by Moodle. If you are interested in how the file API works internally, see [[Development:File API]]. The page is just about what you need to know to use the file API. Related is the [[Development:Repository API]], which lets users get files into Moodle. | |||
If you are looking for an explanation on how to upgrade pre-2.0 code to using the file API, you most likely need to read [[Development:Using_the_File_API_in_Moodle_forms|Using the File API in Moodle forms]]. | |||
== | ==File areas== | ||
For example, if | Files are conceptually stored in '''file areas'''. A file area is uniquely identified by: | ||
* A context id. | |||
* full component name, for example 'course', 'mod_forum', 'mod_glossary'. | |||
* A file area type, for example 'intro' or 'post'. | |||
* A unique itemid. Normally, the itemid relates to something depending on the file area type. For example, for a 'course', 'intro' file area, the itemid is 0. For forum post, it is the post id. | |||
File areas are not listed separately anywhere, they are stored implicitly in the files table. Please note that each subsystem is allowed to access only own file areas, for example only code in /mod/assignment/* may access files with component 'mod_assignment'. | |||
===Naming file areas=== | |||
The names of the file areas are not strictly defined, but it is strongly recommended to use singulars and common names of areas if possible (intro, post, attachment, description, ...). | |||
==Serving files to users== | |||
You must refer to the file with a URL that includes a file-serving script, often pluginfile.php. For example | |||
The general form of the URL is something like | |||
<code php> | |||
$url = $CFG->wwwroot/pluginfile.php/$contextid/$component/$filearea/arbitrary/extra/infomation.ext | |||
</code> | |||
A specific example might be | |||
<code php> | |||
$url = $CFG->wwwroot/pluginfile.php/$forumcontextid/mod_forum/post/$postid/image.jpg | |||
</code> | |||
The file serving script then looks at the context id, and component name, and the file area name, and based on that arranges for the file to be served, following appropriate security checks. | |||
In most cases this is done by calling a callback function in the appropriate plugin, these functions are stored in lib.php files and named component_name_pluginfile() . The arbitrary/extra/infomation.ext is passed to the callback. For example, files in the mod_forum+post file area end up being served by the mod_forum_pluginfile function in mod/forum/lib.php. | |||
You normally use an API function to generate these URL automatically, most often the file_rewrite_pluginfile_urls function. | |||
==Getting files from the user== | |||
* See [[Development:Using_the_File_API_in_Moodle_forms|Using the File API in Moodle forms]] | |||
==Examples== | |||
Please note that in reality developers outside of core will not deal with file api directly in majority of cases, instead use formslib elements which are doing all this automatically. | |||
===Browsing files=== | |||
<code php> | |||
$browser = get_file_browser(); | |||
$context = get_system_context(); | |||
$filearea = null; | |||
$itemid = null; | |||
$filename = null; | |||
if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, '/', $filename)) { | |||
// build a Breadcrumb trail | |||
$level = $fileinfo->get_parent(); | |||
while ($level) { | |||
$params = base64_encode(serialize($level->get_params())); | |||
$path[] = array('name'=>$level->get_visible_name(), 'path'=>$params); | |||
$level = $level->get_parent(); | |||
} | |||
$path = array_reverse($path); | |||
$children = $fileinfo->get_children(); | |||
foreach ($children as $child) { | |||
if ($child->is_directory()) { | |||
echo $child->get_visible_name(); | |||
// display contextid, itemid, component, filepath and filename | |||
var_dump($child->get_params()); | |||
} | |||
} | |||
} | |||
</code> | |||
===Moving files around=== | |||
For example, if you have just built a file at the path | |||
<code php> | |||
$from_zip_file = $CFG->dataroot . '/temp/backup/' . $preferences->backup_unique_code . | |||
'/' . $preferences->backup_name; | |||
</code> | |||
And you want to move it into the course_backup file area, do | And you want to move it into the course_backup file area, do | ||
<code php> | |||
$context = get_context_instance(CONTEXT_COURSE, $preferences->backup_course); | $context = get_context_instance(CONTEXT_COURSE, $preferences->backup_course); | ||
$fs = get_file_storage(); | $fs = get_file_storage(); | ||
$file_record = array('contextid'=>$context->id, 'filearea'=>' | $file_record = array('contextid'=>$context->id, 'component'=>'course', 'filearea'=>'backup', | ||
'itemid'=>0, 'filepath'=>'/', 'filename'=>$preferences->backup_name, | 'itemid'=>0, 'filepath'=>'/', 'filename'=>$preferences->backup_name, | ||
'timecreated'=>time(), 'timemodified'=>time()); | 'timecreated'=>time(), 'timemodified'=>time()); | ||
$fs->create_file_from_pathname($file_record, $from_zip_file); | $fs->create_file_from_pathname($file_record, $from_zip_file); | ||
</code> | |||
===Create a copy of stored file=== | |||
TODO: this example is bogus, nobody should ever write code like this (skodak) | |||
If you need to create a copy of stored file (actually, it add a new record in database): | |||
<code php> | |||
$context = get_context_instance_by_id($contextid); | |||
$file_info = $browser->get_file_info($context, $component, $filearea, $fileitemid, $filepath, $filename); | |||
// copy this file to draft area | |||
$file_info->copy_to_storage($user_context->id, 'user', 'draft', $newitemid, '/', $title); | |||
</code> | |||
The above code is intended for user-interface behaviour which respects the current user's permissions. If you need to write code which copies files at the back-end in areas which are not directly editable by users (for example, when copying a glossary entry to another glossary, and you want to copy its attachments), you do not need to use the user-level $browser at all. All operations are carried out with the file_storage object (usually $fs). The method to use is create_file_from_storedfile. | |||
For example, the glossary stores per-entry files in two areas ('mod_glossary', 'attachment' which stores attachments and 'mod_glossary', 'entry' which stores files directly related to the HTML definition, such as embedded images). The following code copies all the files relating to a specific glossary entry from one such area to a different glossary item in a different glossary. Note that it changes the context ID and item ID of each file to represent the new context (for the different glossary module) and the new glossary entry (a new row was added to glossary_entries). | |||
<code php> | |||
$fs = get_file_storage(); | |||
if ($files = $fs->get_area_files($oldcontextid, 'mod_glossary', 'attachment', $oldentryid)) { | |||
foreach ($files as $file) { | |||
$fs->create_file_from_storedfile(array( | |||
'contextid' => $newcontextid, | |||
'itemid' => $newentryid), $file); | |||
} | |||
} | |||
</code> | |||
=== List area files === | |||
<code php> | |||
$fs = get_file_storage(); | |||
$files = $fs->get_area_files($contextid, 'mod_assignment', 'submission', $submission->id); | |||
foreach ($files as $f) { | |||
// $f is an instance of stored_file | |||
echo $f->get_filename(); | |||
} | |||
</code> | |||
=== Create file === | |||
Here's how to create a file whose contents will be a text string. This is the equivalent of the PHP function <tt>file_put_contents</tt>. | |||
<code php> | |||
$fs = get_file_storage(); | |||
// Prepare file record object | |||
$fileinfo = array( | |||
'contextid' => $context->id, // ID of context | |||
'component' => 'mod_mymodule', // usually = table name | |||
'filearea' => 'myarea', // usually = table name | |||
'itemid' => 0, // usually = ID of row in table | |||
'filepath' => '/', // any path beginning and ending in / | |||
'filename' => 'myfile.txt'); // any filename | |||
// Create file containing text 'hello world' | |||
$fs->create_file_from_string($fileinfo, 'hello world'); | |||
</code> | |||
If you want to create a file in the Moodle file area based on a 'real' file e.g. in a temporary folder, you can use <tt>create_file_from_pathname</tt> instead. | |||
Unlike with ordinary files, this method will not automatically overwrite an existing file. If you wish to overwrite a file, you must first get the file and (if it exists) delete it, and only then create it again. | |||
=== Read file === | |||
This is a way to read a file, equivalent to <tt>file_get_contents</tt>. '''Please note your are allowed to do this ONLY from mod/mymodule/* code, it is not acceptable to do this anywhere else.''' Other code has to use file_browser interface instead. | |||
<code php> | |||
$fs = get_file_storage(); | |||
// Prepare file record object | |||
$fileinfo = array( | |||
'component' => 'mod_mymodule', // usually = table name | |||
'filearea' => 'myarea', // usually = table name | |||
'itemid' => 0, // usually = ID of row in table | |||
'contextid' => $context->id, // ID of context | |||
'filepath' => '/', // any path beginning and ending in / | |||
'filename' => 'myfile.txt'); // any filename | |||
// Get file | |||
$file = $fs->get_file($fileinfo->contextid, $fileinfo->component, $fileinfo->filearea, | |||
$fileinfo->itemid, $fileinfo->filepath, $fileinfo->filename); | |||
// Read contents | |||
if ($file) { | |||
$contents = $file->get_content(); | |||
} else { | |||
// file doesn't exist - do something | |||
} | |||
</code> | |||
If you want to access the file directly on disk, this is not permitted. Instead, you need to make a copy of the file in a temporary area and use that. You can do this with <tt>$file->copy_content_to($pathname)</tt>. | |||
=== Delete file === | |||
<code php> | |||
$fs = get_file_storage(); | |||
// Prepare file record object | |||
$fileinfo = array( | |||
'filearea' => 'mod_mymodule', | |||
'filearea' => 'myarea', // usually = table name | |||
'itemid' => 0, // usually = ID of row in table | |||
'contextid' => $context->id, // ID of context | |||
'filepath' => '/', // any path beginning and ending in / | |||
'filename' => 'myfile.txt'); // any filename | |||
// Get file | |||
$file = $fs->get_file($fileinfo->contextid, $fileinfo->component, $fileinfo->filearea, | |||
$fileinfo->itemid, $fileinfo->filepath, $fileinfo->filename); | |||
// Delete it if it exists | |||
if ($file) { | |||
$file->delete(); | |||
} | |||
</code> | |||
==See also== | ==See also== | ||
* [[Development:File API | * [[Development:File API]] how the File API works internally. | ||
* [[Roadmap|Moodle 2.0 roadmap]] | |||
[[Category:Developer]] | [[Category:Developer]] | ||
[[Category:Files]] |
Latest revision as of 04:35, 5 April 2011
Template:Moodle 2.0 The File API is for managing all the files stored by Moodle. If you are interested in how the file API works internally, see Development:File API. The page is just about what you need to know to use the file API. Related is the Development:Repository API, which lets users get files into Moodle.
If you are looking for an explanation on how to upgrade pre-2.0 code to using the file API, you most likely need to read Using the File API in Moodle forms.
File areas
Files are conceptually stored in file areas. A file area is uniquely identified by:
- A context id.
- full component name, for example 'course', 'mod_forum', 'mod_glossary'.
- A file area type, for example 'intro' or 'post'.
- A unique itemid. Normally, the itemid relates to something depending on the file area type. For example, for a 'course', 'intro' file area, the itemid is 0. For forum post, it is the post id.
File areas are not listed separately anywhere, they are stored implicitly in the files table. Please note that each subsystem is allowed to access only own file areas, for example only code in /mod/assignment/* may access files with component 'mod_assignment'.
Naming file areas
The names of the file areas are not strictly defined, but it is strongly recommended to use singulars and common names of areas if possible (intro, post, attachment, description, ...).
Serving files to users
You must refer to the file with a URL that includes a file-serving script, often pluginfile.php. For example
The general form of the URL is something like
$url = $CFG->wwwroot/pluginfile.php/$contextid/$component/$filearea/arbitrary/extra/infomation.ext
A specific example might be
$url = $CFG->wwwroot/pluginfile.php/$forumcontextid/mod_forum/post/$postid/image.jpg
The file serving script then looks at the context id, and component name, and the file area name, and based on that arranges for the file to be served, following appropriate security checks.
In most cases this is done by calling a callback function in the appropriate plugin, these functions are stored in lib.php files and named component_name_pluginfile() . The arbitrary/extra/infomation.ext is passed to the callback. For example, files in the mod_forum+post file area end up being served by the mod_forum_pluginfile function in mod/forum/lib.php.
You normally use an API function to generate these URL automatically, most often the file_rewrite_pluginfile_urls function.
Getting files from the user
Examples
Please note that in reality developers outside of core will not deal with file api directly in majority of cases, instead use formslib elements which are doing all this automatically.
Browsing files
$browser = get_file_browser();
$context = get_system_context();
$filearea = null;
$itemid = null;
$filename = null;
if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, '/', $filename)) {
// build a Breadcrumb trail
$level = $fileinfo->get_parent();
while ($level) {
$params = base64_encode(serialize($level->get_params()));
$path[] = array('name'=>$level->get_visible_name(), 'path'=>$params);
$level = $level->get_parent();
}
$path = array_reverse($path);
$children = $fileinfo->get_children();
foreach ($children as $child) {
if ($child->is_directory()) {
echo $child->get_visible_name();
// display contextid, itemid, component, filepath and filename
var_dump($child->get_params());
}
}
}
Moving files around
For example, if you have just built a file at the path
$from_zip_file = $CFG->dataroot . '/temp/backup/' . $preferences->backup_unique_code .
'/' . $preferences->backup_name;
And you want to move it into the course_backup file area, do
$context = get_context_instance(CONTEXT_COURSE, $preferences->backup_course);
$fs = get_file_storage();
$file_record = array('contextid'=>$context->id, 'component'=>'course', 'filearea'=>'backup',
'itemid'=>0, 'filepath'=>'/', 'filename'=>$preferences->backup_name,
'timecreated'=>time(), 'timemodified'=>time());
$fs->create_file_from_pathname($file_record, $from_zip_file);
Create a copy of stored file
TODO: this example is bogus, nobody should ever write code like this (skodak)
If you need to create a copy of stored file (actually, it add a new record in database):
$context = get_context_instance_by_id($contextid);
$file_info = $browser->get_file_info($context, $component, $filearea, $fileitemid, $filepath, $filename);
// copy this file to draft area
$file_info->copy_to_storage($user_context->id, 'user', 'draft', $newitemid, '/', $title);
The above code is intended for user-interface behaviour which respects the current user's permissions. If you need to write code which copies files at the back-end in areas which are not directly editable by users (for example, when copying a glossary entry to another glossary, and you want to copy its attachments), you do not need to use the user-level $browser at all. All operations are carried out with the file_storage object (usually $fs). The method to use is create_file_from_storedfile.
For example, the glossary stores per-entry files in two areas ('mod_glossary', 'attachment' which stores attachments and 'mod_glossary', 'entry' which stores files directly related to the HTML definition, such as embedded images). The following code copies all the files relating to a specific glossary entry from one such area to a different glossary item in a different glossary. Note that it changes the context ID and item ID of each file to represent the new context (for the different glossary module) and the new glossary entry (a new row was added to glossary_entries).
$fs = get_file_storage();
if ($files = $fs->get_area_files($oldcontextid, 'mod_glossary', 'attachment', $oldentryid)) {
foreach ($files as $file) {
$fs->create_file_from_storedfile(array(
'contextid' => $newcontextid,
'itemid' => $newentryid), $file);
}
}
List area files
$fs = get_file_storage();
$files = $fs->get_area_files($contextid, 'mod_assignment', 'submission', $submission->id);
foreach ($files as $f) {
// $f is an instance of stored_file
echo $f->get_filename();
}
Create file
Here's how to create a file whose contents will be a text string. This is the equivalent of the PHP function file_put_contents.
$fs = get_file_storage();
// Prepare file record object
$fileinfo = array(
'contextid' => $context->id, // ID of context
'component' => 'mod_mymodule', // usually = table name
'filearea' => 'myarea', // usually = table name
'itemid' => 0, // usually = ID of row in table
'filepath' => '/', // any path beginning and ending in /
'filename' => 'myfile.txt'); // any filename
// Create file containing text 'hello world'
$fs->create_file_from_string($fileinfo, 'hello world');
If you want to create a file in the Moodle file area based on a 'real' file e.g. in a temporary folder, you can use create_file_from_pathname instead.
Unlike with ordinary files, this method will not automatically overwrite an existing file. If you wish to overwrite a file, you must first get the file and (if it exists) delete it, and only then create it again.
Read file
This is a way to read a file, equivalent to file_get_contents. Please note your are allowed to do this ONLY from mod/mymodule/* code, it is not acceptable to do this anywhere else. Other code has to use file_browser interface instead.
$fs = get_file_storage();
// Prepare file record object
$fileinfo = array(
'component' => 'mod_mymodule', // usually = table name
'filearea' => 'myarea', // usually = table name
'itemid' => 0, // usually = ID of row in table
'contextid' => $context->id, // ID of context
'filepath' => '/', // any path beginning and ending in /
'filename' => 'myfile.txt'); // any filename
// Get file
$file = $fs->get_file($fileinfo->contextid, $fileinfo->component, $fileinfo->filearea,
$fileinfo->itemid, $fileinfo->filepath, $fileinfo->filename);
// Read contents
if ($file) {
$contents = $file->get_content();
} else {
// file doesn't exist - do something
}
If you want to access the file directly on disk, this is not permitted. Instead, you need to make a copy of the file in a temporary area and use that. You can do this with $file->copy_content_to($pathname).
Delete file
$fs = get_file_storage();
// Prepare file record object
$fileinfo = array(
'filearea' => 'mod_mymodule',
'filearea' => 'myarea', // usually = table name
'itemid' => 0, // usually = ID of row in table
'contextid' => $context->id, // ID of context
'filepath' => '/', // any path beginning and ending in /
'filename' => 'myfile.txt'); // any filename
// Get file
$file = $fs->get_file($fileinfo->contextid, $fileinfo->component, $fileinfo->filearea,
$fileinfo->itemid, $fileinfo->filepath, $fileinfo->filename);
// Delete it if it exists
if ($file) {
$file->delete();
}
See also
- Development:File API how the File API works internally.
- Moodle 2.0 roadmap