Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Using the File API in Moodle forms

From MoodleDocs

Moodle 2.0


This document shows you exactly how to use Moodle forms to get files from users in a standard and secure way.


Overview

In Moodle 2.0 all files are stored in a central database accessible via the File API, and every file is associated with a "file area" in Moodle, such as a particular module.

A common use case is to provide a form (using Moodle's Forms API) which allows users to upload or import files as attachments or media embedded into HTML.

Normally this works like this:

  1. User starts creation or re-edits an existing item in Moodle (eg forum post, resource, glossary entry etc)
  2. User presses some sort of button to browse for new files to attach or embed
  3. User sees our "Choose file..." dialog, which contains one or more repository instances.
  4. User chooses a file, the Repository API takes care of copying the file into a "draft file area" within Moodle
  5. File appears in the text or as an attachment in the form.
  6. When the user hits save, the File API is invoked to move the file from the draft file area into a permanent file area associated with that data

This document shows you exactly how to use Moodle forms to interact with users in a standard and secure way.

If you just want to write code to manipulate Moodle files internally (without user input) then see Using_the_File_API.


Form elements

In Moodle 2.0 there are three file-related form elements for interacting with users:

  1. filemanager - the way to attach one or more files as a set
  2. editor - the way to specify a textarea with a HTML editor, and all the handling of images and movies within that HTML
  3. filepicker - a way to specify one file for the case when you want to process the file and throw it away

In Moodle 1.9 there were two other types which are now deprecated (they work, but please do not use these anymore)

  1. file - used to just allow a normal file upload from the desktop only.
  2. htmleditor - this old method of embedding a HTML editor in a textarea is not able to support repositories etc.


filepicker

File picker (filepicker) is a direct replacement of the older file formslib element.

It is intended for situations when you want the user upload one file so you can process it and delete it, such as when you are importing data from a CSV file.

Using the filepicker element

$mform->addElement('filepicker', 'userfile', get_string('file'), null, array('maxbytes' => $maxbytes, 'filetypes' => '*'));

Obtain the chosen file

The API for getting file contents is exactly the same as for file element.

$content = $mform->get_file_content('userfile');


filemanager

The File Manager element improves on file picker by allowing you to manage more than one file. It is expected that the files are stored permanently for future use, such as forum and glossary attachments.

Add file manager element

$mform->addElement('filemanager', 'attachments', get_string('attachment', 'moodle'), null,

   array('subdirs' => 0, 'maxbytes' => $maxbytes, 'maxfiles' => 50));

You can specify what file types are accepted by filemanager, all file types are listed at moodle/lib/file/file_types.mm, this is a freemind file, you can edit it freely, the changes will be reflected in moodle.

Load existing files into draft area

if (empty($entry->id)) {

 $entry = new object();
 $entry->id = null;

}

$draftitemid = file_get_submitted_draft_itemid('attachments'); file_prepare_draft_area($draftitemid, $context->id, 'glossary_attachment', $entry->id , false); $entry->attachements = $draftitemid;

$mform->set_data($entry);

Store updated set of files

if ($data = $mform->get_data()) {

   // ... store or update $entry
   file_save_draft_area_files($data->attachements, $context->id, 'glossary_attachment', $entry->id, array('subdirs' => 0, 'maxbytes' => $maxbytes, 'maxfiles' => 50));

}

editor

There are two way for using of editor element in code, the first one is easier but expects some standardized fields. The second method is more low level.

Simple use

  1. name database fields: textfield, textfieldformat (and textfieldtrust if required)
  2. create options array $textfieldoptions = array('trusttext'=>true, 'subdirs'=>true, 'maxfiles'=>$maxfiles, 'maxbytes'=>$maxbytes);
  3. add editor textfield_editor to moodle form, pass options through custom data in form constructor, set $data->id to null if data not exist yet $mform->addElement('editor', 'textfield_editor', get_string('fieldname', 'somemodule'), null, $textfieldoptions);
  4.  prepare data $data = file_prepare_standard_editor($data, 'textfield', $textfieldoptions, $context, 'somemodule_somearea', $data->id);
  5. get submitted data and after inserting/updating of data $data = file_postupdate_standard_editor($data, 'textfield', $textfieldoptions, $context, 'somemodule_somearea', $data->id);

Real world examples are in mod/glossary/edit.php and mod/glossary/comment.php

Low level use

When using editor element you need to preprocess and postprocess the data:

  1. detect if form was already submitted (usually means draft is area already exists) - file_get_submitted_draft_itemid()
  2. prepare draft file area, temporary storage of all files attached to the text - file_prepare_draft_area()
  3. convert encoded relative links to absolute links - file_prepare_draft_area()
  4. create form and set current data
  5. after submission the changed files must be merged back into original area - file_save_draft_area_files()
  6. absolute links have to be replaced by relative links - file_save_draft_area_files()
Replace old htmleditor with editor

The file picker has been integrated with with TinyMCE to make the editor element. This new element should support all types on editors and should be able to switch them on-the-fly. Instances of the old htmleditor element in your forms should be replaced by the new editor element, this may need adding of new format and trusttext columns. For example: $mform->addElement('editor', 'entry', get_string('definition', 'glossary'), null,

       array('maxfiles' => EDITOR_UNLIMITED_FILES, 'filearea' => 'glossary_entry'));

The editor element can take following options: maxfiles, maxbytes, filearea, subdirs and changeformat. Please note that the embedded files is optional feature and is not expected be used everywhere.

Note: the editor element now includes text format option. You should no longer use the separate format element type.

Prepare current data - text and files

if (empty($entry->id)) {

 $entry = new object();
 $entry->id = null;
 $entry->definition = ;
 $entry->format = FORMAT_HTML;

}

$draftid_editor = file_get_submitted_draft_itemid('entry'); $currenttext = file_prepare_draft_area($draftid_editor, $context->id, 'glossary_entry', $entry->id, array('subdirs'=>true), $entry->definition); $entry->entry = array('text'=>$currenttext, 'format'=>$entry->format, 'itemid'=>$draftid_editor);

$mform->set_data($entry);

If there are multiple files, they will share the same itemid.

Obtain text, format and save draft files

To retrieve editor content, you need to use following code:

if ($fromform = $mform->get_data()) {

   // content of editor
   $messagetext = $fromform->entry['text'];
   // format of content
   $messageformat  = $fromform->entry['format'];

}

When a user selects a file using the file picker, the file is initially stored in a draft file area, and a URL is inserted into the HTML in the editor that lets the person editing the content (but no one else) see the file.

When the user submits the form, we then need to save the draft files to the correct place in permanent storage. (Just like you have to call $DB->update_record('tablename', $data); to have the other parts of the form submission stored correctly.)

The save_files_from_draft_area function and replace absolute links with internal relative links do: $messagetext = file_save_draft_area_files($draftid_editor, $context->id, 'glossary_entry', $entry->id, array('subdirs'=>true), $messagetext);

$context->id, 'proper_file_area' and $entry->id
correspond to the contextid, filearea and itemid columns in the files table.
$messagetext
this is the message text. As the files are saved to the real file area, the URLs in this content are rewritten.

All URLs in content that point to files managed to the File API are converted to a form that starts '@@PLUGINFILE@@/' before the content is stored in the database. That is what we mean by rewriting.

File serving

Convert internal relative links to absolute links

Before text content is displayed to the user, any URLs in the '@@PLUGINFILE@@/' form in the content need to be rewritten to the real URL where the user can access the files. $messagetext = file_rewrite_pluginfile_urls($messagetext, 'pluginfile.php',

       "$context->id/proper_file_area/$itemid/");

$messagetext
is the content containing the @@PLUGINFILE@@ URLs from the database.
'pluginfile.php'
there are a number of different scripts that can serve files with different permissions checks. You need to specify which one to use.
"$context->id/proper_file_area/$itemid/"
uniquely identifies the file area, as before.

Implement file serving access control

Attachments and embedded images should have the same access control like the text itself, in majority of cases these files are served using pluginfile.php. Access control is defined in module/lib.php file in function module_pluginfile().


File browsing support

Only owner of each file area is allowed to use low level File API function to access files, other parts of Moodle should use file browsing API.

Activities may specify browsing support in own module/lib.php file by implementing functions module_get_file_areas() and module_get_file_info().

See also

Template:CategoryDeveloper