<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://docs.moodle.org/dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Demsley</id>
	<title>MoodleDocs - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://docs.moodle.org/dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Demsley"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/Special:Contributions/Demsley"/>
	<updated>2026-07-20T07:05:35Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Moodle_forms_library&amp;diff=58970</id>
		<title>Moodle forms library</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Moodle_forms_library&amp;diff=58970"/>
		<updated>2021-06-11T15:01:10Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* display */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{obsolete}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;This was a proposal for discussion that was not used.&lt;br /&gt;
&lt;br /&gt;
==Former discussions about documentation of the forms library that is used in Moodle==&lt;br /&gt;
&lt;br /&gt;
The [[lib/formslib.php]] page is about the documentation of the forms library that was implemented as a result of old discussions  and is archived here.&lt;br /&gt;
&lt;br /&gt;
==And now, on with the history ...==&lt;br /&gt;
&lt;br /&gt;
The OU has developed quite a nice in-house library to simplify creating Moodle editing forms. We would like to contribute it back to the community, and then as part of the accessibility work we are comissioning, get all Moodle forms modified to use it. However, before we can contribute it back, we need to get consensus withing the community that it is right for Moodle, and we need to tidy up the code a bit.&lt;br /&gt;
&lt;br /&gt;
This document outlines how the library should work after cleaning up, so the community can see whether they like it, and OU developers know what is involved in the cleanup.&lt;br /&gt;
&lt;br /&gt;
==What the system will be capable of==&lt;br /&gt;
&lt;br /&gt;
The library allows developers to create editing forms by creating a high-level representation of the form as a data-structure in memory, configuring all necessary options, and setting initial values of fields, and then the library generates the actual HTML of the form to include in the page.&lt;br /&gt;
&lt;br /&gt;
We are not planning to change significantly the way that Moodle editing forms look and function. However, by putting all the HTML (and JavaScript, CSS, ...) generation in one place, we make it much easier to make systematic improvements to accessibility, usability, client-side validation, etc. in future. Indeed, the OU&#039;s code already generates HTML that is a bit more accessible that most Moodle forms.&lt;br /&gt;
&lt;br /&gt;
By allowing developers to think at a higher level, we make their life easier, in the same way that datalib saves them from worrying about the details of SQL most of the time.&lt;br /&gt;
&lt;br /&gt;
The general way the library will work is that it will assume sensible defaults for everything to reduce the amount that has to be typed. So for a lot of input fields, you will only have to specify&lt;br /&gt;
&lt;br /&gt;
# the field type (e.g. text, think &amp;lt;input type=&amp;quot;&amp;quot; ... /&amp;gt;, However the hope would be to move towards higher-level types, such as integer, path, url, as in require_param, etc.)&lt;br /&gt;
# the field name (think &amp;lt;input name=&amp;quot;&amp;quot; ... /&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Everything else that is needed will be derived from these using sensible defaults. We need label text? We&#039;ll look up the field name in the lang file (each form will specify which lang file to use). We need a help link? Well, use /lang/en_utf8/help$langfile/$name.html. We need a field size? Each type will have a sensible default.&lt;br /&gt;
&lt;br /&gt;
However, if you want to override any of these defaults, you can by setting that option explicitly. Because there will be lots of options, and normally you will only need to change a few, the library will use a named attribute style, so example code would look like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$field = new text_field(&#039;fieldname&#039;);&lt;br /&gt;
$field-&amp;gt;set_set(&#039;size&#039;, 30);&lt;br /&gt;
$field-&amp;gt;set_set(&#039;label&#039;, &#039;lablestring&#039;);&lt;br /&gt;
&lt;br /&gt;
// or &lt;br /&gt;
&lt;br /&gt;
$field = new text_field(&#039;fieldname&#039;);&lt;br /&gt;
$field-&amp;gt;set_set(array(&#039;size&#039; =&amp;gt; 30, &#039;label&#039; =&amp;gt; &#039;lablestring&#039;));&lt;br /&gt;
&lt;br /&gt;
// or &lt;br /&gt;
&lt;br /&gt;
$field = new text_field(&#039;fieldname&#039;, array(&lt;br /&gt;
        &#039;size&#039; =&amp;gt; 30, &lt;br /&gt;
        &#039;label&#039; =&amp;gt; &#039;lablestring&#039;&lt;br /&gt;
));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this example, &#039;lablestring&#039; would again get looked up in the lang file automatically.&lt;br /&gt;
&lt;br /&gt;
For this situation, where there are lots of options available, but most people only need to change a few, I think this style of API works better than PHP function calls with lots of optional parameters. The options available for each field type will be documented in the comment where that class is defined.&lt;br /&gt;
&lt;br /&gt;
The library is designed to make it easy to add new field types, or new options to existing field types. Indeed, that is how the library has evolved so far: a bit at a time as needed. &lt;br /&gt;
&lt;br /&gt;
New field types are just new PHP classes, and they are likely to be subclasses of a base class that only change a few things. They don&#039;t even have to be defined in the central form library. If you need an specialist field type only within one (e.g. contrib) module, you can create a new field-type subclass in that module code and use it in that modules forms without having to touch core code. And if we later want to move that field type into core, it is trivial to move the class definition.&lt;br /&gt;
&lt;br /&gt;
Since we will have to tidy up the code anyway, all the function/class/method/option names in the API are potentially changable.&lt;br /&gt;
&lt;br /&gt;
==PHP API==&lt;br /&gt;
&lt;br /&gt;
Each thing in the API will be a PHP class, these basically fall into three categories: the whole form, groupings of form fields, and individual field types. Note that the groupings are currently only used for functional reasons, like showing or hiding groups of elements. Logical groupings, that would correspond to &amp;lt;fieldset&amp;gt; tags for accessibility, are not included yet, but could be added.&lt;br /&gt;
&lt;br /&gt;
I am just going to list all the possible method calls. I will assume that it is clear what they do from their names (and I am running out of time).&lt;br /&gt;
&lt;br /&gt;
===The whole form===&lt;br /&gt;
&lt;br /&gt;
There is a class to represent a form, and most of the time you will be able to do everything you want to do by calling methods on this class, and you don&#039;t have to worry about the rest of the API.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mf = new moodle_form($langfile);&lt;br /&gt;
&lt;br /&gt;
$field = &amp;amp;$mf-&amp;gt;add($fieldtype, $fieldname, $fieldoptions, $insertbefore);&lt;br /&gt;
$field = &amp;amp;$mf-&amp;gt;add_item($fieldobject, $insertbefore); // $insertbefore is optional. By default things are inserted at the end of the form.&lt;br /&gt;
$field = &amp;amp;$mf-&amp;gt;remove($fieldname);&lt;br /&gt;
$field = &amp;amp;$mf-&amp;gt;get($fieldname);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These let you add or remove things from the form. The field you have just done stuff to is returned, but most of the time you will not bother to do anything with the return value. These method work with groups, etc. not just fields.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mf-&amp;gt;set_action($url);&lt;br /&gt;
$mf-&amp;gt;set_init_html_editor(); // There was a reason why the library can&#039;t guess this from the list of fields.&lt;br /&gt;
$mf-&amp;gt;set_submit_caption($langstringid);&lt;br /&gt;
$mf-&amp;gt;set_hidden($fieldname, $value);&lt;br /&gt;
$mf-&amp;gt;set_value($fieldname, $value); // The initial value for this field. (or use the $form attribute on show()).  &lt;br /&gt;
$mf-&amp;gt;set_error($fieldname, $message); // Error message to be displayed in red next to this field if you are doing server-side validation.&lt;br /&gt;
&lt;br /&gt;
$mf-&amp;gt;show($action, $form);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Show the form. $form is an object holding the default value of each field that has not already been set using set_value().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mf-&amp;gt;add_xhtml($htmlphpcode, $insertbefore);&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param($name, $value);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can insert arbitrary bits of HTML or PHP code into the form using this. When outputting the form, the library does &#039;&#039;&#039;eval(&#039;?&amp;gt;&#039;.$htmlphpcode.&#039;&amp;lt;?php &#039;);&#039;&#039;&#039; in a place where $form is in scope, and the variables passed in via set_xhtml_param are available from an array as $xmlform[$name].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$new_mf = moodle_form::loadfile($xmlfilename);&lt;br /&gt;
$new_mf = moodle_form::loadstring($xmlstring);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create a whole form using the XML syntax mentioned below.&lt;br /&gt;
&lt;br /&gt;
===Fields===&lt;br /&gt;
&lt;br /&gt;
The library also has classes representing the different field types, which you can use if you want.&lt;br /&gt;
&lt;br /&gt;
 $field = new xxxx_field($name, $options_array); // where xxxx is one of the field types below.&lt;br /&gt;
 $field-&amp;gt;set($optionname, $optionvalue);&lt;br /&gt;
 $field-&amp;gt;set($optionarray); // Array of $optionname =&amp;gt; $optionvalue pairs.&lt;br /&gt;
 $optionvalue = $field-&amp;gt;get($optionname);&lt;br /&gt;
&lt;br /&gt;
 $field-&amp;gt;set_value($value); // Sets the initial value of this field.&lt;br /&gt;
&lt;br /&gt;
 $field-&amp;gt;output($form); // Print this form element. &lt;br /&gt;
&lt;br /&gt;
Normally you won&#039;t call output() directly. You will call $mf-&amp;gt;output, which will call $field-&amp;gt;output on each field for you, but you can use this yourself if you just want to print one form control anywhere you like, not as part of a form.&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
&lt;br /&gt;
Groups (which will probably get renamed to conditionalshow, but I don&#039;t want to change all the examples just now) allow you to show or hide a collection of fields, depending on the setting of another field.&lt;br /&gt;
&lt;br /&gt;
groups have basically the same methods as fields, but $name is optional. $name has no function, unless you need to identify the group later for a call the $mf-&amp;gt;get($groupname);&lt;br /&gt;
&lt;br /&gt;
There are a few extra methods they have over fields:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$field = &amp;amp;$group-&amp;gt;add($fieldtype, $fieldname, $fieldoptions, $insertbefore);&lt;br /&gt;
$field = &amp;amp;$group-&amp;gt;add_item($fieldobject, $insertbefore);&lt;br /&gt;
$field = &amp;amp;$group-&amp;gt;remove($fieldname);&lt;br /&gt;
$field = &amp;amp;$group-&amp;gt;get($fieldname);&lt;br /&gt;
&lt;br /&gt;
$group-&amp;gt;set_condition($conditionfield, $conditionvalue);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The elements within the group will only be visible when the field $conditionfield elsewhere in the form has value $conditionvalue. This works best when $conditionfield is a dropdown.&lt;br /&gt;
&lt;br /&gt;
Do we also want a conditionalenable group that enables/disables a bunch of fields, rather than showing or hiding them?&lt;br /&gt;
&lt;br /&gt;
===Multiples===&lt;br /&gt;
&lt;br /&gt;
This lets you do things like enter multiple usernames, as in the &#039;working example&#039; below. You would also use it on the editing form for multiple choice questions, to enter any number of answers with matching grades.&lt;br /&gt;
&lt;br /&gt;
Again, they support the same methods as fields, and also:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$field = &amp;amp;$multiple-&amp;gt;add($fieldtype, $fieldname, $fieldoptions, $insertbefore);&lt;br /&gt;
$field = &amp;amp;$multiple-&amp;gt;add_item($fieldobject, $insertbefore);&lt;br /&gt;
$field = &amp;amp;$multiple-&amp;gt;remove($fieldname);&lt;br /&gt;
$field = &amp;amp;$multiple-&amp;gt;get($fieldname);&lt;br /&gt;
&lt;br /&gt;
$multiple-&amp;gt;set_max($number);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For accessibility reasons, multiples work by printing a certain number of copies into the HTML, and these are then shown or hidden by the JavaScript. This is for accessibility reasons, and so the forms work without JavaScript. max is the number of copies that are printed. It defaults to 10.&lt;br /&gt;
&lt;br /&gt;
==Field types initially supported==&lt;br /&gt;
&lt;br /&gt;
All field will support the options:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;lable&#039;&#039;&#039; the field lable. This is a string that is looked up in the language file. Or, if the string starts with an &#039;=&#039; character, then this is trimmed off, and the rest of the string is used literally. Defaults to name.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;help&#039;&#039;&#039; the name of the help file to link to. Defaults to name. Setting to &#039;&#039; removes the help icon.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;helpalt&#039;&#039;&#039; the langstring to use for the tooltip of the help icon.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;optional&#039;&#039;&#039; if set to &#039;yes&#039;, then adds a checkbox before the field to enable or diable it. The checkbox&#039;s name is name is constructed by taking the field name and appending &#039;enable&#039;. If you want another name, set this option to the checkbox name, instead of &#039;yes&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;needcapability&#039;&#039;&#039; this is mainly aimed at 1.7 roles and permissions. Will hide this field unless the user has the named capability. At first, this will only recognise the values admin, teacher, student, noneditingteacher, guest, and translate these into the obvious isadmin() type calls.&lt;br /&gt;
&lt;br /&gt;
More options will probably get added in future.&lt;br /&gt;
&lt;br /&gt;
===display===&lt;br /&gt;
&lt;br /&gt;
Just display a value with a label, the value can&#039;t be edited.&lt;br /&gt;
&lt;br /&gt;
===text===&lt;br /&gt;
&lt;br /&gt;
Text input box.&lt;br /&gt;
&lt;br /&gt;
Supports the additional options:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;required&#039;&#039;&#039; a regexp that is used for client-side validation against that regexp.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;size&#039;&#039;&#039; as in &amp;lt;input size=&amp;quot;&amp;quot; ... /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===file===&lt;br /&gt;
&lt;br /&gt;
File upload box.&lt;br /&gt;
&lt;br /&gt;
===date===&lt;br /&gt;
&lt;br /&gt;
Date, like quiz open and close dates.&lt;br /&gt;
&lt;br /&gt;
===html===&lt;br /&gt;
&lt;br /&gt;
The standard HTML editor, or just a text area, depending on the settings. (This calls print_textarea).&lt;br /&gt;
&lt;br /&gt;
===dropdown===&lt;br /&gt;
&lt;br /&gt;
A dropdown menu. This field has the additional methods:&lt;br /&gt;
&lt;br /&gt;
 $dropdown-&amp;gt;add_option($value, $label);&lt;br /&gt;
 $dropdown-&amp;gt;remove_option($value);&lt;br /&gt;
&lt;br /&gt;
$label is optional. By default: if $value is an integer, use that integer as the label, otherwise look $value up in the langfile.&lt;br /&gt;
&lt;br /&gt;
Dropdown supports the additional option:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;default&#039;&#039;&#039; which option to select by default.&lt;br /&gt;
&lt;br /&gt;
===radio===&lt;br /&gt;
&lt;br /&gt;
A set of linked radio buttons, which are defined in the same way as dropdown menu options.&lt;br /&gt;
&lt;br /&gt;
===multiselect===&lt;br /&gt;
&lt;br /&gt;
A list box where you can select multiple options, which are defined in the same way as dropdown menu options.&lt;br /&gt;
&lt;br /&gt;
Actually, I think we should change the implementation of this to use a set of checkboxes when there are fewer than about a dozen options, and automatically switch to useing a list box when there are more than that. And maybe add an optional parameter to force the listbox/checkbox decision one way or the other.&lt;br /&gt;
&lt;br /&gt;
===yesno===&lt;br /&gt;
&lt;br /&gt;
A dropdown menu with just the two options &#039;yes&#039; and &#039;no&#039;.&lt;br /&gt;
&lt;br /&gt;
===user===&lt;br /&gt;
&lt;br /&gt;
A flashy text box where you can enter user&#039;s names, and it does AJAX stuff to help you auto-complete them.&lt;br /&gt;
&lt;br /&gt;
===visible===&lt;br /&gt;
&lt;br /&gt;
This generates the standard &amp;quot;Visible to students&amp;quot; field that appears on add/update module forms.&lt;br /&gt;
&lt;br /&gt;
===groupmode===&lt;br /&gt;
&lt;br /&gt;
This generates the standard &amp;quot;Group mode&amp;quot; field that appears on add/update module forms.&lt;br /&gt;
&lt;br /&gt;
==XML form definition format==&lt;br /&gt;
&lt;br /&gt;
This provides the quickest way to create most forms. It should be clear how this translates into the PHP API calls defined above. XML elements $mf-&amp;gt;add() calls. XML attributes correspond either to required information, or to set_opt calls. Form filds, like dropdowns, that need extra information get it from child elements. See the examples below for what it looks like.&lt;br /&gt;
&lt;br /&gt;
==A working example==&lt;br /&gt;
&lt;br /&gt;
Here is an example of a form produced with the current (un-cleaned-up) version of the OU&#039;s library. The form looks like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:Formproposal_Add_newsfeed_form.png]]&lt;br /&gt;
&lt;br /&gt;
There is some OU-specific stuff here, like presentation and authids, and the fact that we reveal $user-&amp;gt;username to teachers. I am assuming you can filter that out.&lt;br /&gt;
&lt;br /&gt;
More interestingly, notice that&lt;br /&gt;
&lt;br /&gt;
* The required field Name has not been filled it, so its label is red, and the create button is disabled. Below, you will see that anything typed into this field will actually be validated against a regular expression.&lt;br /&gt;
* The user field type does cool AJAX to save you typing the whole name.&lt;br /&gt;
* After you have added one user as a poster, a second box appeared where we could type a second user name. And when we finish typing here, a third box will appear.&lt;br /&gt;
&lt;br /&gt;
The XML defining the form looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;editform langfile=&#039;block_newsfeed&#039;&amp;gt;&lt;br /&gt;
     &amp;lt;text name=&#039;location&#039;/&amp;gt;&lt;br /&gt;
     &amp;lt;text name=&#039;name&#039; required=&#039;\S&#039;/&amp;gt;&lt;br /&gt;
     &amp;lt;text name=&#039;pres&#039; required=&#039;^([0-9]{2}[a-zA-Z]?)?$&#039;/&amp;gt;&lt;br /&gt;
     &amp;lt;html name=&#039;summary&#039;/&amp;gt;&lt;br /&gt;
     &amp;lt;dropdown name=&#039;type&#039;&amp;gt;&lt;br /&gt;
       &amp;lt;option value=&#039;internal&#039;&amp;gt;type_internal&amp;lt;/option&amp;gt;&lt;br /&gt;
       &amp;lt;option value=&#039;external&#039;&amp;gt;type_external&amp;lt;/option&amp;gt;&lt;br /&gt;
     &amp;lt;/dropdown &amp;gt;&lt;br /&gt;
     &amp;lt;group requiredname=&amp;quot;type&amp;quot; requiredvalue=&amp;quot;external&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;item type=&#039;text&#039; name=&#039;url&#039;/&amp;gt;&lt;br /&gt;
     &amp;lt;/group&amp;gt;&lt;br /&gt;
     &amp;lt;group requiredname=&amp;quot;type&amp;quot; requiredvalue=&amp;quot;internal&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;date name=&#039;startdate&#039;/&amp;gt;&lt;br /&gt;
         &amp;lt;dropdown name=&#039;public&#039;&amp;gt;&lt;br /&gt;
           &amp;lt;option value=&#039;1&#039;&amp;gt;access_public&amp;lt;/option&amp;gt;&lt;br /&gt;
           &amp;lt;option value=&#039;0&#039;&amp;gt;access_private&amp;lt;/option&amp;gt;&lt;br /&gt;
         &amp;lt;/dropdown &amp;gt;&lt;br /&gt;
         &amp;lt;text name=&#039;defaultauthid&#039;/&amp;gt;&lt;br /&gt;
         &amp;lt;multiple&amp;gt;&lt;br /&gt;
             &amp;lt;text name=&#039;optionalauthids&#039; required=&#039;^([A-Z0-9]+)?$&#039;/&amp;gt;&lt;br /&gt;
         &amp;lt;/multiple&amp;gt;&lt;br /&gt;
         &amp;lt;multiple&amp;gt;&lt;br /&gt;
             &amp;lt;user name=&#039;posters&#039;/&amp;gt;&lt;br /&gt;
         &amp;lt;/multiple&amp;gt;&lt;br /&gt;
         &amp;lt;multiple&amp;gt;&lt;br /&gt;
             &amp;lt;user name=&#039;approvers&#039;/&amp;gt;&lt;br /&gt;
         &amp;lt;/multiple&amp;gt;&lt;br /&gt;
     &amp;lt;/group&amp;gt;          &lt;br /&gt;
&amp;lt;/editform&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PHP code that creates the form definition ($xf), and the object with all the current values ($form) and sets all the default values looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$xf=xml_form::load_file(&#039;editfeed.xml&#039;);&lt;br /&gt;
$xf-&amp;gt;set_init_html_editor(true);&lt;br /&gt;
$form=new stdClass;&lt;br /&gt;
$newsfeedid=optional_param(&#039;newsfeedid&#039;,0,PARAM_INT);&lt;br /&gt;
if($newsfeedid) {&lt;br /&gt;
    $nf=feed_system::$inst-&amp;gt;get_feed($newsfeedid);&lt;br /&gt;
    $form-&amp;gt;location=$nf-&amp;gt;get_folder()-&amp;gt;get_path();&lt;br /&gt;
    $xf-&amp;gt;set_hidden(&#039;newsfeedid&#039;,$newsfeedid);&lt;br /&gt;
    &lt;br /&gt;
    $form-&amp;gt;name=$nf-&amp;gt;get_name();&lt;br /&gt;
    $form-&amp;gt;pres=$nf-&amp;gt;get_pres();&lt;br /&gt;
    if($form-&amp;gt;pres==null) {&lt;br /&gt;
        $form-&amp;gt;pres=&#039;&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    $form-&amp;gt;summary=$nf-&amp;gt;get_summary();&lt;br /&gt;
    if(is_a($nf,&#039;external_news_feed&#039;)) {&lt;br /&gt;
        $form-&amp;gt;type=&#039;external&#039;;&lt;br /&gt;
        $form-&amp;gt;url=$form-&amp;gt;get_url();&lt;br /&gt;
    } else {&lt;br /&gt;
        $form-&amp;gt;type=&#039;internal&#039;;&lt;br /&gt;
        $form-&amp;gt;startdate=$nf-&amp;gt;get_start_date();&lt;br /&gt;
        $form-&amp;gt;public=$nf-&amp;gt;is_public();&lt;br /&gt;
        $form-&amp;gt;defaultauthid=$nf-&amp;gt;get_default_authid();&lt;br /&gt;
        xml_form::set_multiple($form,&#039;optionalauthids&#039;,$nf-&amp;gt;get_optional_authids());&lt;br /&gt;
        xml_form::set_multiple($form,&#039;posters&#039;,$nf-&amp;gt;get_poster_usernames());&lt;br /&gt;
        xml_form::set_multiple($form,&#039;approvers&#039;,$nf-&amp;gt;get_approver_usernames());&lt;br /&gt;
        $form-&amp;gt;newsfeedid=$newsfeedid;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
} else {&lt;br /&gt;
    $folderid=required_param(&#039;folderid&#039;,PARAM_INT);&lt;br /&gt;
    $lf=feed_system::$inst-&amp;gt;get_location_folder($folderid,null);&lt;br /&gt;
    $nf=null;&lt;br /&gt;
    $xf-&amp;gt;set_submit_caption(get_string(&#039;create&#039;));&lt;br /&gt;
    $form-&amp;gt;location=$lf-&amp;gt;get_path();&lt;br /&gt;
    $xf-&amp;gt;set_hidden(&#039;newsfeedid&#039;,0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... print_header and other boring bits.&lt;br /&gt;
&lt;br /&gt;
print_simple_box_start(&#039;center&#039;);&lt;br /&gt;
$xf-&amp;gt;show(basename(__FILE__), $form);&lt;br /&gt;
print_simple_box_end();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Examples of converting existing Moodle forms==&lt;br /&gt;
&lt;br /&gt;
These are four example forms that Marting Dougiamas asked to see how we would handle.&lt;br /&gt;
&lt;br /&gt;
===Add resource -&amp;gt; Link to a file or web site===&lt;br /&gt;
&lt;br /&gt;
[[Image:Formproposal_Add link resource.png]]&lt;br /&gt;
&lt;br /&gt;
I have taken the liberty of changing the way the window options work on this form. Instead of having a show/hide window settings button, and when that is turned on showing all the options for both same window and new window, I have changed it to be a same window/popup window dropdown, and depending on the setting there, showing or hiding the particular set of options. It would also be possible to use the library to generate the existing form.&lt;br /&gt;
&lt;br /&gt;
This form contains some very specific bits, namely the location field and the parameters sections. For now I have kept the existing code to generate these bits. If fields like the location field were used in other places, we could add a url field type. I don&#039;t see any future in generalising the parameters bit, though the code to generate it could be cleaned up. I just copied and pasted the existing code, and made the minimal changes.&lt;br /&gt;
&lt;br /&gt;
I&#039;ve used a multiselect for the window options. That will require a small change to the response processing code. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mf = moodle_form::loadstring(&#039;&lt;br /&gt;
&amp;lt;editform langfile=&amp;quot;resource&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;text name=&amp;quot;name&amp;quot; help=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;htmlarea name=&amp;quot;summary&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;xhtml label=&amp;quot;location&amp;quot;&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
        echo &amp;quot;&amp;lt;input type=\&amp;quot;text\&amp;quot; name=\&amp;quot;reference\&amp;quot; size=\&amp;quot;90\&amp;quot; value=\&amp;quot;$form-&amp;gt;reference\&amp;quot; alt=\&amp;quot;reference\&amp;quot; /&amp;gt;&amp;lt;br /&amp;gt;&amp;quot;;&lt;br /&gt;
        button_to_popup_window (&amp;quot;/files/index.php?id=$form-&amp;gt;course&amp;amp;amp;choose=form.reference&amp;quot;, &amp;quot;coursefiles&amp;quot;, $xmlform[&#039;strchooseafile&#039;], 500, 750, $xmlform[&#039;strchooseafile&#039;]);&lt;br /&gt;
        echo &amp;quot;&amp;lt;input type=\&amp;quot;button\&amp;quot; name=\&amp;quot;searchbutton\&amp;quot; value=\&amp;quot;$xmlform[&#039;strsearch&#039;] ...\&amp;quot; &amp;quot;.&lt;br /&gt;
             &amp;quot;onclick=\&amp;quot;return window.open(&#039;$CFG-&amp;gt;resource_websearch&#039;, &#039;websearch&#039;, &#039;menubar=1,location=1,directories=1,toolbar=1,scrollbars,resizable,width=800,height=600&#039;);\&amp;quot; /&amp;gt;\n&amp;quot;;&lt;br /&gt;
        if ($CFG-&amp;gt;resource_allowlocalfiles) {&lt;br /&gt;
            button_to_popup_window (&amp;quot;/mod/resource/type/file/localfile.php?choose=form.reference&amp;quot;, &lt;br /&gt;
            &amp;quot;localfiles&amp;quot;, get_string(&#039;localfilechoose&#039;, &#039;resource&#039;), 400, 600, &lt;br /&gt;
            get_string(&#039;localfilechoose&#039;, &#039;resource&#039;));&lt;br /&gt;
        }&lt;br /&gt;
    ]]&amp;gt;&amp;lt;/xhtml&amp;gt;&lt;br /&gt;
    &amp;lt;url name=&amp;quot;location&amp;quot; showuploadbutton=&amp;quot;1&amp;quot; showsearchbutton=&amp;quot;1&amp;quot; help=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;dropdown name=&amp;quot;windowpopup&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&#039;0&#039;&amp;gt;pagewindow&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&#039;1&#039;&amp;gt;newwindow&amp;lt;/option&amp;gt;&lt;br /&gt;
    &amp;lt;/dropdown&amp;gt;&lt;br /&gt;
    &amp;lt;group requiredname=&amp;quot;newwindow&amp;quot; requiredvalue=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;hidden name=&amp;quot;hframepage&amp;quot; value=&amp;quot;0&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;multiselect name=&amp;quot;framepage&amp;quot; help=&amp;quot;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;1&amp;quot;&amp;gt;frameifpossible&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;/multiselect&amp;gt;&lt;br /&gt;
    &amp;lt;/group&amp;gt;&lt;br /&gt;
    &amp;lt;group requiredname=&amp;quot;newwindow&amp;quot; requiredvalue=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;multiselect name=&amp;quot;windowoptions&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;/multiselect&amp;gt;&lt;br /&gt;
        &amp;lt;integer name=&amp;quot;resource_popupwidth&amp;quot; label=&amp;quot;resource_popupwidth&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;integer name=&amp;quot;resource_popupheight&amp;quot; label=&amp;quot;resource_popupheight&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/group&amp;gt;&lt;br /&gt;
    &amp;lt;yesno name=&amp;quot;parameters&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;group requiredname=&amp;quot;newwindow&amp;quot; requiredvalue=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;xhtml&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
&amp;lt;table align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;tr&amp;gt;&lt;br /&gt;
            &amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;?php print_string(&amp;quot;parameter&amp;quot;, &amp;quot;resource&amp;quot;) ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
            &amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;?php print_string(&amp;quot;variablename&amp;quot;, &amp;quot;resource&amp;quot;) ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
        &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
for ($i=0; $i &amp;lt; $xmlform[&#039;maxparameters&#039;]; $i++) {&lt;br /&gt;
    echo &amp;quot;&amp;lt;tr&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;td valign=\&amp;quot;top\&amp;quot;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;select name=\&amp;quot;parameter$i\&amp;quot;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;option value=\&amp;quot;-\&amp;quot;&amp;gt;-- &amp;quot;.get_string(&#039;chooseparameter&#039;, &#039;resource&#039;).&amp;quot; --&amp;lt;/option&amp;gt;\n&amp;quot;;&lt;br /&gt;
    foreach ($xmlform[&#039;parameters&#039;] as $field=&amp;gt;$fieldarr) {&lt;br /&gt;
        if ($fieldarr[&#039;value&#039;] === &amp;quot;optgroup&amp;quot;) {&lt;br /&gt;
            echo &amp;quot;&amp;lt;optgroup label=\&amp;quot;{$fieldarr[&#039;langstr&#039;]}\&amp;quot;&amp;gt;\n&amp;quot;;&lt;br /&gt;
        } elseif ($fieldarr[&#039;value&#039;] === &amp;quot;/optgroup&amp;quot;) {&lt;br /&gt;
            echo &amp;quot;&amp;lt;/optgroup&amp;gt;\n&amp;quot;;&lt;br /&gt;
        } else {&lt;br /&gt;
            echo &amp;quot;&amp;lt;option value=\&amp;quot;$field\&amp;quot;&amp;quot;;&lt;br /&gt;
            if ($xmlform[&#039;alltextfield&#039;][$i][&#039;parameter&#039;] == $field) {&lt;br /&gt;
                echo &amp;quot; selected=\&amp;quot;selected\&amp;quot;&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
            echo &amp;quot;&amp;gt;{$fieldarr[&#039;langstr&#039;]}&amp;lt;/option&amp;gt;\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    echo &amp;quot;&amp;lt;/select&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;/td&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;td valign=\&amp;quot;top\&amp;quot;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;input type=\&amp;quot;text\&amp;quot; name=\&amp;quot;parse$i\&amp;quot; value=\&amp;quot;{$xmlform[&#039;alltextfield&#039;][$i][&#039;parse&#039;]}\&amp;quot; alt=\&amp;quot;parameter$i\&amp;quot;/&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;/td&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;/tr&amp;gt;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
    ]]&amp;gt;&amp;lt;/xhtml&amp;gt;&lt;br /&gt;
    &amp;lt;/group&amp;gt;&lt;br /&gt;
&#039;)&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param(&#039;strchooseafile&#039;, get_string(...));&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param(&#039;strsearch&#039;, get_string(...));&lt;br /&gt;
&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param(&#039;maxparameters&#039;, $this-&amp;gt;maxparameters);&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param(&#039;parameters&#039;, $this-&amp;gt;parameters);&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param(&#039;alltextfield&#039;, $alltextfield); // Assuming that this code is after the end of setup() in mod\resource\type\file\resource.class.php&lt;br /&gt;
&lt;br /&gt;
$winopt = $mf-&amp;gt;get(&#039;windowoptions&#039;);&lt;br /&gt;
foreach ($RESOURCE_WINDOW_OPTIONS as $optionname) {&lt;br /&gt;
    $defaultvalue = &amp;quot;resource_popup$optionname&amp;quot;;&lt;br /&gt;
    $form-&amp;gt;$optionname = $CFG-&amp;gt;$defaultvalue;&lt;br /&gt;
    if ($optionname != &#039;height&#039; &amp;amp;&amp;amp; $optionname != &#039;width&#039;) {&lt;br /&gt;
        $winopt-&amp;gt;add_option($optionname, &amp;quot;str$optionname&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Add quiz===&lt;br /&gt;
&lt;br /&gt;
[[Image:Formproposal_Add quiz form.png]]&lt;br /&gt;
&lt;br /&gt;
To convert this form, we need to do something special for the &amp;quot;students may review&amp;quot; bit. You could either invent an new &amp;lt;optiongrid&amp;gt; type, which would be quite easy to implement. That is the option used below. Alternatively, you could use the feature for including arbitrary HTML and PHP in the form, and keep the existing code for generating this part of the form. Since the existing layout breaks when you make the browser window narrow, as in the screenshot, I would be inclined to redo it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mf = moodle_form::loadstring(&#039;&lt;br /&gt;
&amp;lt;editform langfile=&amp;quot;quiz&amp;quot; help=&amp;quot;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;text name=&amp;quot;name&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;htmlarea name=&amp;quot;introduction&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;date name=&amp;quot;available&amp;quot; label=&amp;quot;quizopen&amp;quot; optional=&amp;quot;yes&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;date name=&amp;quot;due&amp;quot; label=&amp;quot;quizclose&amp;quot; optional=&amp;quot;yes&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;real name=&amp;quot;timelimit&amp;quot; lableafter=&amp;quot;minutes&amp;quot; helpalt=&amp;quot;quiztimer&amp;quot; optional=&amp;quot;yes&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;dropdown name=&amp;quot;questionsperpage&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&#039;0&#039;&amp;gt;unlimited&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;!-- other options will be added programmatically --&amp;gt;&lt;br /&gt;
    &amp;lt;/dropdown&amp;gt;&lt;br /&gt;
    &amp;lt;yesno name=&amp;quot;shufflequestions&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;!-- ... skipping the next few boring fields ... --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;optiongrid name=&amp;quot;reviewoptions&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;col name=&amp;quot;responses&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;col name=&amp;quot;scores&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;col name=&amp;quot;feedback&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;col name=&amp;quot;answers&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;row name=&amp;quot;immediately&amp;quot; lable=&amp;quot;reviewimmediately&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;row name=&amp;quot;open&amp;quot; lable=&amp;quot;reviewopen&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;row name=&amp;quot;closed&amp;quot; lable=&amp;quot;reviewclosed&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/optiongrid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;!-- ... skipping the next few boring fields ... --&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;groupmode/&amp;gt;&lt;br /&gt;
    &amp;lt;visible/&amp;gt;&lt;br /&gt;
&#039;)&lt;br /&gt;
$questionsperpage =&amp;amp; $mf-&amp;gt;get(&#039;questionsperpage&#039;);&lt;br /&gt;
for ($i = 1; $i &amp;lt;= 50; $i += 1) {&lt;br /&gt;
    $questionsperpage-&amp;gt;add_option($i);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This does not take into account the settings on http://&#039;&#039;example.com&#039;&#039;/moodle/admin/module.php?module=quiz, which lets the admin move certain quiz options to be hidden behind an &amp;quot;advanced&amp;quot; option. To implement this you would need to add a show/hide advanced control (I would do this as a &amp;lt;yesno name=&amp;quot;showadvanced&amp;quot;/&amp;gt;, and an empty advanced group, then use some PHP code like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$fix = 0;&lt;br /&gt;
$advgroup = $mf-&amp;gt;get_field(&#039;advanced&#039;);&lt;br /&gt;
&lt;br /&gt;
if ($CFG-&amp;gt;quiz_fix_timelimit) {&lt;br /&gt;
    $item = &amp;amp;$mf-&amp;gt;remove(&#039;timelimit&#039;);&lt;br /&gt;
    $advgroup-&amp;gt;add($item)&lt;br /&gt;
    $fix = 1;&lt;br /&gt;
}&lt;br /&gt;
// ... and so on, for all the other options. Or you could try to be clever &lt;br /&gt;
// and do this as a loop over an array of field names ... then&lt;br /&gt;
if (!$fix) {&lt;br /&gt;
    $form-&amp;gt;remove(&#039;showadvanced&#039;);&lt;br /&gt;
    $form-&amp;gt;remove(&#039;advanced&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Add database===&lt;br /&gt;
&lt;br /&gt;
[[Image:Formproposal_Add_database_form.png]]&lt;br /&gt;
&lt;br /&gt;
I won&#039;t type out full code for this example most of it is simple, and it should be clear how to do it given the above examples, just comment on a couple of things:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entries required before viewing&#039;&#039;&#039; I assume this is only indented because the label is so long that having it all lined up would break the table layout. I think our code using CSS for layout just word-wraps long labels and keeps everything aligned, which I think is better.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Allow posts to be rated?&#039;&#039;&#039; I would put the disabled controls in a group that only appears if the checkbox is checked, so that those controls show and hide, rather than enabling or disabling. However, if we want to keep this the same as it is now, you could implemnt the conditionalenable group type.&lt;br /&gt;
&lt;br /&gt;
===Manage groups for a course===&lt;br /&gt;
&lt;br /&gt;
This sort of form is currently beyond what the form library was designed to produce. I would leave this as hand-coded HTML. In time, the form library may gain some AJAX field types for selecting students, and such like, that could usefully be within a redesigned version of this form. Since the form field types just output HTML and Javascript, it should be possible to use them within hand-crafted forms.&lt;br /&gt;
&lt;br /&gt;
[[Image:Formproposal_Groups_form.png]]&lt;br /&gt;
&lt;br /&gt;
==Questions==&lt;br /&gt;
&lt;br /&gt;
Do we need a syntax like lable=&#039;langfile:langstring&#039; for using lang strings from other lang files where necessary? &#039;&#039;&#039;yes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Where does this live. I think the main library file that people have to include should be lib/formlib.php, and that is all anyone needs to include. However, to we break each field type into its own PHP file, perhaps in a lib/formlib directory, to make it easier to add new field types in future? &#039;&#039;&#039;Probably all in one file&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Some form fields will need bits of CSS and JavaScript to work. Do we add the CSS to the standard theme, and combine all the javascript into a single library somewhere, or do we break it up into the individual field types, and recombine it dynamically at runtime? I think I favour breaking up the PHP code, but keeping all the JS and CSS in one place. &#039;&#039;&#039;Should use YUI were possible. We will still need some custom JavaScript and CSS though. Where?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Do we use this for vaidation as well? &#039;&#039;&#039;Yes.&#039;&#039;&#039; Currently Moodle PHP files that do forms tend to look like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$var1 = require/optional_param(...);&lt;br /&gt;
// etc. for all the other form variables.&lt;br /&gt;
&lt;br /&gt;
if (data_submitted &amp;amp;&amp;amp; confirm_sesskey()) {&lt;br /&gt;
   // Try to process submitted data.&lt;br /&gt;
   if (/*processing ok*/)&lt;br /&gt;
	   // Redirect away&lt;br /&gt;
} else {&lt;br /&gt;
   // Set initial form values.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lots of code to output the form HTML. Of maybe include(somthing.html);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The proposal is to change this to something like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mf = new moodle_form();&lt;br /&gt;
// Setup $mf with the form structure&lt;br /&gt;
&lt;br /&gt;
$form = new stdClass;&lt;br /&gt;
if ($mf-&amp;gt;validate_submitted_data($form)) { // Pass by reference to get data back.&lt;br /&gt;
   // Try to process submitted data in $form&lt;br /&gt;
   if (/*processing ok*/)&lt;br /&gt;
	   // Redirect away&lt;br /&gt;
} else {&lt;br /&gt;
   // Set initial values in $form&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$mf-&amp;gt;display($form)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=AMD_Modal&amp;diff=58835</id>
		<title>AMD Modal</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=AMD_Modal&amp;diff=58835"/>
		<updated>2021-05-19T11:52:37Z</updated>

		<summary type="html">&lt;p&gt;Demsley: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.2}}&lt;br /&gt;
&lt;br /&gt;
This is currently (2017) the recommended way to create various sorts of pop-up dialogue boxes in JavaScript.&lt;br /&gt;
&lt;br /&gt;
= Why should you use it? =&lt;br /&gt;
The AMD modal modules provide a simple interface for creating a modal within Moodle. The module will ensure all accessibility requirements are met including applying the correct aria roles, focus control, aria hiding background elements and locking keyboard navigation.&lt;br /&gt;
&lt;br /&gt;
The modals will fire events for common actions that occur within the modal, e.g. show / hide, for other code to listen to and react accordingly.&lt;br /&gt;
&lt;br /&gt;
Moodle ships with a couple of standard modal types for you to re-use including a simple cancel modal, and a save/cancel modal. Hopefully with more to come!&lt;br /&gt;
&lt;br /&gt;
= How do you create a basic modal? =&lt;br /&gt;
If you&#039;d simply like to display a modal with some simple content then all you&#039;ll need is the modal factory. The factory provides a create function that accepts some configuration for your modal that the factory will use to create the modal and optionally a trigger element (the element that will open the modal when activated). The create function will return a promise that is resolved with the created modal.&lt;br /&gt;
&lt;br /&gt;
The configuration is provided as an object with key/value pairs. The options are:&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! &#039;&#039;&#039;key&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;description&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| title&lt;br /&gt;
| the title to display in the modal header - note: this will render HTML&lt;br /&gt;
|-&lt;br /&gt;
| body&lt;br /&gt;
| the main content to be rendered in the modal body&lt;br /&gt;
|-&lt;br /&gt;
| footer&lt;br /&gt;
| the content to be rendered in the modal footer&lt;br /&gt;
|-&lt;br /&gt;
| type&lt;br /&gt;
| one of the modal types registered with the factory&lt;br /&gt;
|-&lt;br /&gt;
| large&lt;br /&gt;
| a boolean to indicate if the modal should be wider than the default size&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example 1&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/modal_factory&#039;], function($, ModalFactory) {&lt;br /&gt;
  var trigger = $(&#039;#create-modal&#039;);&lt;br /&gt;
  ModalFactory.create({&lt;br /&gt;
    title: &#039;test title&#039;,&lt;br /&gt;
    body: &#039;&amp;lt;p&amp;gt;test body content&amp;lt;/p&amp;gt;&#039;,&lt;br /&gt;
    footer: &#039;test footer content&#039;,&lt;br /&gt;
  }, trigger)&lt;br /&gt;
  .done(function(modal) {&lt;br /&gt;
    // Do what you want with your new modal.&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
NB - When I tried this with Moodle 3.10 it did not work but changing the initial &amp;quot;define&amp;quot; to &#039;&#039;&#039;require&#039;&#039;&#039; worked as per [[https://docs.moodle.org/dev/Useful_core_Javascript_modules#Modal_.28core.2Fmodal.29]]&lt;br /&gt;
&lt;br /&gt;
The modals will also accept a promise for the body and footer content. It is expected that the promise will be resolved with two strings, the html content and any associated javascript. This allows the modal module to work natively with the templates module, such as in the example below.&lt;br /&gt;
&lt;br /&gt;
Example 2&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/modal_factory&#039;, &#039;core/templates&#039;], function($, ModalFactory, Templates) {&lt;br /&gt;
  var trigger = $(&#039;#create-modal&#039;);&lt;br /&gt;
  ModalFactory.create({&lt;br /&gt;
    title: &#039;test title&#039;,&lt;br /&gt;
    // Can include JS which is run when modal is attached to DOM.&lt;br /&gt;
    body: Templates.render(&#039;core/modal_test_3&#039;, {}),&lt;br /&gt;
    footer: &#039;test footer content&#039;,&lt;br /&gt;
  }, trigger)&lt;br /&gt;
  .done(function(modal) {&lt;br /&gt;
    // Do what you want with your modal.&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since the modals aren&#039;t actually added to the DOM until they are made visible any javascript resolved with the promise is cached and only run after all of the elements are added to the DOM,  so you don&#039;t have to worry about any special handling in the javascript that is being loaded from the template.&lt;br /&gt;
&lt;br /&gt;
= How to manually trigger a Modal =&lt;br /&gt;
&lt;br /&gt;
If your trigger is (say) a class that covers a number of actual elements, you may want to grab the actual element that caused the event. Unfortunately, ModalEvents do not capture this and you need to approach the modal a different way. Here is an example - the scenario is a list of items with delete buttons. You need to know which delete button in order to get the item id. Note that code is somewhat simplified for clarity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
    $(&#039;a.item-delete&#039;).on(&#039;click&#039;, function(e) {&lt;br /&gt;
        var clickedLink = $(e.currentTarget);&lt;br /&gt;
        ModalFactory.create({&lt;br /&gt;
            type: ModalFactory.types.SAVE_CANCEL,&lt;br /&gt;
            title: &#039;Delete item&#039;,&lt;br /&gt;
            body: &#039;Do you really want to delete?&#039;,&lt;br /&gt;
        })&lt;br /&gt;
        .then(function(modal) {&lt;br /&gt;
            modal.setSaveButtonText(&#039;Delete&#039;);&lt;br /&gt;
            var root = modal.getRoot();&lt;br /&gt;
            root.on(ModalEvents.save, function() {&lt;br /&gt;
                var elementid = clickedLink.data(&#039;id&#039;);&lt;br /&gt;
                // Do something to delete item&lt;br /&gt;
            });&lt;br /&gt;
            modal.show();&lt;br /&gt;
    });&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;Normal&#039; javascript is used to trap the event and the required element can be captured. No &#039;trigger&#039; is passed to the ModalFactory so the resulting modal cannot display itself. The modal save event (in this case clicking the delete button) is handled as normally and will still trigger as expected. &#039;modal.show()&#039; is called to display the modal.&lt;br /&gt;
&lt;br /&gt;
= How do you create a different type of modal? =&lt;br /&gt;
Moodle comes with a few specialised types of modals for common use cases. Look at lib/amd/src/modal_factory.js to see what is available. These can be created using the factory, similar to the examples above, by specifying the type of modal in the configuration provided to the create function. Each of the modal types may have different configuration options, for example the save/cancel modal doesn&#039;t allow you to set the footer content.&lt;br /&gt;
&lt;br /&gt;
Example 3&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/modal_factory&#039;, &#039;core/templates&#039;], function($, ModalFactory, Templates) {&lt;br /&gt;
  var trigger = $(&#039;#create-modal&#039;);&lt;br /&gt;
  ModalFactory.create({&lt;br /&gt;
    type: ModalFactory.types.SAVE_CANCEL,&lt;br /&gt;
    title: &#039;Modal save cancel&#039;,&lt;br /&gt;
    body: &#039;This modal is a save/cancel modal&#039;,&lt;br /&gt;
  }, trigger)&lt;br /&gt;
  .done(function(modal) {&lt;br /&gt;
    // Do what you want with your modal.&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each type of modal may fire additional events to allow your code to handle the new functionality being offered. See the modal_events.js module for a list of events that can be fired. For example, if you wanted to have a save/cancel modal that you did some form validation on before saving you could do something like the example below.&lt;br /&gt;
&lt;br /&gt;
Example 4&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/modal_factory&#039;, &#039;core/modal_events&#039;, &#039;core/templates&#039;],&lt;br /&gt;
        function($, ModalFactory, ModalEvents, Templates) {&lt;br /&gt;
  &lt;br /&gt;
  var trigger = $(&#039;#create-modal&#039;);&lt;br /&gt;
  ModalFactory.create({&lt;br /&gt;
    type: ModalFactory.types.SAVE_CANCEL,&lt;br /&gt;
    title: &#039;Modal save cancel&#039;,&lt;br /&gt;
    body: &#039;This modal is a save/cancel modal&#039;,&lt;br /&gt;
  }, trigger)&lt;br /&gt;
  .done(function(modal) {&lt;br /&gt;
    modal.getRoot().on(ModalEvents.save, function(e) {&lt;br /&gt;
      // Stop the default save button behaviour which is to close the modal.&lt;br /&gt;
      e.preventDefault();&lt;br /&gt;
      // Do your form validation here.&lt;br /&gt;
    });&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= How do you write a new type of modal? =&lt;br /&gt;
If you&#039;d like to write a new type of modal to be re-used throughout your code you can extend the default modal implementation and make any customisations you require. In order to create a new modal type you&#039;ll need to create a new AMD module and import the core/modal module to extend. You can also optionally create a new modal template that builds upon the core/modal template. Finally, you can register your new type with the modal registry to allow you to create your new type using the modal factory.&lt;br /&gt;
&lt;br /&gt;
For example, let&#039;s create a modal that opens a login form:&lt;br /&gt;
First we can create the HTML for out modal by including and extending the core modal template. All we need to do is override the block defined in that template with our new title, body and footer content.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s assume the file is &amp;lt;your_module&amp;gt;/templates/modal_login.mustache&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{{&amp;lt; core/modal }}&lt;br /&gt;
    {{$title}}{{#str}} login {{/str}}{{/title}}&lt;br /&gt;
    {{$body}}&lt;br /&gt;
        &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;form&amp;gt;&lt;br /&gt;
                &amp;lt;div class=&amp;quot;form-group row&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;label for=&amp;quot;inputEmail&amp;quot; class=&amp;quot;col-sm-2 col-form-label&amp;quot;&amp;gt;{{#str}} email {{/str}}&amp;lt;/label&amp;gt;&lt;br /&gt;
                    &amp;lt;div class=&amp;quot;col-sm-10&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;input type=&amp;quot;email&amp;quot; class=&amp;quot;form-control&amp;quot; id=&amp;quot;inputEmail&amp;quot; placeholder=&amp;quot;{{#str}} email {{/str}}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;div class=&amp;quot;form-group row&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;label for=&amp;quot;inputPassword&amp;quot; class=&amp;quot;col-sm-2 col-form-label&amp;quot;&amp;gt;{{#str}} password {{/str}}&amp;lt;/label&amp;gt;&lt;br /&gt;
                    &amp;lt;div class=&amp;quot;col-sm-10&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;input type=&amp;quot;password&amp;quot; class=&amp;quot;form-control&amp;quot; id=&amp;quot;inputPassword&amp;quot; placeholder=&amp;quot;{{#str}} password {{/str}}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;/form&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
    {{/body}}&lt;br /&gt;
    {{$footer}}&lt;br /&gt;
        &amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;btn btn-primary&amp;quot; data-action=&amp;quot;login&amp;quot;&amp;gt;{{#str}} login {{/str}}&amp;lt;/button&amp;gt;&lt;br /&gt;
        &amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;btn btn-secondary&amp;quot; data-action=&amp;quot;cancel&amp;quot;&amp;gt;{{#str}} cancel {{/str}}&amp;lt;/button&amp;gt;&lt;br /&gt;
    {{/footer}}&lt;br /&gt;
{{/ core/modal }}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next we can create the new AMD module for the login modal. You can extend the existing modal module which will give you all of the core modal functionality for free allowing you to focus on writing only the specific logic you need. In this example we would only need to write the logic for handling the login.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s assume the file is &amp;lt;your_module&amp;gt;/amd/src/modal_login.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/notification&#039;, &#039;core/custom_interaction_events&#039;, &#039;core/modal&#039;, &#039;core/modal_registry&#039;],&lt;br /&gt;
        function($, Notification, CustomEvents, Modal, ModalRegistry) {&lt;br /&gt;
&lt;br /&gt;
    var registered = false;&lt;br /&gt;
    var SELECTORS = {&lt;br /&gt;
        LOGIN_BUTTON: &#039;[data-action=&amp;quot;login&amp;quot;]&#039;,&lt;br /&gt;
        CANCEL_BUTTON: &#039;[data-action=&amp;quot;cancel&amp;quot;]&#039;,&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Constructor for the Modal.&lt;br /&gt;
     *&lt;br /&gt;
     * @param {object} root The root jQuery element for the modal&lt;br /&gt;
     */&lt;br /&gt;
    var ModalLogin = function(root) {&lt;br /&gt;
        Modal.call(this, root);&lt;br /&gt;
&lt;br /&gt;
        if (!this.getFooter().find(SELECTORS.LOGIN_BUTTON).length) {&lt;br /&gt;
            Notification.exception({message: &#039;No login button found&#039;});&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!this.getFooter().find(SELECTORS.CANCEL_BUTTON).length) {&lt;br /&gt;
            Notification.exception({message: &#039;No cancel button found&#039;});&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    ModalLogin.TYPE = &#039;your_module-login&#039;;&lt;br /&gt;
    ModalLogin.prototype = Object.create(Modal.prototype);&lt;br /&gt;
    ModalLogin.prototype.constructor = ModalLogin;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Set up all of the event handling for the modal.&lt;br /&gt;
     *&lt;br /&gt;
     * @method registerEventListeners&lt;br /&gt;
     */&lt;br /&gt;
    ModalLogin.prototype.registerEventListeners = function() {&lt;br /&gt;
        // Apply parent event listeners.&lt;br /&gt;
        Modal.prototype.registerEventListeners.call(this);&lt;br /&gt;
&lt;br /&gt;
        this.getModal().on(CustomEvents.events.activate, SELECTORS.LOGIN_BUTTON, function(e, data) {&lt;br /&gt;
            // Add your logic for when the login button is clicked. This could include the form validation,&lt;br /&gt;
            // loading animations, error handling etc.&lt;br /&gt;
        }.bind(this));&lt;br /&gt;
&lt;br /&gt;
        this.getModal().on(CustomEvents.events.activate, SELECTORS.CANCEL_BUTTON, function(e, data) {&lt;br /&gt;
            // Add your logic for when the cancel button is clicked.&lt;br /&gt;
        }.bind(this));&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Automatically register with the modal registry the first time this module is imported so that you can create modals&lt;br /&gt;
    // of this type using the modal factory.&lt;br /&gt;
    if (!registered) {&lt;br /&gt;
        ModalRegistry.register(ModalLogin.TYPE, ModalLogin, &#039;&amp;lt;your_module&amp;gt;/modal_login&#039;);&lt;br /&gt;
        registered = true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return ModalLogin;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you have both your template and javascript ready to go then all you need to do is tie them into where ever you&#039;d like to launch the modal. For the purpose of this example let&#039;s assume that we&#039;ve got a page with a login button with id=&amp;quot;login&amp;quot; then you might add this javascript to the page.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/templates&#039;, &#039;core/modal_factory&#039;, &#039;&amp;lt;your_module&amp;gt;/modal_login&#039;], function($, Templates, ModalFactory, ModalLogin) {&lt;br /&gt;
    var trigger = $(&#039;#login&#039;);&lt;br /&gt;
&lt;br /&gt;
    ModalFactory.create({type: ModalLogin.TYPE}, trigger); &lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Related =&lt;br /&gt;
* AMD and [[Javascript Modules]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Javascript]]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=AMD_Modal&amp;diff=58834</id>
		<title>AMD Modal</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=AMD_Modal&amp;diff=58834"/>
		<updated>2021-05-19T11:50:51Z</updated>

		<summary type="html">&lt;p&gt;Demsley: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.2}}&lt;br /&gt;
&lt;br /&gt;
This is currently (2017) the recommended way to create various sorts of pop-up dialogue boxes in JavaScript.&lt;br /&gt;
&lt;br /&gt;
= Why should you use it? =&lt;br /&gt;
The AMD modal modules provide a simple interface for creating a modal within Moodle. The module will ensure all accessibility requirements are met including applying the correct aria roles, focus control, aria hiding background elements and locking keyboard navigation.&lt;br /&gt;
&lt;br /&gt;
The modals will fire events for common actions that occur within the modal, e.g. show / hide, for other code to listen to and react accordingly.&lt;br /&gt;
&lt;br /&gt;
Moodle ships with a couple of standard modal types for you to re-use including a simple cancel modal, and a save/cancel modal. Hopefully with more to come!&lt;br /&gt;
&lt;br /&gt;
= How do you create a basic modal? =&lt;br /&gt;
If you&#039;d simply like to display a modal with some simple content then all you&#039;ll need is the modal factory. The factory provides a create function that accepts some configuration for your modal that the factory will use to create the modal and optionally a trigger element (the element that will open the modal when activated). The create function will return a promise that is resolved with the created modal.&lt;br /&gt;
&lt;br /&gt;
The configuration is provided as an object with key/value pairs. The options are:&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! &#039;&#039;&#039;key&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;description&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| title&lt;br /&gt;
| the title to display in the modal header - note: this will render HTML&lt;br /&gt;
|-&lt;br /&gt;
| body&lt;br /&gt;
| the main content to be rendered in the modal body&lt;br /&gt;
|-&lt;br /&gt;
| footer&lt;br /&gt;
| the content to be rendered in the modal footer&lt;br /&gt;
|-&lt;br /&gt;
| type&lt;br /&gt;
| one of the modal types registered with the factory&lt;br /&gt;
|-&lt;br /&gt;
| large&lt;br /&gt;
| a boolean to indicate if the modal should be wider than the default size&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example 1&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/modal_factory&#039;], function($, ModalFactory) {&lt;br /&gt;
  var trigger = $(&#039;#create-modal&#039;);&lt;br /&gt;
  ModalFactory.create({&lt;br /&gt;
    title: &#039;test title&#039;,&lt;br /&gt;
    body: &#039;&amp;lt;p&amp;gt;test body content&amp;lt;/p&amp;gt;&#039;,&lt;br /&gt;
    footer: &#039;test footer content&#039;,&lt;br /&gt;
  }, trigger)&lt;br /&gt;
  .done(function(modal) {&lt;br /&gt;
    // Do what you want with your new modal.&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
NB - When I tried this with Moodle 3.10 it did not work but changing the initial &amp;quot;define&amp;quot; to &#039;&#039;&#039;require&#039;&#039;&#039; worked.&lt;br /&gt;
&lt;br /&gt;
The modals will also accept a promise for the body and footer content. It is expected that the promise will be resolved with two strings, the html content and any associated javascript. This allows the modal module to work natively with the templates module, such as in the example below.&lt;br /&gt;
&lt;br /&gt;
Example 2&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/modal_factory&#039;, &#039;core/templates&#039;], function($, ModalFactory, Templates) {&lt;br /&gt;
  var trigger = $(&#039;#create-modal&#039;);&lt;br /&gt;
  ModalFactory.create({&lt;br /&gt;
    title: &#039;test title&#039;,&lt;br /&gt;
    // Can include JS which is run when modal is attached to DOM.&lt;br /&gt;
    body: Templates.render(&#039;core/modal_test_3&#039;, {}),&lt;br /&gt;
    footer: &#039;test footer content&#039;,&lt;br /&gt;
  }, trigger)&lt;br /&gt;
  .done(function(modal) {&lt;br /&gt;
    // Do what you want with your modal.&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since the modals aren&#039;t actually added to the DOM until they are made visible any javascript resolved with the promise is cached and only run after all of the elements are added to the DOM,  so you don&#039;t have to worry about any special handling in the javascript that is being loaded from the template.&lt;br /&gt;
&lt;br /&gt;
= How to manually trigger a Modal =&lt;br /&gt;
&lt;br /&gt;
If your trigger is (say) a class that covers a number of actual elements, you may want to grab the actual element that caused the event. Unfortunately, ModalEvents do not capture this and you need to approach the modal a different way. Here is an example - the scenario is a list of items with delete buttons. You need to know which delete button in order to get the item id. Note that code is somewhat simplified for clarity.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
    $(&#039;a.item-delete&#039;).on(&#039;click&#039;, function(e) {&lt;br /&gt;
        var clickedLink = $(e.currentTarget);&lt;br /&gt;
        ModalFactory.create({&lt;br /&gt;
            type: ModalFactory.types.SAVE_CANCEL,&lt;br /&gt;
            title: &#039;Delete item&#039;,&lt;br /&gt;
            body: &#039;Do you really want to delete?&#039;,&lt;br /&gt;
        })&lt;br /&gt;
        .then(function(modal) {&lt;br /&gt;
            modal.setSaveButtonText(&#039;Delete&#039;);&lt;br /&gt;
            var root = modal.getRoot();&lt;br /&gt;
            root.on(ModalEvents.save, function() {&lt;br /&gt;
                var elementid = clickedLink.data(&#039;id&#039;);&lt;br /&gt;
                // Do something to delete item&lt;br /&gt;
            });&lt;br /&gt;
            modal.show();&lt;br /&gt;
    });&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;Normal&#039; javascript is used to trap the event and the required element can be captured. No &#039;trigger&#039; is passed to the ModalFactory so the resulting modal cannot display itself. The modal save event (in this case clicking the delete button) is handled as normally and will still trigger as expected. &#039;modal.show()&#039; is called to display the modal.&lt;br /&gt;
&lt;br /&gt;
= How do you create a different type of modal? =&lt;br /&gt;
Moodle comes with a few specialised types of modals for common use cases. Look at lib/amd/src/modal_factory.js to see what is available. These can be created using the factory, similar to the examples above, by specifying the type of modal in the configuration provided to the create function. Each of the modal types may have different configuration options, for example the save/cancel modal doesn&#039;t allow you to set the footer content.&lt;br /&gt;
&lt;br /&gt;
Example 3&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/modal_factory&#039;, &#039;core/templates&#039;], function($, ModalFactory, Templates) {&lt;br /&gt;
  var trigger = $(&#039;#create-modal&#039;);&lt;br /&gt;
  ModalFactory.create({&lt;br /&gt;
    type: ModalFactory.types.SAVE_CANCEL,&lt;br /&gt;
    title: &#039;Modal save cancel&#039;,&lt;br /&gt;
    body: &#039;This modal is a save/cancel modal&#039;,&lt;br /&gt;
  }, trigger)&lt;br /&gt;
  .done(function(modal) {&lt;br /&gt;
    // Do what you want with your modal.&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each type of modal may fire additional events to allow your code to handle the new functionality being offered. See the modal_events.js module for a list of events that can be fired. For example, if you wanted to have a save/cancel modal that you did some form validation on before saving you could do something like the example below.&lt;br /&gt;
&lt;br /&gt;
Example 4&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/modal_factory&#039;, &#039;core/modal_events&#039;, &#039;core/templates&#039;],&lt;br /&gt;
        function($, ModalFactory, ModalEvents, Templates) {&lt;br /&gt;
  &lt;br /&gt;
  var trigger = $(&#039;#create-modal&#039;);&lt;br /&gt;
  ModalFactory.create({&lt;br /&gt;
    type: ModalFactory.types.SAVE_CANCEL,&lt;br /&gt;
    title: &#039;Modal save cancel&#039;,&lt;br /&gt;
    body: &#039;This modal is a save/cancel modal&#039;,&lt;br /&gt;
  }, trigger)&lt;br /&gt;
  .done(function(modal) {&lt;br /&gt;
    modal.getRoot().on(ModalEvents.save, function(e) {&lt;br /&gt;
      // Stop the default save button behaviour which is to close the modal.&lt;br /&gt;
      e.preventDefault();&lt;br /&gt;
      // Do your form validation here.&lt;br /&gt;
    });&lt;br /&gt;
  });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= How do you write a new type of modal? =&lt;br /&gt;
If you&#039;d like to write a new type of modal to be re-used throughout your code you can extend the default modal implementation and make any customisations you require. In order to create a new modal type you&#039;ll need to create a new AMD module and import the core/modal module to extend. You can also optionally create a new modal template that builds upon the core/modal template. Finally, you can register your new type with the modal registry to allow you to create your new type using the modal factory.&lt;br /&gt;
&lt;br /&gt;
For example, let&#039;s create a modal that opens a login form:&lt;br /&gt;
First we can create the HTML for out modal by including and extending the core modal template. All we need to do is override the block defined in that template with our new title, body and footer content.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s assume the file is &amp;lt;your_module&amp;gt;/templates/modal_login.mustache&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{{&amp;lt; core/modal }}&lt;br /&gt;
    {{$title}}{{#str}} login {{/str}}{{/title}}&lt;br /&gt;
    {{$body}}&lt;br /&gt;
        &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;form&amp;gt;&lt;br /&gt;
                &amp;lt;div class=&amp;quot;form-group row&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;label for=&amp;quot;inputEmail&amp;quot; class=&amp;quot;col-sm-2 col-form-label&amp;quot;&amp;gt;{{#str}} email {{/str}}&amp;lt;/label&amp;gt;&lt;br /&gt;
                    &amp;lt;div class=&amp;quot;col-sm-10&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;input type=&amp;quot;email&amp;quot; class=&amp;quot;form-control&amp;quot; id=&amp;quot;inputEmail&amp;quot; placeholder=&amp;quot;{{#str}} email {{/str}}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;div class=&amp;quot;form-group row&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;label for=&amp;quot;inputPassword&amp;quot; class=&amp;quot;col-sm-2 col-form-label&amp;quot;&amp;gt;{{#str}} password {{/str}}&amp;lt;/label&amp;gt;&lt;br /&gt;
                    &amp;lt;div class=&amp;quot;col-sm-10&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;input type=&amp;quot;password&amp;quot; class=&amp;quot;form-control&amp;quot; id=&amp;quot;inputPassword&amp;quot; placeholder=&amp;quot;{{#str}} password {{/str}}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;/form&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
    {{/body}}&lt;br /&gt;
    {{$footer}}&lt;br /&gt;
        &amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;btn btn-primary&amp;quot; data-action=&amp;quot;login&amp;quot;&amp;gt;{{#str}} login {{/str}}&amp;lt;/button&amp;gt;&lt;br /&gt;
        &amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;btn btn-secondary&amp;quot; data-action=&amp;quot;cancel&amp;quot;&amp;gt;{{#str}} cancel {{/str}}&amp;lt;/button&amp;gt;&lt;br /&gt;
    {{/footer}}&lt;br /&gt;
{{/ core/modal }}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next we can create the new AMD module for the login modal. You can extend the existing modal module which will give you all of the core modal functionality for free allowing you to focus on writing only the specific logic you need. In this example we would only need to write the logic for handling the login.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s assume the file is &amp;lt;your_module&amp;gt;/amd/src/modal_login.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/notification&#039;, &#039;core/custom_interaction_events&#039;, &#039;core/modal&#039;, &#039;core/modal_registry&#039;],&lt;br /&gt;
        function($, Notification, CustomEvents, Modal, ModalRegistry) {&lt;br /&gt;
&lt;br /&gt;
    var registered = false;&lt;br /&gt;
    var SELECTORS = {&lt;br /&gt;
        LOGIN_BUTTON: &#039;[data-action=&amp;quot;login&amp;quot;]&#039;,&lt;br /&gt;
        CANCEL_BUTTON: &#039;[data-action=&amp;quot;cancel&amp;quot;]&#039;,&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Constructor for the Modal.&lt;br /&gt;
     *&lt;br /&gt;
     * @param {object} root The root jQuery element for the modal&lt;br /&gt;
     */&lt;br /&gt;
    var ModalLogin = function(root) {&lt;br /&gt;
        Modal.call(this, root);&lt;br /&gt;
&lt;br /&gt;
        if (!this.getFooter().find(SELECTORS.LOGIN_BUTTON).length) {&lt;br /&gt;
            Notification.exception({message: &#039;No login button found&#039;});&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!this.getFooter().find(SELECTORS.CANCEL_BUTTON).length) {&lt;br /&gt;
            Notification.exception({message: &#039;No cancel button found&#039;});&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    ModalLogin.TYPE = &#039;your_module-login&#039;;&lt;br /&gt;
    ModalLogin.prototype = Object.create(Modal.prototype);&lt;br /&gt;
    ModalLogin.prototype.constructor = ModalLogin;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Set up all of the event handling for the modal.&lt;br /&gt;
     *&lt;br /&gt;
     * @method registerEventListeners&lt;br /&gt;
     */&lt;br /&gt;
    ModalLogin.prototype.registerEventListeners = function() {&lt;br /&gt;
        // Apply parent event listeners.&lt;br /&gt;
        Modal.prototype.registerEventListeners.call(this);&lt;br /&gt;
&lt;br /&gt;
        this.getModal().on(CustomEvents.events.activate, SELECTORS.LOGIN_BUTTON, function(e, data) {&lt;br /&gt;
            // Add your logic for when the login button is clicked. This could include the form validation,&lt;br /&gt;
            // loading animations, error handling etc.&lt;br /&gt;
        }.bind(this));&lt;br /&gt;
&lt;br /&gt;
        this.getModal().on(CustomEvents.events.activate, SELECTORS.CANCEL_BUTTON, function(e, data) {&lt;br /&gt;
            // Add your logic for when the cancel button is clicked.&lt;br /&gt;
        }.bind(this));&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Automatically register with the modal registry the first time this module is imported so that you can create modals&lt;br /&gt;
    // of this type using the modal factory.&lt;br /&gt;
    if (!registered) {&lt;br /&gt;
        ModalRegistry.register(ModalLogin.TYPE, ModalLogin, &#039;&amp;lt;your_module&amp;gt;/modal_login&#039;);&lt;br /&gt;
        registered = true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return ModalLogin;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you have both your template and javascript ready to go then all you need to do is tie them into where ever you&#039;d like to launch the modal. For the purpose of this example let&#039;s assume that we&#039;ve got a page with a login button with id=&amp;quot;login&amp;quot; then you might add this javascript to the page.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;, &#039;core/templates&#039;, &#039;core/modal_factory&#039;, &#039;&amp;lt;your_module&amp;gt;/modal_login&#039;], function($, Templates, ModalFactory, ModalLogin) {&lt;br /&gt;
    var trigger = $(&#039;#login&#039;);&lt;br /&gt;
&lt;br /&gt;
    ModalFactory.create({type: ModalLogin.TYPE}, trigger); &lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Related =&lt;br /&gt;
* AMD and [[Javascript Modules]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Javascript]]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Javascript_Modules&amp;diff=57646</id>
		<title>Javascript Modules</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Javascript_Modules&amp;diff=57646"/>
		<updated>2020-06-23T10:50:20Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* Install grunt */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.9}}&lt;br /&gt;
&lt;br /&gt;
= Javascript Modules =&lt;br /&gt;
&lt;br /&gt;
== What is a Javascript module and why do I care? ==&lt;br /&gt;
&lt;br /&gt;
A Javascript module is nothing more than a collection of Javascript code that can be used (reliably) from other pieces of Javascript. &lt;br /&gt;
&lt;br /&gt;
== Why should I package my code as a module? ==&lt;br /&gt;
&lt;br /&gt;
By packaging your code as a module you break your code up into smaller reusable pieces. This is good because:&lt;br /&gt;
&lt;br /&gt;
a) Each smaller piece is simpler to understand / debug&lt;br /&gt;
&lt;br /&gt;
b) Each smaller piece is simpler to test&lt;br /&gt;
&lt;br /&gt;
c) You can re-use common code instead of duplicating it&lt;br /&gt;
&lt;br /&gt;
= How do I write a Javascript module in Moodle? =&lt;br /&gt;
&lt;br /&gt;
Since version 2.9, Moodle supports Javascript modules written using the Asynchronous Module Definition ([https://github.com/amdjs/amdjs-api/wiki/AMD AMD]) API. This is a standard API for creating Javascript modules and you will find many useful third party libraries that are already using this format. &lt;br /&gt;
&lt;br /&gt;
To edit or create an AMD module in Moodle you need to do a couple of things. &lt;br /&gt;
&lt;br /&gt;
Since version 3.8, Moodle supports [https://github.com/lukehoban/es6features#readme ECMAScript 2015 features] (aka ES6) in a cross browser compatible way thanks to [https://babeljs.io/ Babel JS]. In order to achieve the compatibility with older browsers Babel will compile the newer ES6 features back into ES5 Javascript. Unfortunately this means that in order for your Javascript changes to show in the browser they must be compiled by running [[Grunt]], even with the cachejs config setting set to false (i.e. &amp;quot;Development mode&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Install NVM and Node ==&lt;br /&gt;
&lt;br /&gt;
The recommended way of installing NodeJS is via the [https://github.com/nvm-sh/nvm Node Version Manager], or NVM. NVM allows you to have several different versions of NodeJS installed at and in-use at any once on your computer. Supported versions of Moodle all use version {{NodeJSExactVersion}} of NodeJS.&lt;br /&gt;
&lt;br /&gt;
https://github.com/nvm-sh/nvm#installing-and-updating&lt;br /&gt;
&lt;br /&gt;
Confirm it is working:&lt;br /&gt;
&lt;br /&gt;
 $ nvm --version&lt;br /&gt;
 0.35.3&lt;br /&gt;
&lt;br /&gt;
After you have installed &#039;&#039;&#039;nvm&#039;&#039;&#039;, you should install the correct version of NodeJS by running the following commands from your Moodle directory:&lt;br /&gt;
&lt;br /&gt;
 nvm install&lt;br /&gt;
 nvm use&lt;br /&gt;
&lt;br /&gt;
If your primary use of NodeJS is for Moodle then we recommend that you set NodeJS version {{NodeJSExactVersion}} as your default version. You can do this by running:&lt;br /&gt;
&lt;br /&gt;
 nvm alias default {{NodeJSExactVersion}}&lt;br /&gt;
&lt;br /&gt;
== Install grunt ==&lt;br /&gt;
&lt;br /&gt;
The AMD modules in Moodle must be processed by some build tools before they will be visible to your web browser. We use &amp;quot;[[grunt]]&amp;quot; as a build tool to wrap our different processes. Grunt is a build tool written in Javascript that runs in the &amp;quot;[http://nodejs.org/ nodejs]&amp;quot; environment.&lt;br /&gt;
&lt;br /&gt;
Once this is done, you can run the the following commands from your Moodle directory:&lt;br /&gt;
&lt;br /&gt;
 npm install&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;This may mention vulnerabilities, that&#039;s fine and doesn&#039;t apply.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 npm install -g grunt-cli&lt;br /&gt;
&lt;br /&gt;
== Development mode (Moodle v2.9 to v3.7)  ==&lt;br /&gt;
&lt;br /&gt;
To avoid having to constantly run grunt, make sure you set the following in your config.php&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Prevent JS caching&lt;br /&gt;
$CFG-&amp;gt;cachejs = false;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Moodle will now run your module from the amd/src module. Don&#039;t forget to switch this off and run &#039;grunt&#039; before deploying the new version!&lt;br /&gt;
&lt;br /&gt;
In this mode - if you get a strange message in your javascript console like &amp;quot;No define call for core/first&amp;quot; it means you have a syntax error in the javascript you are developing. &lt;br /&gt;
Or, &amp;quot;No define call for theme_XXX/loader&amp;quot; as you are probably missing the &#039;src&#039; folder with relevant JS files. which might happen when you turn debugging ON on a theme that was bought, without &#039;src&#039; folder :-(&lt;br /&gt;
&lt;br /&gt;
== Development mode (Moodle v3.8 and above)  ==&lt;br /&gt;
&lt;br /&gt;
All Javascript code is now compiled using Babel which means Moodle will only ever serve minified Javascript to the browser, even in development mode. However in development mode Moodle will also send the browser the corresponding source map files for each of the Javascript modules. The source map files will tell the browser how to map the minified source code back to the unminified original source code so that the original source files will be displayed in the sources section of the browser&#039;s development tools.&lt;br /&gt;
&lt;br /&gt;
While in development mode each of the Javascript modules will appear in the browser&#039;s source tree as separate modules (no more giant first.js file!) and they will also be loaded with individual network requests (this is a compromise we had to make thanks to some browser bugs with source map files).&lt;br /&gt;
&lt;br /&gt;
To enable development mode set the &#039;&#039;&#039;cachejs&#039;&#039;&#039; config value to &#039;&#039;&#039;false&#039;&#039;&#039; in the admin settings or directly in your config.php file:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Prevent JS caching&lt;br /&gt;
$CFG-&amp;gt;cachejs = false;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since all Javascript must now be compiled you must run [[Grunt]] in order for you changes to appear in the browser. However rather than running Grunt manually each time on either the whole project or each file you modified, it is recommended that you just run the &#039;&#039;&#039;grunt watch&#039;&#039;&#039; task at the root of your Moodle directory. The grunt watch task will listen for changes to the Javascript files in the Moodle directory and will automatically lint and compile only the file that is changed after each change is detected. This removes the need to manually run grunt after each change.&lt;br /&gt;
&lt;br /&gt;
== Running grunt ==&lt;br /&gt;
&lt;br /&gt;
You can run grunt in your plugin&#039;s &#039;amd&#039; directory and it will only operate on your modules. If you&#039;re having problems or just want to check your work it is worth running for the &#039;lint&#039; feature. This can find basic problems. This sub-directory support wont work on Windows unfortunately but there is an alternative: Run grunt from the top directory with the --root=path/to/dir to limit execution to a sub-directory.&lt;br /&gt;
&lt;br /&gt;
See [[Grunt#Running_grunt]] for more details of specific grunt commands which can be used.&lt;br /&gt;
&lt;br /&gt;
If you get the error message&lt;br /&gt;
&lt;br /&gt;
 /usr/bin/env: node: No such file or directory&lt;br /&gt;
&lt;br /&gt;
Then see the thread https://github.com/nodejs/node-v0.x-archive/issues/3911&lt;br /&gt;
&lt;br /&gt;
On Ubuntu 14.04 this fixed it for me:&lt;br /&gt;
&lt;br /&gt;
 sudo ln -fs /usr/bin/nodejs /usr/local/bin/node&lt;br /&gt;
&lt;br /&gt;
Note: Once you have run grunt and built your code, you will then need to purge Moodle caches otherwise the changes made to your minified files may not be picked up by Moodle.&lt;br /&gt;
&lt;br /&gt;
== ES6 Modules (Moodle v3.8 and above) ==&lt;br /&gt;
&lt;br /&gt;
In addition to AMD module syntax Moodle now supports the [https://github.com/lukehoban/es6features#modules ES6 syntax] for writing Javascript modules. All modules (defined using either syntax) are compatible with one another. Behind the scenes the ES6 module syntax is converted into an AMD syntax as part of the Babel compiling process.&lt;br /&gt;
&lt;br /&gt;
=== Export default ===&lt;br /&gt;
There is one slight difference between the ES6 definition for exporting modules and the RequireJS (AMD) definition.&lt;br /&gt;
&lt;br /&gt;
ES6 allows you to export a “default” value which is actually no different to exporting a named value where the name is “default”. Unfortunately, RequireJS allows for unnamed default exports (e.g. you can do &amp;quot;return SomeClass;&amp;quot;) which can be imported by just requiring them in other AMD modules.&lt;br /&gt;
&lt;br /&gt;
That’s a bit confusing, get to the point! Well, it basically means that in Moodle you won’t be able to write an ES6 module that exports both a default and named exports, e.g. &amp;quot;export default function() {...}&amp;quot; and &amp;quot;export const FOO = &#039;bar&#039;&amp;quot; in the same module. The export default will simply override all other exports in that module.&lt;br /&gt;
&lt;br /&gt;
=== Inline Javascript ===&lt;br /&gt;
Another important note is that ES6 support is only for stand alone Javascript files because it relies on the compilation from Babel and Grunt. That means any inline Javascript (either in PHP or in Mustache templates) won&#039;t support the ES6 features. Instead it would be best to keep the inline Javascript as minimal as possible and only use it to load a stand alone Javascript module.&lt;br /&gt;
&lt;br /&gt;
== Minimum (getting started) module for plugins ==&lt;br /&gt;
&lt;br /&gt;
This shows the absolute minimum module you need to get started adding modules to your plugins. It&#039;s actually quite simple...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
// Put this file in path/to/plugin/amd/src&lt;br /&gt;
// You can call it anything you like&lt;br /&gt;
&lt;br /&gt;
define([&#039;jquery&#039;], function($) {&lt;br /&gt;
&lt;br /&gt;
    return {&lt;br /&gt;
        init: function() {&lt;br /&gt;
&lt;br /&gt;
            // Put whatever you like here. $ is available&lt;br /&gt;
            // to you as normal.&lt;br /&gt;
            $(&amp;quot;.someclass&amp;quot;).change(function() {&lt;br /&gt;
                alert(&amp;quot;It changed!!&amp;quot;);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code passes the jquery module into our function (parameter $). There are a number of other useful modules available in Moodle, some of which you&#039;ll probably need in a practical application. See [[Useful_core_Javascript_modules]]. Simply list them in both the define() first parameter and the function callback. E.g.,&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
    define([&#039;jquery&#039;, &#039;core/str&#039;, &#039;core/ajax&#039;], function($, str, ajax) {&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The idea here is that we will run the &#039;init&#039; function from our (PHP) code to set things up. This is called from PHP like this...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    $PAGE-&amp;gt;requires-&amp;gt;js_call_amd(&#039;frankenstyle_path/your_js_filename&#039;, &#039;init&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to supply the complete &#039;[[Frankenstyle]]&#039; path. The .js is not needed. &lt;br /&gt;
&lt;br /&gt;
js_call_amd takes a third parameter which is an &#039;&#039;array&#039;&#039; of parameters. These will translate to individual parameters in the &#039;init&#039; function call. For example...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    $PAGE-&amp;gt;requires-&amp;gt;js_call_amd(&#039;block_iomad_company_admin/department_select&#039;, &#039;init&#039;, array($first, $last));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...calls&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
    return {&lt;br /&gt;
        init: function(first, last) {&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A more comprehensive explanation follows...&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Hello World&amp;quot; I am a Javascript Module ==&lt;br /&gt;
Lets now create a simple Javascript module so we can see how to lay things out. &lt;br /&gt;
&lt;br /&gt;
Each Javascript module is contained in a single source file in the &amp;lt;componentdir&amp;gt;/amd/src folder. The final name of the module is taken from the file name and the component name. E.g. block_overview/amd/src/helloworld.js would be a module named &amp;quot;block_overview/helloworld&amp;quot;. the name of the module is important when you want to call it from somewhere else in the code. &lt;br /&gt;
&lt;br /&gt;
After running grunt - the minified Javascript files are stored in the &amp;lt;componentdir&amp;gt;/amd/build folder. The javascript files are renamed to show that they are minified (helloworld.js becomes helloworld.min.js). &lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add the built files (the ones in amd/build) to your git commits, or in production no-one will see your changes. &lt;br /&gt;
&lt;br /&gt;
Lets create a simple module now:&lt;br /&gt;
&lt;br /&gt;
blocks/overview/amd/src/helloworld.js&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
// Standard license block omitted.&lt;br /&gt;
/*&lt;br /&gt;
 * @package    block_overview&lt;br /&gt;
 * @copyright  2015 Someone cool&lt;br /&gt;
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
 /**&lt;br /&gt;
  * @module block_overview/helloworld&lt;br /&gt;
  */&lt;br /&gt;
define([&#039;jquery&#039;], function($) {&lt;br /&gt;
&lt;br /&gt;
     /** &lt;br /&gt;
      * Give me blue.&lt;br /&gt;
      * @access private&lt;br /&gt;
      * @return {string}&lt;br /&gt;
      */&lt;br /&gt;
     var makeItBlue = function() {&lt;br /&gt;
          // We can use our jquery dependency here.&lt;br /&gt;
          return $(&#039;.blue&#039;).show();&lt;br /&gt;
     };&lt;br /&gt;
      &lt;br /&gt;
    /**&lt;br /&gt;
     * @constructor&lt;br /&gt;
     * @alias module:block_overview/helloworld&lt;br /&gt;
     */&lt;br /&gt;
    var greeting = function() {&lt;br /&gt;
        /** @access private */&lt;br /&gt;
        var privateThoughts = &#039;I like the colour blue&#039;;&lt;br /&gt;
        &lt;br /&gt;
        /** @access public */&lt;br /&gt;
        this.publicThoughts = &#039;I like the colour orange&#039;;&lt;br /&gt;
&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * A formal greeting.&lt;br /&gt;
     * @access public&lt;br /&gt;
     * @return {string}&lt;br /&gt;
     */&lt;br /&gt;
    greeting.prototype.formal = function() {&lt;br /&gt;
        return &#039;How do you do?&#039;;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * An informal greeting.&lt;br /&gt;
     * @access public&lt;br /&gt;
     * @return {string}&lt;br /&gt;
     */&lt;br /&gt;
    greeting.prototype.informal = function() {&lt;br /&gt;
        return &#039;Wassup!&#039;;&lt;br /&gt;
    };&lt;br /&gt;
    return greeting;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The most interesting line above is:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
define([&#039;jquery&#039;], function($) {&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All AMD modules must call &amp;quot;define()&amp;quot; as the first and only global scoped piece of code. This ensures the javascript code contains no global variables and will not conflict with any other loaded module. The name of the module does not need to be specified because it is determined from the filename and component (but it can be listed in a comment for JSDoc as shown here). &lt;br /&gt;
&lt;br /&gt;
The first argument to &amp;quot;define&amp;quot; is the list of dependencies for the module. This argument must be passed as an array, even if there is only one. In this example &amp;quot;jquery&amp;quot; is a dependency. &amp;quot;jquery&amp;quot; is shipped as a core module is available to all AMD modules. &lt;br /&gt;
&lt;br /&gt;
The second argument to &amp;quot;define&amp;quot; is the function that defines the module. This function will receive as arguments, each of the requested dependencies in the same order they were requested. In this example we receive JQuery as an argument and we name the variable &amp;quot;$&amp;quot; (it&#039;s a JQuery thing). We can then access JQuery normally through the $ variable which is in scope for any code in our module. &lt;br /&gt;
&lt;br /&gt;
The rest of the code in this example is a standard way to define a Javascript module with public/private variables and methods. There are many ways to do this, this is only one.&lt;br /&gt;
&lt;br /&gt;
It is important that we are returning &#039;greeting&#039;. If there is no return then your module will be declared as undefined.&lt;br /&gt;
&lt;br /&gt;
== Loading modules dynamically ==&lt;br /&gt;
What do you do if you don&#039;t know in advance which modules will be required? Stuffing all possible required modules in the define call is one solution, but it&#039;s ugly and it only works for code that is in an AMD module (what about inline code in the page?). AMD lets you load a dependency any time you like. &lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Load a new dependency.&lt;br /&gt;
require([&#039;mod_wiki/timer&#039;], function(timer) {&lt;br /&gt;
   // timer is available to do my bidding.&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Including an external javascript/jquery library ==&lt;br /&gt;
If you want to include a javascript / jquery library downloaded from the internet you can do so as follows:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Warning: if the library you download, supports AMD but is already &amp;quot;named&amp;quot; you will not be able to include it directly&#039;&#039;&#039;&lt;br /&gt;
e.g. &lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
        define(&amp;quot;typeahead.js&amp;quot;, *[ &amp;quot;jquery&amp;quot; ], function(a0) {&lt;br /&gt;
            return factory(a0);&lt;br /&gt;
        });&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
will not work, as moodle injects it&#039;s own define name when loading the library.&lt;br /&gt;
&lt;br /&gt;
If the library is in AMD format and has a define:&lt;br /&gt;
e.g. i want to include the jquery final countdown timer on my page ( hilios.github.io/jQuery.countdown/ )&lt;br /&gt;
* download the module in both normal and minified versions&lt;br /&gt;
* place the modules in your moodle install e.g. your custom theme dir, or plugin dir&lt;br /&gt;
* /theme/mytheme/amd/src/jquery.countdown.js&lt;br /&gt;
&lt;br /&gt;
you can now include the module and initialise it (there are multiple ways to do this)&lt;br /&gt;
php:&lt;br /&gt;
&lt;br /&gt;
1. Create your own AMD module and initialise it:&lt;br /&gt;
&lt;br /&gt;
In your PHP file:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$this-&amp;gt;page-&amp;gt;requires-&amp;gt;js_call_amd(&#039;theme_mytheme/countdowntimer&#039;, &#039;initialise&#039;, $params);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Javascript module:&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
// /theme/mytheme/amd/src/countdowntimer.js&lt;br /&gt;
define([&#039;jquery&#039;, &#039;theme_mytheme/jquery.countdown&#039;], function($, c) {&lt;br /&gt;
    return {&lt;br /&gt;
        initialise: function ($params) {&lt;br /&gt;
           $(&#039;#clock&#039;).countdown(&#039;2020/10/10&#039;, function(event) {&lt;br /&gt;
             $(this).html(event.strftime(&#039;%D days %H:%M:%S&#039;));&lt;br /&gt;
           });&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Put the javascript into a mustache template:&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
// /theme/mytheme/templates/countdowntimer.mustache&lt;br /&gt;
&amp;lt;span id=&amp;quot;clock&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
{{#js}}&lt;br /&gt;
require([&#039;jquery&#039;, &#039;theme_mytheme/jquery.countdown&#039;], function($) {&lt;br /&gt;
           $(&#039;#clock&#039;).countdown(&#039;2020/10/10&#039;, function(event) {&lt;br /&gt;
             $(this).html(event.strftime(&#039;%D days %H:%M:%S&#039;));&lt;br /&gt;
           });&lt;br /&gt;
});&lt;br /&gt;
{{/js}}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3. Call the javascript directly from php (although who would want to put javascript into php? ergh):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;requires-&amp;gt;js_amd_inline(&#039;&lt;br /&gt;
require([&#039;theme_mytheme/jquery.countdown&#039;], function(min) {&lt;br /&gt;
           $(&#039;#clock&#039;).countdown(&#039;2020/10/10&#039;, function(event) {&lt;br /&gt;
             $(this).html(event.strftime(&#039;%D days %H:%M:%S&#039;));&lt;br /&gt;
           });&lt;br /&gt;
});&lt;br /&gt;
&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Note: It could be useful when you wish to pass a big chunk of data from php directly into a JS variable, to workaround MDL-62468 .&amp;lt;br&amp;gt;&lt;br /&gt;
Example: https://github.com/moodle/moodle/blob/master/media/player/videojs/classes/plugin.php#L386&lt;br /&gt;
&lt;br /&gt;
===More examples of including 3rd-party JS modules===&lt;br /&gt;
* [https://moodle.org/mod/forum/discuss.php?d=327579#p1317252 Adding custom js via AMD (highcharts) to essential theme]&lt;br /&gt;
&lt;br /&gt;
== Embedding AMD code in a page ==&lt;br /&gt;
So you have created lots of cool Javascript modules. Great. How do we actually call them? Any javascript code that calls an AMD module must execute AFTER the requirejs module loader has finished loading. We have provided a function &amp;quot;js_call_amd&amp;quot; that will call a single function from an AMD module with parameters.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;requires-&amp;gt;js_call_amd($modulename, $functionname, $params);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
that will &amp;quot;do the right thing&amp;quot; with your block of AMD code and execute it at the end of the page, after our AMD module loader has loaded. &lt;br /&gt;
Notes:&lt;br /&gt;
* the $modulename is the &#039;componentname/modulename&#039; discussed above&lt;br /&gt;
* the $functionname is the name of a public function exposed by the amd module. &lt;br /&gt;
* the $params is an array of params passed as arguments to the function. These should be simple types that can be handled by json_encode (no recursive arrays, or complex classes please). &lt;br /&gt;
* if the size of the params array is too large (&amp;gt; 1Kb), this will produce a developer warning. Do not attempt to pass large amounts of data through this function, it will pollute the page size. A preferred approach is to pass css selectors for DOM elements that contain data-attributes for any required data, or fetch data via ajax in the background.&lt;br /&gt;
&lt;br /&gt;
AMD / JS code can also be embedded on a page via mustache templates&lt;br /&gt;
see here: https://docs.moodle.org/dev/Templates#What_if_a_template_contains_javascript.3F&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== npm-shrinkwrap.json sha1 / sha512 changes ===&lt;br /&gt;
&lt;br /&gt;
If grunt changes all the hashes in npm-shrinkwrap.json then try this:&lt;br /&gt;
&lt;br /&gt;
 rm -rf node_modules &amp;amp;&amp;amp; npm i&lt;br /&gt;
&lt;br /&gt;
== But I have a mega JS file I don&#039;t want loaded on every page? ==&lt;br /&gt;
Loading all JS files at once and stuffing them in the browser cache is the right choice for MOST js files, there are probably some exceptions. For these files, you can rename the javascript file to end with the suffix &amp;quot;-lazy.js&amp;quot; which indicates that the module will not be loaded by default, it will be requested the first time it is used. There is no difference in usage for lazy loaded modules, the require() call looks exactly the same, it&#039;s just that the module name will also have the &amp;quot;-lazy&amp;quot; suffix.&lt;br /&gt;
&lt;br /&gt;
== Useful links ==&lt;br /&gt;
* [https://assets.moodlemoot.org/sites/15/20171004085436/JavaScript-AMD-with-RequireJS-presented-by-Daniel-Roperto-Catalyst.pdf JavaScript AMD with RequireJS] presented by Daniel Roperto, Catalyst. (MoodleMOOT AU 2017)&lt;br /&gt;
* [[Useful_core_Javascript_modules]]&lt;br /&gt;
*[[Guide_to_adding_third_party_jQuery_for_AMD]] by Patrick Thibaudeau&lt;br /&gt;
*[https://moodle.org/mod/forum/discuss.php?d=378112#p1524459 How to get variables from PHP into javascript AMD modules in M3.5] Justin Hunt, on Moodle forums.&lt;br /&gt;
*MDL-67327 JavaScript issues when not following the official documentation, since Moodle 3.8&lt;br /&gt;
&lt;br /&gt;
[[Category:AJAX]]&lt;br /&gt;
[[Category:Javascript]]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Setting_up_your_development_environment_for_the_Moodle_App&amp;diff=57574</id>
		<title>Setting up your development environment for the Moodle App</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Setting_up_your_development_environment_for_the_Moodle_App&amp;diff=57574"/>
		<updated>2020-06-10T11:03:09Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* Open the app in the browser */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle Mobile}}&lt;br /&gt;
{{Moodle Mobile 3.5}}&lt;br /&gt;
&lt;br /&gt;
Note: These instructions do work (give or take) for the current 3.5.x version of the Moodle Mobile app.&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
The structure of this page is&lt;br /&gt;
* the first part, up to the point where you get the &#039;ionic serve&#039; command to work is what you need to be able to do development on the app and test it in a browser.&lt;br /&gt;
* the second part is about how to build a version of the app that can be run on a device.&lt;br /&gt;
* then at the end is a list of troubleshooting advice. If you encouter a problem that is not already listed, please consider adding it.&lt;br /&gt;
&lt;br /&gt;
The majority of your development will be done using a browser. You will probably on begin to use an emulator once you need to simulate a real mobile device.&lt;br /&gt;
&lt;br /&gt;
If you are just [[Mobile support for plugins|adding mobile support to plugins]], remember that most of your development can be done using the online pre-built version at https://mobileapp.moodledemo.net/ (with Chrome or Chromium). However, if you want to be able to to write [[Acceptance_testing_for_the_mobile_app|automated acceptance tests for the app]] then you need to follow this page at least as far as getting the ionic serve command to work on this page.&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
&lt;br /&gt;
Windows tip: ingore any use of the sudo command below. Just use the command without it. Most things work that way, and if they don&#039;t try in a Powershell window that you have opened with &#039;Run as administrator ...&#039;.&lt;br /&gt;
&lt;br /&gt;
===Install a browser for development===&lt;br /&gt;
&lt;br /&gt;
We recommend Chromium browser (Google Chrome open source version) https://download-chromium.appspot.com/&lt;br /&gt;
Please, read [[Moodle_Mobile_development_using_Chrome_or_Chromium]] for more information&lt;br /&gt;
&lt;br /&gt;
===Install git===&lt;br /&gt;
&lt;br /&gt;
https://git-scm.com/book/en/v2/Getting-Started-Installing-Git&lt;br /&gt;
&lt;br /&gt;
===Install Node.js===&lt;br /&gt;
&lt;br /&gt;
On Linux we recommend you use [https://github.com/creationix/nvm nvm] - this lets you switch Node versions, and makes the install a bit easier than the official installation route.&lt;br /&gt;
&lt;br /&gt;
 nvm install node&lt;br /&gt;
 nvm use 11.12.0 # Or whatever number nvm install reported.&lt;br /&gt;
&lt;br /&gt;
(Exact version is probably not critical. Moodle HQ devs report using both 8.12.x and 11.12.0 as of 2019-03-25.)&lt;br /&gt;
&lt;br /&gt;
On Windows we recommend you use https://github.com/coreybutler/nvm-windows. Same nvm commands as for Linux.&lt;br /&gt;
&lt;br /&gt;
On Mac users we recommend to install NodeJS via Macports.&lt;br /&gt;
&lt;br /&gt;
(It may seem simpler and easier to install directly from http://nodejs.org, but actually it seems to me more tricky to get that to work. If you have previously installed Node directly, and want to switch to nvm, you need to un-install node completely before installing nvm - or Google for trouble-shooting instructions, for example https://github.com/coreybutler/nvm-windows/issues/58#issuecomment-272608696.)&lt;br /&gt;
&lt;br /&gt;
===Install ionic===&lt;br /&gt;
 npm cache clean&lt;br /&gt;
 npm install -g cordova@8.1.2 ionic    # (If it throws an EACCESS error, run it again with sudo)&lt;br /&gt;
&lt;br /&gt;
===Install the npm required packages===&lt;br /&gt;
 sudo npm install -g gulp                      # (This will install gulp in a folder that should be in the PATH)&lt;br /&gt;
&lt;br /&gt;
===Windows only: Native build dependencies===&lt;br /&gt;
&lt;br /&gt;
node-gyp requires native build tools for your platform.  If you&#039;re developing on Mac or Linux, you&#039;ll probably have these already ([https://github.com/nodejs/node-gyp/blob/master/README.md refer to the docs if not]), on Windows, run the following command as administrator (in cmd or Powershell):&lt;br /&gt;
&lt;br /&gt;
 npm install --global --production windows-build-tools&lt;br /&gt;
&lt;br /&gt;
Warning! this installer can take a very, very long time to run. We were seeing it take hours. Literally. Be prepared to be very patient. Don&#039;t just make the natural assumption that it has crashed.&lt;br /&gt;
&lt;br /&gt;
===Mac only: Push notifications===&lt;br /&gt;
&lt;br /&gt;
Phonegap plugin push 1.9.0 requires CocoaPods to work on a Mac. The installation steps can be found in https://cocoapods.org/&lt;br /&gt;
&lt;br /&gt;
 sudo gem install cocoapods&lt;br /&gt;
 pod setup&lt;br /&gt;
&lt;br /&gt;
Please note that for compiling the app in Mac you need to open the &#039;&#039;&#039;Moodle.xcworkspace&#039;&#039;&#039; file, more information here: MOBILE-1970&lt;br /&gt;
&lt;br /&gt;
== Clone the app base code ==&lt;br /&gt;
&lt;br /&gt;
Clone the code base into a local directory in your computer.&lt;br /&gt;
It may be an idea to work from the integration branch rather than master.&lt;br /&gt;
 git clone https://github.com/moodlehq/moodleapp.git moodlemobiledirectory&lt;br /&gt;
 cd moodlemobiledirectory&lt;br /&gt;
 git checkout integration&lt;br /&gt;
&lt;br /&gt;
== Setup the environment ==&lt;br /&gt;
&lt;br /&gt;
Please, note that if you are creating a custom app with a custom URL scheme, you should edit the /package.json and /config.xml files and specify there your custom URL_SCHEME (replacing the existing value) and your [https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/INSTALLATION.md GCMPN SENDER_ID].&lt;br /&gt;
&lt;br /&gt;
The following command must be run in the project&#039;s root folder:&lt;br /&gt;
 npm run setup&lt;br /&gt;
&lt;br /&gt;
If this fails, you can see what it is doing by looking at the &#039;scripts&#039; section in package.json. At the moment it is doing &amp;lt;tt&amp;gt;npm install &amp;amp;&amp;amp; cordova prepare &amp;amp;&amp;amp; gulp&amp;lt;/tt&amp;gt;. That is, running three commands back-to-back, but only carrying on if the previous one succeeds completely. You can try running the three commands separately. If you do, &amp;lt;tt&amp;gt;ionic serve&amp;lt;/tt&amp;gt; (see below) may work, even if &amp;lt;tt&amp;gt;cordova prepare&amp;lt;/tt&amp;gt; gives errors. You only really need &amp;lt;tt&amp;gt;cordova prepare&amp;lt;/tt&amp;gt; to work if you are going to go on and build the app.&lt;br /&gt;
&lt;br /&gt;
== Open the app in the browser ==&lt;br /&gt;
First start Chromium via the command line using the custom parameters as is mentioned here: [[Moodle Mobile development using Chrome or Chromium]]&lt;br /&gt;
&lt;br /&gt;
and then, start the Ionic server:&lt;br /&gt;
&lt;br /&gt;
 ionic serve --browser chromium         # or chrome or whatever browser.&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to open any browser you should run:&lt;br /&gt;
&lt;br /&gt;
 ionic serve -b&lt;br /&gt;
&lt;br /&gt;
Congratulations! Now that you have got to the point where the &#039;ionic serve&#039; command works, you can start doing development on the app. You only need to read the rest of the page if you want to build packaged versions of the app.&lt;br /&gt;
&lt;br /&gt;
== Updating ionic and cordova ==&lt;br /&gt;
&lt;br /&gt;
 sudo npm update -g cordova&lt;br /&gt;
 sudo npm update -g ionic&lt;br /&gt;
&lt;br /&gt;
Update project platforms:&lt;br /&gt;
&lt;br /&gt;
 ionic cordova platform remove android&lt;br /&gt;
 ionic cordova platform remove ios&lt;br /&gt;
 ionic cordova platform add android&lt;br /&gt;
 ionic cordova platform add ios&lt;br /&gt;
&lt;br /&gt;
== Updating plugins ==&lt;br /&gt;
&lt;br /&gt;
 cordova plugin remove your_plugin_id&lt;br /&gt;
 cordova plugin add your_plugin_id&lt;br /&gt;
&lt;br /&gt;
== Building for Android and iOS ==&lt;br /&gt;
&lt;br /&gt;
Please see the guides below to be able to build for Android and iOS using the command line:&lt;br /&gt;
&lt;br /&gt;
Android: https://cordova.apache.org/docs/en/latest/guide/platforms/android/index.html&lt;br /&gt;
&lt;br /&gt;
iOS: https://cordova.apache.org/docs/en/latest/guide/platforms/ios/index.html&lt;br /&gt;
&lt;br /&gt;
If the build fails, please run &amp;lt;code&amp;gt;cordova requirements&amp;lt;/code&amp;gt; to check that you fulfilled all requirements for the platform.&lt;br /&gt;
&lt;br /&gt;
If you get errors while building, please see the Troubleshooting section below.&lt;br /&gt;
&lt;br /&gt;
If using &#039;&#039;&#039;Ubuntu&#039;&#039;&#039; you should install the packages: gradle and libgradle-android-plugin-java (and all its dependencies) to build.&lt;br /&gt;
&lt;br /&gt;
== Compiling using AOT ==&lt;br /&gt;
&lt;br /&gt;
Angular has 2 ways of compiling: JIT and AOT. Running &amp;quot;ionic serve&amp;quot; or &amp;quot;ionic build&amp;quot; compiles using JIT by default, which is faster to compile but the app takes longer to start.&lt;br /&gt;
&lt;br /&gt;
When building for release you should always compile using AOT, otherwise the app can take too long to start in some devices. The default AOT compiling causes some issues with the database activity and the [[Mobile support for plugins]], so you have to modify a couple of files in order to make this work.&lt;br /&gt;
&lt;br /&gt;
First you need to open the file: &#039;&#039;node_modules/@angular/platform-browser-dynamic/esm5/platform-browser-dynamic.js&#039;&#039;. Search the variable called &amp;quot;&#039;&#039;_NO_RESOURCE_LOADER&#039;&#039;&amp;quot;, you&#039;ll see it has a function named &amp;quot;&#039;&#039;get&#039;&#039;&amp;quot; with this line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
throw new Error(&amp;quot;No ResourceLoader implementation has been provided. Can&#039;t read the url \&amp;quot;&amp;quot; + url + &amp;quot;\&amp;quot;&amp;quot;);&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remove that line and put this code instead:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
        url = &#039;templates/&#039; + url;&lt;br /&gt;
&lt;br /&gt;
        var resolve;&lt;br /&gt;
        var reject;&lt;br /&gt;
        var promise = new Promise(function (res, rej) {&lt;br /&gt;
            resolve = res;&lt;br /&gt;
            reject = rej;&lt;br /&gt;
        });&lt;br /&gt;
        var xhr = new XMLHttpRequest();&lt;br /&gt;
        xhr.open(&#039;GET&#039;, url, true);&lt;br /&gt;
        xhr.responseType = &#039;text&#039;;&lt;br /&gt;
        xhr.onload = function () {&lt;br /&gt;
            // responseText is the old-school way of retrieving response (supported by IE8 &amp;amp; 9)&lt;br /&gt;
            // response/responseType properties were introduced in ResourceLoader Level2 spec (supported by IE10)&lt;br /&gt;
            var response = xhr.response || xhr.responseText;&lt;br /&gt;
            // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)&lt;br /&gt;
            var status = xhr.status === 1223 ? 204 : xhr.status;&lt;br /&gt;
            // fix status code when it is 0 (0 status is undocumented).&lt;br /&gt;
            // Occurs when accessing file resources or on Android 4.1 stock browser&lt;br /&gt;
            // while retrieving files from application cache.&lt;br /&gt;
            if (status === 0) {&lt;br /&gt;
                status = response ? 200 : 0;&lt;br /&gt;
            }&lt;br /&gt;
            if (200 &amp;lt;= status &amp;amp;&amp;amp; status &amp;lt;= 300) {&lt;br /&gt;
                resolve(response);&lt;br /&gt;
            }&lt;br /&gt;
            else {&lt;br /&gt;
                reject(&amp;quot;Failed to load &amp;quot; + url);&lt;br /&gt;
            }&lt;br /&gt;
        };&lt;br /&gt;
        xhr.onerror = function () { reject(&amp;quot;Failed to load &amp;quot; + url); };&lt;br /&gt;
        xhr.send();&lt;br /&gt;
        return promise;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We tried to replace the default loader with our own implementation, but we weren&#039;t able to make the compiler work so the only solution left was to modify the default one.&lt;br /&gt;
&lt;br /&gt;
Now you need to open the file: &#039;&#039;node_modules/@ionic/app-scripts/dist/util/config.js&#039;&#039;. In that file you need to remove the &#039;&#039;context.isProd&#039;&#039; condition from the options &#039;&#039;optimizeJs&#039;&#039;. So the final code for that part should be like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
    context.optimizeJs = [&lt;br /&gt;
        context.optimizeJs,&lt;br /&gt;
        hasArg(&#039;--optimizeJs&#039;)&lt;br /&gt;
    ].find(function (val) { return typeof val === &#039;boolean&#039;; });&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We want to compile in production mode but without optimizing Javascript because that breaks our plugins support. However, Ionic doesn&#039;t let you do that, so the only option is to do this change.&lt;br /&gt;
&lt;br /&gt;
With these changes done you can now compile using production mode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
npm run ionic:build -- --prod&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This command will generate the app files and put them inside &#039;&#039;www&#039;&#039; folder. If you now want to install that app in a real device you can run &amp;quot;&#039;&#039;cordova run android&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;cordova build ios&#039;&#039;&amp;quot; (please don&#039;t use &amp;quot;&#039;&#039;ionic cordova ...&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;ionic serve&#039;&#039;&amp;quot; because it will override your build files!).&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== Strange NPM errors ===&lt;br /&gt;
&lt;br /&gt;
To get more debug output from npm commands, see https://docs.npmjs.com/misc/config#shorthands-and-other-cli-niceties. In particular try adding &amp;lt;tt&amp;gt;--loglevel verbose&amp;lt;/tt&amp;gt; (or &amp;lt;tt&amp;gt;--loglevel info&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;--loglevel silly&amp;lt;/tt&amp;gt;) to the command-line.&lt;br /&gt;
&lt;br /&gt;
=== Error: libsass bindings not found. Try reinstalling node-sass? ===&lt;br /&gt;
&lt;br /&gt;
Please read: http://fettblog.eu/gulp-and-node4-first-aid/, alternatively you must be sure that you installed Node v0.12&lt;br /&gt;
&lt;br /&gt;
=== node-gyp\src\win_delay_load_hook.c(34): error C2373: &#039;__pfnDliNotifyHook2&#039;: redefinition; different type modifiers ===&lt;br /&gt;
&lt;br /&gt;
Try updating npm to the latest version using: &lt;br /&gt;
&lt;br /&gt;
  npm install -g npm@latest&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== com.android.dex.DexException: Multiple dex files define XXX ===&lt;br /&gt;
Open the file &#039;&#039;platforms/android/build.gradle&#039;&#039; and add this code at the end:&lt;br /&gt;
&lt;br /&gt;
  configurations {&lt;br /&gt;
      all*.exclude group: &#039;com.android.support&#039;, module: &#039;support-v4&#039;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Could not resolve all dependencies for configuration &#039;:_debugCompile&#039;. ===&lt;br /&gt;
Open the Android SDK Manager and make sure you have installed: Android Support Repository, Android Support Library, Google Play Services and Google Repository.&lt;br /&gt;
&lt;br /&gt;
=== Could not find com.android.support:support-v4:XXX ===&lt;br /&gt;
Open the file &#039;&#039;platforms/android/build.gradle&#039;&#039; and add this code at the end:&lt;br /&gt;
&lt;br /&gt;
  configurations.all {&lt;br /&gt;
      resolutionStrategy.force &#039;com.android.support:support-v4:24.0.0&#039;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== ERROR: In &amp;lt;declare-styleable&amp;gt; FontFamilyFont, unable to find attribute android:font  ===&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;platforms/android/build.gradle&#039;&#039; and add this code at the end:&lt;br /&gt;
&lt;br /&gt;
 android {&lt;br /&gt;
     compileSdkVersion 26&lt;br /&gt;
     buildToolsVersion &amp;quot;26.0.1&amp;quot;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Error: Could not find gradle wrapper within Android SDK. Might need to update your Android SDK.  ===&lt;br /&gt;
&lt;br /&gt;
1. Download Android studio - https://developer.android.com/studio/&lt;br /&gt;
&lt;br /&gt;
2. Copy the folder android-studio/plugins/android/lib/templates&lt;br /&gt;
&lt;br /&gt;
3. Paste in the folder android-sdk-folder/Sdk/tools&lt;br /&gt;
&lt;br /&gt;
=== Could not find com.android.support:support-v4:27.1.0 ===&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;platforms/android/build.gradle&#039;&#039; and configure like this:&lt;br /&gt;
&lt;br /&gt;
  allprojects {&lt;br /&gt;
      repositories {&lt;br /&gt;
          jcenter()&lt;br /&gt;
          maven {&lt;br /&gt;
              url &amp;quot;https://maven.google.com&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Error: not found: make ===&lt;br /&gt;
&lt;br /&gt;
If you see this error in Ubuntu, run &amp;lt;tt&amp;gt;sudo apt-get install build-essential&amp;lt;/tt&amp;gt; and retry.&lt;br /&gt;
&lt;br /&gt;
=== Current working directory is not a Cordova-based project. ===&lt;br /&gt;
&lt;br /&gt;
If you see this error during &amp;lt;tt&amp;gt;npm setup&amp;lt;/tt&amp;gt;, run &amp;lt;tt&amp;gt;mkdir www&amp;lt;/tt&amp;gt; and retry.&lt;br /&gt;
&lt;br /&gt;
=== ReferenceError: internalBinding is not defined ===&lt;br /&gt;
&lt;br /&gt;
This [https://stackoverflow.com/questions/53146394/node-app-fails-to-run-on-mojave-referenceerror-internalbinding-is-not-defined seems to be] an error with &#039;natives&#039; prior to 1.1.6. I fixed it using &amp;lt;tt&amp;gt;npm install natives@1.1.6&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== ReferenceError: internalBinding is not defined ===&lt;br /&gt;
&lt;br /&gt;
This [https://stackoverflow.com/questions/53146394/node-app-fails-to-run-on-mojave-referenceerror-internalbinding-is-not-defined seems to be] an error with &#039;natives&#039; prior to 1.1.6. I fixed it using &amp;lt;tt&amp;gt;npm install natives@1.1.6&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== npm update check failed===&lt;br /&gt;
&lt;br /&gt;
I got the error&lt;br /&gt;
&lt;br /&gt;
 │                   npm update check failed                   │&lt;br /&gt;
 │             Try running with sudo or get access             │ &lt;br /&gt;
 │            to the local update config store via             │&lt;br /&gt;
 │ sudo chown -R $USER:$(id -gn $USER) C:\Users\username\.config │&lt;br /&gt;
&lt;br /&gt;
on Windows because I installed too much as admin, and the suggested command does not work on Windows. The is to manually check the ownership of all the files in C:\Users\username\.config\configstore. In my case it was update-notifier-npm.json which got changed to be owned by Administrator.&lt;br /&gt;
&lt;br /&gt;
===Unhandled rejection Error: Command failed: C:\cygwin64\bin\git.EXE ...===&lt;br /&gt;
&lt;br /&gt;
You need to heed the advice at https://www.npmjs.com/package/npm#installing-on-cygwin. Cygwin users are not welcome in the Node world. However, you just need to ensure that Msysgit is on your windows path and that the cygwin bin folder is not. Then always use another shell like Powershell for your Moodle mobile development. (You don&#039;t need your Cygwin bin folder on the Windows path. It automatically gets added to the path when you lauch Cygwin bash.)&lt;br /&gt;
&lt;br /&gt;
===The product name change (&amp;lt;name&amp;gt; tag) in config.xml is not supported dynamically===&lt;br /&gt;
&lt;br /&gt;
According to Google, this happens when you create the iOS platform with a certain &amp;lt;name&amp;gt; and then you change that name in config.xml. It&#039;s weird that the installation process does that, it should create the platform with the right name unless it was changed manually.&lt;br /&gt;
&lt;br /&gt;
The solution seems to be removing and adding the iOS platform again:&lt;br /&gt;
&lt;br /&gt;
 ionic platform remove ios&lt;br /&gt;
 ionic platform add ios&lt;br /&gt;
&lt;br /&gt;
Note that this does not seem to prevent &amp;lt;tt&amp;gt;ionic --serve&amp;lt;/tt&amp;gt; from serving a working app if you run gulp after &amp;lt;tt&amp;gt;npm run setup&amp;lt;/tt&amp;gt; has failed with this error.&lt;br /&gt;
&lt;br /&gt;
===Failed to install &#039;cordova-plugin-file-transfer&#039;: CordovaError: Version of installed plugin: &amp;quot;cordova-plugin-file@4.3.3&amp;quot; does not satisfy dependency plugin requirement &amp;quot;cordova-plugin-file@&amp;gt;=5.0.0&amp;quot;.===&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;cordova-plugin-file&#039;&#039; version specified in config.xml is 6.0.1, for some reason the installation process installed a wrong version for that plugin. You can manually install the &#039;&#039;cordova-plugin-file&#039;&#039; plugin like this:&lt;br /&gt;
&lt;br /&gt;
 cordova plugin remove cordova-plugin-file&lt;br /&gt;
 cordova plugin add cordova-plugin-file@6.0.1&lt;br /&gt;
&lt;br /&gt;
Please notice that if there is any plugin installed that depends on &#039;&#039;cordova-plugin-file&#039;&#039; you&#039;ll have to remove and re-add them too.&lt;br /&gt;
&lt;br /&gt;
Note that this does not seem to prevent &amp;lt;tt&amp;gt;ionic --serve&amp;lt;/tt&amp;gt; from serving a working app if you run gulp after &amp;lt;tt&amp;gt;npm run setup&amp;lt;/tt&amp;gt; has failed with this error.&lt;br /&gt;
&lt;br /&gt;
===Cannot download &amp;quot;https://github.com/sass/node-sass/releases/download/v4.11.0/linux-x64-72_binding.node&amp;quot;===&lt;br /&gt;
Force using node 11, since node-sass for node 12 is not yet avalaible. See [https://tracker.moodle.org/browse/MOBILE-2999|MOBILE-2999] for more info.&lt;br /&gt;
&lt;br /&gt;
===Mac: linker code failed with exit code 1===&lt;br /&gt;
If you get this error when trying to build the Moodle app with XCode, some dependencies might not have installed correctly.&lt;br /&gt;
&lt;br /&gt;
Ensure you have followed the [https://docs.moodle.org/dev/Setting_up_your_development_environment_for_Moodle_Mobile_2#Mac_only:_Push_notifications| Mac only: Push notifications] steps above (particularly opening the .xcworkspace file rather than the .xcodeproj file). Then run the following:&lt;br /&gt;
&lt;br /&gt;
  npm run setup&lt;br /&gt;
  cd platforms/ios&lt;br /&gt;
  pod install&lt;br /&gt;
&lt;br /&gt;
Now try running the build again in XCode.&lt;br /&gt;
&lt;br /&gt;
===Windows: &amp;lt;code&amp;gt;npm run ionic:serve&amp;lt;/code&amp;gt; hangs after &amp;quot;Starting &#039;watch&#039;&amp;quot;===&lt;br /&gt;
This appears to have happened since the move to npx. Try running the npx commands generated by npm run directly in bash:&lt;br /&gt;
  npx gulp watch &amp;amp; npx ionic-app-scripts serve -b --devapp --address=0.0.0.0&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
http://ionicframework.com/docs/cli/&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=SCSS&amp;diff=57526</id>
		<title>SCSS</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=SCSS&amp;diff=57526"/>
		<updated>2020-05-21T13:52:47Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* Using in a theme */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.2}}&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
[https://en.wikipedia.org/wiki/Sass_(stylesheet_language) SCSS] or SASS Sass (Syntactically Awesome Stylesheets) is a style sheet language initially designed by Hampton Catlin and developed by Natalie Weizenbaum.  It&#039;s an advanced way of writing CSS that we use in some themes within Moodle.&lt;br /&gt;
&lt;br /&gt;
Browsers don&#039;t interpret it themselves, however, so SCSS files need to be converted into normal CSS before use.&lt;br /&gt;
&lt;br /&gt;
==Using in a theme==&lt;br /&gt;
&lt;br /&gt;
Moodle includes a built in SCSS compiler so to use SCSS in your theme, you only need to set the theme scss variable, in your theme&#039;s config.php file, to point to the main scss file for your theme:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Points to the file theme/themename/scss/main.scss.&lt;br /&gt;
$THEME-&amp;gt;scss = &#039;main&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Moodle will look for a file named &#039;main.scss&#039; in the &#039;theme/themename/scss&#039; folder and compile it. If you don&#039;t see the affect of your changes right away, try purging the caches or enabling theme designer mode.&lt;br /&gt;
&lt;br /&gt;
===Inheriting from a parent===&lt;br /&gt;
&lt;br /&gt;
Even if your theme is inheriting from a parent, the SCSS file itself will not inherit from anything, this is something you should do manually. For instance, if you want your SCSS file to include all of the SCSS code provided by &#039;&#039;theme_boost&#039;&#039;, usually to change the variables, you need to manually import the file like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
// In your theme&#039;s SCSS file&lt;br /&gt;
@import &amp;quot;../../boost/scss/moodle&amp;quot;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The path needs to be relative and not absolute. You would use it like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
$brand-primary:             #00acdf !default;&lt;br /&gt;
$my-color:                  #ff9900 !default;&lt;br /&gt;
&lt;br /&gt;
@import &amp;quot;../../boost/scss/moodle&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
    background-color: $my-color;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 NOTICE: At the moment, Moodle is using a PHP sass compiler which does not support &#039;@import url&#039; at-rule MDL-57875, which is a challenge when you wish to use bootswatch presets or Google fonts directly. so it is advise to use the OS level sass binary compiler (by installing libsass via &#039;yum install libsass&#039;, or other means) and then setting the admin $CFG-&amp;gt;pathtosassc to use the &#039;sassc&#039; binary.&lt;br /&gt;
&lt;br /&gt;
=== Advance usage ===&lt;br /&gt;
&lt;br /&gt;
==== On-the-fly injections ====&lt;br /&gt;
&lt;br /&gt;
The following settings allow themers to point to a function which will either prepend, or append, SCSS code to content gotten from &#039;&#039;$THEME-&amp;gt;scss&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// The content returned by this function is added before the main SCSS.&lt;br /&gt;
$THEME-&amp;gt;prescsscallback = &#039;theme_themename_scss_to_prepend&#039;;&lt;br /&gt;
&lt;br /&gt;
// The content returned by this function is added after the main SCSS.&lt;br /&gt;
$THEME-&amp;gt;extrascsscallback = &#039;theme_themename_scss_to_append&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The functions will receive the &#039;&#039;theme config&#039;&#039; as first argument.&lt;br /&gt;
&lt;br /&gt;
==== Dynamic $THEME-&amp;gt;scss ====&lt;br /&gt;
&lt;br /&gt;
Rather than giving the name of your SCSS file in &#039;&#039;$THEME-&amp;gt;scss&#039;&#039;, you can make it a function which returns the SCSS content.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$THEME-&amp;gt;scss = function($themeconfig) {&lt;br /&gt;
    $scsscontent = &#039;$my-var: red;&#039;;&lt;br /&gt;
    $scsscontent .= body { color: $my-var; }&#039;;&lt;br /&gt;
    return $scsscontent;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Example ==&lt;br /&gt;
&lt;br /&gt;
Moodle&#039;s Boost theme has rules written using SCSS.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* Using &#039;sassc&#039; binary sass compiler as an alternative to Moodle PHP sass compiler. MDL-61394 ($CFG-&amp;gt;pathtosassc)&lt;br /&gt;
* [http://sass-lang.com/ Sass official website]&lt;br /&gt;
&lt;br /&gt;
[[Category:Themes]]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Using_the_Moodle_App_in_a_browser&amp;diff=56333</id>
		<title>Using the Moodle App in a browser</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Using_the_Moodle_App_in_a_browser&amp;diff=56333"/>
		<updated>2019-08-22T09:32:09Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* Differences between Chromium and Google Chrome */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Chromium or Google Chrome browser are the recommended tools for Moodle Mobile development. But remember that if you are going to use functions provided by Phonegap you should use the Android SDK or iOs developer tools.&lt;br /&gt;
{{Moodle Mobile}}&lt;br /&gt;
{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
== Differences between Chromium and Google Chrome ==&lt;br /&gt;
&lt;br /&gt;
Google Chrome is the Chromium open source project built, packaged, and distributed by Google. We can say that Chromium is Google Chrome without the &amp;quot;Google&amp;quot; add-ons&lt;br /&gt;
&lt;br /&gt;
See https://code.google.com/p/chromium/wiki/ChromiumBrowserVsGoogleChrome for more information&lt;br /&gt;
&lt;br /&gt;
We recommend using Chromium instead Google Chrome&lt;br /&gt;
&lt;br /&gt;
== Advantages and disadvantages of using Chromium/Google Chrome ==&lt;br /&gt;
&lt;br /&gt;
Main advantages&lt;br /&gt;
* Quick development&lt;br /&gt;
* DOM inspector&lt;br /&gt;
* Network monitor&lt;br /&gt;
* Database (local storage) inspector&lt;br /&gt;
* Emulation options&lt;br /&gt;
&lt;br /&gt;
Disadvantages&lt;br /&gt;
* You can&#039;t test/develop using Phonegap APIs and Plugins&lt;br /&gt;
* If you use custom Phonegap code (including plugins) you will need to edit the mm.cordova.js for &amp;quot;emulate&amp;quot; those APIs in a browser&lt;br /&gt;
* You will always need to test in a real device and emulator prior to a production release&lt;br /&gt;
* You will need to verify that your CSS/layout works the same in an Android/iOs device&lt;br /&gt;
&lt;br /&gt;
== Installation == &lt;br /&gt;
&lt;br /&gt;
Install the “Chromium” browser just downloading it from https://download-chromium.appspot.com/&lt;br /&gt;
&lt;br /&gt;
In order to be able to access any domain via AJAX from the local file:/// protocol, you must run Chromium or Google Chrome in Unsafe mode adding this param:&lt;br /&gt;
&lt;br /&gt;
 --allow-file-access-from-files --disable-web-security --user-data-dir  --allow-running-insecure-content&lt;br /&gt;
&lt;br /&gt;
NOTE: Since Moodle 3.0 an onwards this shouldn&#039;t be necessary since the Web Service layer supports CORS requests.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: I strongly recommend you create a new link or application launch called &amp;quot;Chrome Unsafe&amp;quot; and use it only for testing the app. Better if you only use this browser for development&lt;br /&gt;
&lt;br /&gt;
How to open the browser:&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;Path to chrome\chrome.exe&amp;quot; --allow-file-access-from-files --disable-web-security --user-data-dir  --allow-running-insecure-content&lt;br /&gt;
&lt;br /&gt;
or in Mac (you can use Automator for creating an app bundle)&lt;br /&gt;
&lt;br /&gt;
 open -a &amp;quot;Google Chrome&amp;quot; --args --allow-file-access-from-files --disable-web-security --user-data-dir  --allow-running-insecure-content&lt;br /&gt;
&lt;br /&gt;
or for Chromium&lt;br /&gt;
&lt;br /&gt;
 open -a /Applications/Chromium.app --args --allow-file-access-from-files --disable-web-security --user-data-dir  --allow-running-insecure-content&lt;br /&gt;
&lt;br /&gt;
In Mac you can use Automator for creating a launching app&lt;br /&gt;
&lt;br /&gt;
Now, you can open the application running:&lt;br /&gt;
 ionic serve --browser chromium &lt;br /&gt;
it should open a new tab in the current Chromium instance with the app, (remember that you have to open first the Chromium so it&#039;s started with all the additional arguments).&lt;br /&gt;
&lt;br /&gt;
If the ionic serve command open a new browser window, you should copy the URL and then paste it in the Chromium instance you opened using the command shell.&lt;br /&gt;
&lt;br /&gt;
== Sites for testing ==&lt;br /&gt;
&lt;br /&gt;
You can test the application using existing sites configured to have the Mobile services enabled.&lt;br /&gt;
&lt;br /&gt;
Real test site: If you type &amp;quot;student&amp;quot; or &amp;quot;teacher&amp;quot; in the &amp;quot;Site URL&amp;quot; field and then tap on &amp;quot;Add site&amp;quot; the app will connect to http://school.demo.moodle.net. This site is reset every hour so you may find unexpected behaviors&lt;br /&gt;
&lt;br /&gt;
== Browser Settings ==&lt;br /&gt;
[[{{ns:file}}:moodlemobile_developer.png|thumb|300px]]&lt;br /&gt;
You should develop always with the &amp;quot;Developer tools&amp;quot; panel open, you can open that panel with F12 (cmd + alt + i in Mac) or &amp;quot;Developer tools&amp;quot; in the Tools menu.&lt;br /&gt;
&lt;br /&gt;
[[{{ns:file}}:moodlemobile_options.png|left]]  Once opened, click on the wheel top-right corner to see the General options, you must Disable the cache there (this setting will work only if you have the Developer tools panel open)&lt;br /&gt;
&lt;br /&gt;
You can dock or undock into a separate window the developer panel using the &amp;quot;Dock&amp;quot; option just at the right of the settings wheel, you can move the developer panel to your right and resize the main browser window to emulate a Mobile device&lt;br /&gt;
&lt;br /&gt;
== Inspecting and changing the DOM ==&lt;br /&gt;
&lt;br /&gt;
Using the &amp;quot;Elements&amp;quot; option you can manipulate the DOM:&lt;br /&gt;
&lt;br /&gt;
* Add/modify style to elements&lt;br /&gt;
* Delete elements&lt;br /&gt;
* Move elements&lt;br /&gt;
* Inject HTML code&lt;br /&gt;
&lt;br /&gt;
You have complete information here: https://developer.chrome.com/devtools/index&lt;br /&gt;
&lt;br /&gt;
And also a free interactive course here: https://www.codeschool.com/courses/discover-devtools&lt;br /&gt;
&lt;br /&gt;
== Monitor XHR (ajax) requests ==&lt;br /&gt;
[[{{ns:file}}:moodlemobile_network.png|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
With the network panel you can check and filter all the HTTP request send from the app to your Moodle server.&lt;br /&gt;
&lt;br /&gt;
You can preserve the log in the navigation, filter by type of requests and also see complete information of all the requests made to the server where is hosted your Moodle installation.&lt;br /&gt;
&lt;br /&gt;
You can also identify with resources takes long to load&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Console, log and errors ==&lt;br /&gt;
[[{{ns:file}}:moodlemobile_log.png|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The console panel is used for displaying the app log and errors, you can also configure Chrome for displaying there XHR requests&lt;br /&gt;
&lt;br /&gt;
In order to view the app log, you need first to enable Logging in the app (Options / Developers / Enable logging)&lt;br /&gt;
&lt;br /&gt;
You can use the console also for executing javascript commands, the console has access to the complete MM global object so you can call from there to core APIs functions of the app&lt;br /&gt;
&lt;br /&gt;
== Browsing the data base ==&lt;br /&gt;
[[{{ns:file}}:moodlemobile_localstorage.png|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The Mobile app stores information in the local HTML5 IndexedDB&lt;br /&gt;
&lt;br /&gt;
You can inspect the local database in the Resources tab.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File system ==&lt;br /&gt;
[[{{ns:file}}:moodlemobile_filesystem.png|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The first time you open the browser with the special flags you will see a couple of warnings, one asking you to accept to store information.&lt;br /&gt;
&lt;br /&gt;
You have to &amp;quot;Accept&amp;quot; that warning because it means that you will be able to emulate the device file system using the browser.&lt;br /&gt;
&lt;br /&gt;
For example, you will be able to download files to the browser storage (like the user profiles images, any resource in the course, etc..)&lt;br /&gt;
&lt;br /&gt;
You can browse the files stored in the application going to Settings &amp;gt; About and clicking the Filesystem root link.&lt;br /&gt;
&lt;br /&gt;
== Emulating devices ==&lt;br /&gt;
[[{{ns:file}}:moodlemobile_emulation.png|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
You can emulate mobile devices changing orientation, resolution, geo-location, touch events... It&#039;s not recommended to use de &amp;quot;Emulation Device&amp;quot; option because it just changes the user-agent and you could get some fails because of the jQuery Swipe library.&lt;br /&gt;
&lt;br /&gt;
The best option to test with the browser is just rescaling the window to get a new viewport size or use the screen settings in the &amp;quot;Emulation&amp;quot; screen.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
[https://developer.chrome.com/devtools/index Chrome Developer Tools help]&lt;br /&gt;
&lt;br /&gt;
[[Category: Mobile]]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Moodle_App_Customisation&amp;diff=56332</id>
		<title>Moodle App Customisation</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Moodle_App_Customisation&amp;diff=56332"/>
		<updated>2019-08-22T09:31:14Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* Configuring how the app should work via Moodle site settings */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle Mobile}}&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Moodle Mobile can be customized in different ways:&lt;br /&gt;
* Changing the app appearance for your site.&lt;br /&gt;
* Adding support in your plugin for the mobile app.&lt;br /&gt;
* Configuring how the app should work via Moodle site settings.&lt;br /&gt;
* Creating a custom application (with your custom name and icon) based on the Moodle Mobile code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With the first three options you are not required to compile or build your custom application, with the last one you are required to compile and publish your custom app in the stores.&lt;br /&gt;
&lt;br /&gt;
== Changing the app appearance for your site ==&lt;br /&gt;
&lt;br /&gt;
The Mobile app can retrieve your custom styles from your Moodle site. Since is an HTML5 app, you can apply safely CSS3 styles.&lt;br /&gt;
&lt;br /&gt;
In your Moodle installation go to Plugins / Web services / Mobile and enter in the mobilecssurl field a valid URL pointing to a CSS file containing your custom styles (theme).&lt;br /&gt;
&lt;br /&gt;
The CSS should be placed inside your Moodle installation (in your custom theme or inside a local plugin)&lt;br /&gt;
&lt;br /&gt;
Once the user is logged in the app, there is a periodical process that retrieves your remote CSS files for applying your custom styles into the app.&lt;br /&gt;
&lt;br /&gt;
Notice that on the first time a user opens the app, he will see the default &amp;quot;orange&amp;quot; style. Your custom styles will be applied once the user has added a site in the app.&lt;br /&gt;
&lt;br /&gt;
Notice also that styles comes from a site, there is no way for changing the initial Add site / Manage account pages styles.&lt;br /&gt;
&lt;br /&gt;
See [[Moodle Mobile Themes]] for additional information.&lt;br /&gt;
&lt;br /&gt;
== Mobile support for plugins ==&lt;br /&gt;
&lt;br /&gt;
You can create Moodle plugins to change how the app works, see [[Mobile support for plugins]] for complete information.&lt;br /&gt;
&lt;br /&gt;
== Creating a custom application ==&lt;br /&gt;
&lt;br /&gt;
See [[Moodle Mobile custom apps]] or [https://moodle.com/mobileapp/ The Branded Moodle Mobile App service] by Moodle HQ&lt;br /&gt;
&lt;br /&gt;
=== Building ===&lt;br /&gt;
&lt;br /&gt;
There are two options for building the app, aka compile it to different devices:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Phonegap Build&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[http://build.phonegap.com Phonegap Build] is an online service that lets you build the app automatically for different platforms. The main advantage is that it simplifies a lot the process of building.&lt;br /&gt;
&lt;br /&gt;
You just put your www/ folder in a github account and Phonegaps automatically builds the app for different platforms pulling your github code.&lt;br /&gt;
&lt;br /&gt;
Notice that the free version is for open repositories, but you can use the non-free version for private repositories.&lt;br /&gt;
&lt;br /&gt;
In order to build your app using Phonegap Build you need to place a special config.xml file into the www/ folder, see [https://github.com/moodlehq/moodlemobile-phonegapbuild/blob/master/config.xml the config.xml file for Moodle Mobile 2] (Please, note that this file is different from the one created by Ionic in the app root folder). Please follow the tutorial mentioned in [[Moodle Mobile custom apps]] to view all the steps required for building the app using Phonegap Build service.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ionic CLI&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
You can also build the app using the Ionic CLI tools, see: http://ionicframework.com/docs/cli/run.html&lt;br /&gt;
&lt;br /&gt;
== Configuring how the app should work via Moodle site settings ==&lt;br /&gt;
&lt;br /&gt;
The app can be configured via Moodle site settings. You can disable features, rename strings, add new elements to the main menu or change how log out works in the app. This requires Moodle 3.3 or the [[https://docs.moodle.org/en/Moodle_Mobile_additional_features Moodle Mobile additional features plugin]].&lt;br /&gt;
&lt;br /&gt;
Please refer to the Configuring the app from your site section in this page: https://docs.moodle.org/en/Moodle_Mobile_guide_for_admins&lt;br /&gt;
&lt;br /&gt;
== Testing and developing ==&lt;br /&gt;
&lt;br /&gt;
=== Chromium or Google Chrome browser ===&lt;br /&gt;
&lt;br /&gt;
See [[Moodle Mobile development using Chrome or Chromium]]&lt;br /&gt;
&lt;br /&gt;
== Moodle Desktop ==&lt;br /&gt;
&lt;br /&gt;
Refer to [[Moodle Desktop]]&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
[https://moodle.com/mobileapp/ Branded Moodle Mobile app service by Moodle HQ]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=XMLDB_defining_an_XML_structure&amp;diff=56169</id>
		<title>XMLDB defining an XML structure</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=XMLDB_defining_an_XML_structure&amp;diff=56169"/>
		<updated>2019-06-18T12:10:11Z</updated>

		<summary type="html">&lt;p&gt;Demsley: Minor grammatical error corrected&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[XMLDB Documentation|XMLDB Documentation]] &amp;gt; [[XMLDB roadmap|Roadmap]] &amp;gt; Defining one XML structure&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Justification ==&lt;br /&gt;
&lt;br /&gt;
Before Moodle 1.7, all the DB install and upgrade was developed twice (once to handle MySQL installations and another to handle PostgreSQL installations). This approach, although working, has caused some headaches in the past, mainly because it was really difficult to keep both lines of development 100% on sync. Some developers do they work against one RDBMS and it was complex to develop to the other one (two test environments, skills on both databases, slower development cycle...). And all this was happening with &#039;&#039;only&#039;&#039; two supported RDBMS!&lt;br /&gt;
&lt;br /&gt;
One of the main objectives of Moodle 1.7 is to extend the the number of supported RDBMS to other flavours (more exactly, to Oracle and MSSQL). And the old approach (one line of development for each DB) could become an absolute nightmare. &lt;br /&gt;
&lt;br /&gt;
Because of this we have planned to build one structure to define all the DB objects used by Moodle. This structure will provide the necessary level of abstraction to be shared by all the RDBMS systems, so the &amp;quot;multiple lines of development&amp;quot; explained in the previous paragraph will be out forever, giving us one robust and well defined way to handle DB objects independently of the underlying RDBMS being used.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
Initially all our best wishes were to use the [http://phplens.com/lens/adodb/docs-datadict.htm#xmlschema AdoDB XML Schema]. As Moodle is using ADOdb libraries to communicate with databases it sounded like the natural approach to solve the problem. But, finally, two reasons prevented us to use it:&lt;br /&gt;
&lt;br /&gt;
# Although working, it seems to be one feature in progress, with important changes/evolutions arriving at the time of write this document.&lt;br /&gt;
# Its lack of support for &amp;quot;prefixes&amp;quot; (one Moodle key feature, to allow multiple instances to run in the same server), would force us to create some awful tricks to generate the objects.&lt;br /&gt;
&lt;br /&gt;
So, finally, we decided to build our own XML files, with everything we need to define every object present in the DB.&lt;br /&gt;
&lt;br /&gt;
== The XMLDB editor ==&lt;br /&gt;
[[XMLDB_editor | Main article]]&lt;br /&gt;
&lt;br /&gt;
Although the XML is pretty simple to read (and to write), one of the major drawbacks was its easy and error-prone adoption by the developers. Also some problems with versioning systems getting crazy with XML files (thanks ML!) pointed us to the requirement to use one high-density format (it means, physically &#039;&#039;&#039;long lines&#039;&#039;&#039;) in our XML files. &lt;br /&gt;
&lt;br /&gt;
After some intense thoughts we decided to build one specialised editor for our XML format. This editor should be easy to use and provide support for all the objects present one Moodle DB. And it&#039;s done (and will support future enhancements easily, we hope).&lt;br /&gt;
&lt;br /&gt;
The XMLDB Editor makes the addition of tables/fields/keys/indexes practically a trivial task, allowing the developer to spend  the time coding and improving things instead of fighting against XML files and the errors caused by manual editing (of course, the developer is free to use such extra-time as desired, beers, dance, books, music...) ;-)&lt;br /&gt;
&lt;br /&gt;
All the new &#039;&#039;&#039;install.xml&#039;&#039;&#039; files, present under each &#039;&#039;&#039;db&#039;&#039;&#039; directory in Moodle can be edited (and we recommend it) with just some clicks and keystrokes. Those &#039;&#039;&#039;install.xml&#039;&#039;&#039; will contain all the info needed to generate the specific objects needed for each RDBMS supported. Obviously, such files, are the neutral replacement for all the *.sql files used until now.&lt;br /&gt;
&lt;br /&gt;
=== Launching ===&lt;br /&gt;
&lt;br /&gt;
Just login to your server as an administrator and, under the Development tab of the Administration Block, you&#039;ll see a new link pointing to the &amp;quot;XMLDB Editor&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
One &#039;&#039;&#039;important note&#039;&#039;&#039; is that, to be able to handle files properly, the web server needs write access to all those &amp;quot;db&amp;quot; directories where the &amp;quot;install.xml&amp;quot; files reside (and to the files themselves, of course). ;-)&lt;br /&gt;
&lt;br /&gt;
That&#039;s all!&lt;br /&gt;
&lt;br /&gt;
=== Use===&lt;br /&gt;
&lt;br /&gt;
We really think the XMLDB Editor is pretty easy to use, so here you won&#039;t see a complete guide to use it. We highly recommend you to play with it for a while, viewing how it works and how it modifies the &#039;&#039;&#039;install.xml&#039;&#039;&#039; files.&lt;br /&gt;
&lt;br /&gt;
It&#039;s organised in a top-botton structure, where you start &#039;&#039;&#039;loading&#039;&#039;&#039; (or &#039;&#039;&#039;creating&#039;&#039;&#039;) a new XMLDB file. Then, you can &#039;&#039;&#039;edit&#039;&#039;&#039; such file and its &#039;&#039;&#039;general structure&#039;&#039;&#039; will be showed. This structure have two type of elements, &#039;&#039;&#039;tables&#039;&#039;&#039; and &#039;&#039;&#039;statements&#039;&#039;&#039; and the XMLDB Editor allows you to &#039;&#039;&#039;add&#039;&#039;&#039;, &#039;&#039;&#039;edit&#039;&#039;&#039;, &#039;&#039;&#039;delete&#039;&#039;&#039;, and &#039;&#039;&#039;move&#039;&#039;&#039; them easily. Also, for initial creation of tables, one small but effective &#039;&#039;&#039;reverse-enginery&#039;&#039;&#039; tool has been developed (only under MySQL) allowing you to retrofit any table from the DB to the XMLDB Editor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note: If you can&#039;t click on the create links....&#039;&#039;&#039; you must first create the /db folder (as shown in the list, but it may not really exist) and then make sure it is writeable by the webserver&lt;br /&gt;
&lt;br /&gt;
While editing tables you will see their &#039;&#039;&#039;fields&#039;&#039;&#039;, &#039;&#039;&#039;keys&#039;&#039;&#039; and &#039;&#039;&#039;indexes&#039;&#039;&#039; and you&#039;ll be able to handle all them easily. Note that some fields can be no-editable. It uses to be because they are being used in some way (part of one key or index) and the idea is to warn you about that.&lt;br /&gt;
&lt;br /&gt;
Fields can be edited and you can specify their &#039;&#039;&#039;name&#039;&#039;&#039;, &#039;&#039;&#039;type&#039;&#039;&#039;, &#039;&#039;&#039;length&#039;&#039;&#039;, &#039;&#039;&#039;decimals&#039;&#039;&#039;, &#039;&#039;&#039;null-ability&#039;&#039;&#039;, &#039;&#039;&#039;defaults&#039;&#039;&#039; and so one. Exactly the same for both &#039;&#039;&#039;keys&#039;&#039;&#039; and &#039;&#039;&#039;indexes&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
While editing statements, you must think about them like &amp;quot;collections of sentences&amp;quot;. Once you select the &#039;&#039;&#039;type&#039;&#039;&#039; (only inserts are allowed for now) and &#039;&#039;&#039;table&#039;&#039;&#039; you are interested you&#039;ll be able to introduce the exact values easily, being able to &#039;&#039;&#039;duplicate&#039;&#039;&#039; them easily to gain some speed if you have a lot of sentences in your development. Sentences can be &#039;&#039;&#039;edited&#039;&#039;&#039; and &#039;&#039;&#039;deleted&#039;&#039;&#039; easily too.&lt;br /&gt;
&lt;br /&gt;
One interesting feature is that all the XMLDB Editor pages allow you to enter one &#039;&#039;&#039;comment&#039;&#039;&#039; about the item being modified (table, index, key, field, statement...). Use it at your entire needs, sure it helps other developers to understand a bit more the DB model.&lt;br /&gt;
&lt;br /&gt;
Please, don&#039;t forget to read and understand the next section, where we talk about &#039;&#039;&#039;some important guidelines&#039;&#039;&#039; to create and handle XMLDB files.&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
Apart of the [[Database| Database Structures guidelines]], some more conventions should be followed:&lt;br /&gt;
&lt;br /&gt;
# About names:&lt;br /&gt;
## All lowercase names (tables, indexes, keys and fields).&lt;br /&gt;
## Table names and field names must use only a-z, 0-9 and _ chars. Table names can be at most 28 characters long; column names at most 30 characters.&lt;br /&gt;
## Key and index names under the XMLDB Files must be formed by concatenating the name of the fields present in the key/index with the &#039;&amp;quot;-&amp;quot; (minus) character.&lt;br /&gt;
## Primary key always must be named &amp;quot;primary&amp;quot; (this is one exception to the previous convention).&lt;br /&gt;
## It&#039;s highly recommended to avoid [[XMLDB_reserved_words|reserved words]] completely. We know we have some of them now but they should be completely out for next releases.&lt;br /&gt;
# About NULLS&lt;br /&gt;
## Avoid creating all the fields as NOT NULL with the &#039;&#039;silly&#039;&#039; default value &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; (empty string). The underlying code used to create tables will handle it properly but the XMLDB structure must be REAL. Read more in the [[XMLDB_Problems#NOT_NULL_fields_using_a_DEFAULT_.27.27_clause|Problems Page]].&lt;br /&gt;
# About FOREIGN KEYS&lt;br /&gt;
## Under the tables of every XMLDB file, you must define the existing &#039;&#039;&#039;Foreign Keys&#039;&#039;&#039; (FK) properly. This will allow everybody to know a bit better the structure, allow to evolve to a better constrained system in the future and will provide the underlying code with the needed info to create the proper indexes. &lt;br /&gt;
## Note that, if you define any field combination as FK you won&#039;t have to create any index on that fields, the code will do it automatically! &lt;br /&gt;
## Respect Convention 1.3&lt;br /&gt;
# About UNIQUE KEYS&lt;br /&gt;
## Declare any fields as UNIQUE KEY (UK) only if they are going to be used as target for one FK. Create unique indexes instead.&lt;br /&gt;
## Respect Convention 1.3&lt;br /&gt;
&lt;br /&gt;
== One example: the assignment module ==&lt;br /&gt;
&lt;br /&gt;
Here we are going to examine the [http://cvs.moodle.org/moodle/mod/assignment/db/install.xml?view=markup current implementation of the XMLDB Schema for the assignment module] (a simple one). It has been completely generated with the XMLDB Editor but it&#039;s nice to know a bit more about the XML internals.&lt;br /&gt;
&lt;br /&gt;
As you can see the structure is pretty simple:&lt;br /&gt;
&lt;br /&gt;
* XMLDB&lt;br /&gt;
** TABLES, one or more, each one with&lt;br /&gt;
*** FIELDS&lt;br /&gt;
*** KEYS&lt;br /&gt;
*** INDEXES&lt;br /&gt;
** STATEMENTS, none or more, each one with&lt;br /&gt;
*** SENTENCES&lt;br /&gt;
&lt;br /&gt;
First of all you should note that all the elements contain the &#039;&#039;&#039;PREVIOUS&#039;&#039;&#039; and &#039;&#039;&#039;NEXT&#039;&#039;&#039; attributes. They allow us to keep everything ordered although it isn&#039;t meaningful at all from the RDBMS perspective. Also the &#039;&#039;&#039;COMMENT&#039;&#039;&#039; field is present everywhere to be used as desired.&lt;br /&gt;
&lt;br /&gt;
=== The TABLE element ===&lt;br /&gt;
&lt;br /&gt;
We can ignore the TABLE element, as it&#039;s simply one container for the internals (FIELDS, KEYS and INDEXES). Let&#039;s go to examine them a bit more:&lt;br /&gt;
&lt;br /&gt;
==== The FIELD element ====&lt;br /&gt;
&lt;br /&gt;
It maps with one field in the DB (obviously). For each field you can define its &#039;&#039;&#039;name&#039;&#039;&#039;, &#039;&#039;&#039;type&#039;&#039;&#039; (from a list of [[XMLDB column types|neutral types]]), &#039;&#039;&#039;length&#039;&#039;&#039;, &#039;&#039;&#039;decimals&#039;&#039;&#039; (for some types), &#039;&#039;&#039;notnull&#039;&#039;&#039; (true/false), &#039;&#039;&#039;unsigned&#039;&#039;&#039; (true/false), &#039;&#039;&#039;sequence&#039;&#039;&#039; (if it&#039;s autonumeric or serial, true/false) and &#039;&#039;&#039;default&#039;&#039;&#039; (to assign a default value).&lt;br /&gt;
&lt;br /&gt;
So, in our example, we have two tables, assignment and assignment_submissions, each one with its own fields, defining all the information related above. Please note that naming conventions are followed.&lt;br /&gt;
&lt;br /&gt;
==== The KEY element ====&lt;br /&gt;
&lt;br /&gt;
Here is where all the PRIMARY KEYS (PK), UNIQUE KEYS (UK) and FOREIGN KEYS (FK) will be defined. For each key we define its &#039;&#039;&#039;name&#039;&#039;&#039;, &#039;&#039;&#039;type&#039;&#039;&#039;, &#039;&#039;&#039;fields&#039;&#039;&#039; (that belongs to it) and optionally (if the key is one FK) the target &#039;&#039;&#039;reftable&#039;&#039;&#039; and &#039;&#039;&#039;reffields&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
In our example, the assignment table has one (mandatory!) PK (called, &amp;quot;primary&amp;quot;, rules are rules) built with the &amp;quot;id&amp;quot; field. &lt;br /&gt;
&lt;br /&gt;
The other table, the &amp;quot;assignment_submissions&amp;quot; one, also has its PK (called &amp;quot;primary&amp;quot; once more) and one FK, with the field &amp;quot;assignment&amp;quot; pointing to the field &amp;quot;id&amp;quot; of the table &amp;quot;assignment&amp;quot;. Note that the FK follows the name conventions and its name is, simply, the name of the fields being part of it (&amp;quot;assignment&amp;quot;). Also, the FK has as target to one PK of the same module.&lt;br /&gt;
&lt;br /&gt;
Finally, note that there isn&#039;t any index created for all these keys. Moodle will generate them automatically when the table is created. All the keys will have their corresponding index. Point. ;-)&lt;br /&gt;
&lt;br /&gt;
==== The INDEX element ====&lt;br /&gt;
&lt;br /&gt;
Where all the indexes will be defined. For each index you can define its &#039;&#039;&#039;name&#039;&#039;&#039;, &#039;&#039;&#039;unique&#039;&#039;&#039; (true/false) and the &#039;&#039;&#039;fields&#039;&#039;&#039; (as a comma-separated string) that it comprises. Please note that naming conventions are followed.&lt;br /&gt;
&lt;br /&gt;
Also, some &amp;quot;obvious index&amp;quot;, like the one based in the &amp;quot;assignment&amp;quot; field of the &amp;quot;assignment_submissions&amp;quot; table doesn&#039;t exist. Yes, you know why: Because such column has been defined as a FK and the index will be automatically created (see previous section).&lt;br /&gt;
&lt;br /&gt;
=== The STATEMENT element ===&lt;br /&gt;
&lt;br /&gt;
This is the other &#039;&#039;&#039;big container&#039;&#039;&#039; in the XMLDB Schema (at the same level as the &#039;&#039;&#039;TABLES&#039;&#039;&#039; one) and we can define its &#039;&#039;&#039;name&#039;&#039;&#039;, &#039;&#039;&#039;type&#039;&#039;&#039; (only insert allowed for now) and &#039;&#039;&#039;table&#039;&#039;&#039; (against the sentences will be executed).&lt;br /&gt;
&lt;br /&gt;
Every statement is a collection of &#039;&#039;&#039;sentences&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
==== The SENTENCE element ====&lt;br /&gt;
&lt;br /&gt;
Each sentence implies one simple action to be performed against the DB and it can be defined as the &amp;quot;missing part of the SQL statement&amp;quot;. In our example, we have one statement, of type &amp;quot;insert&amp;quot; on table &amp;quot;log_display&amp;quot;. With this Moodle knows the initial part of the sentence, i.e:&lt;br /&gt;
&lt;br /&gt;
 INSERT INTO log_display &lt;br /&gt;
&lt;br /&gt;
and then the text will be added to create this:&lt;br /&gt;
&lt;br /&gt;
 INSERT INTO log_display &lt;br /&gt;
   (module, action, mtable, field) &lt;br /&gt;
 VALUES &lt;br /&gt;
   (&#039;assignment&#039;, &#039;view&#039;, &#039;assignment&#039;, &#039;name&#039;)&lt;br /&gt;
&lt;br /&gt;
There is one important trick when handling sentences, although they aren&#039;t in the assignment example. Take a look to the [http://cvs.moodle.org/moodle/lib/db/install.xml?view=co Core Tables XML Schema] (it&#039;s a huge one!). If you go near the end, to the statements section, you will see some sentences like this:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;SENTENCE TEXT=&amp;quot;....VALUES (&#039;user&#039;, &#039;view&#039;, &#039;user&#039;, &#039;CONCAT(firstname,&amp;quot; &amp;quot;,lastname)&#039;)&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Such &amp;quot;CONCAT&amp;quot; function isn&#039;t standard at all (only MySQL supports it), but don&#039;t worry, we&#039;ll transform it to the correct concatenation operators for other RDBMS. Just be sure to use the syntax showed above.&lt;br /&gt;
&lt;br /&gt;
== DTD and XML schema ==&lt;br /&gt;
&lt;br /&gt;
Not sure if this will be usable for somebody but here you can find one [http://cvs.moodle.org/moodle/lib/xmldb/xmldb.dtd?view=co automatically generated DTD] for the XMLDB files. Also one [http://cvs.moodle.org/moodle/lib/xmldb/xmldb.xsd?view=co automatically generated XML Schema] is available. Any improvement/fix to them will be welcome!&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[XMLB List of files to create|List of files to create]]: The list of files to be created from scratch. Used to follow the progress.&lt;br /&gt;
* http://www.hitsw.com/xml_utilites/: One online XML-DTD-Schema converter.&lt;br /&gt;
&lt;br /&gt;
[[Category:XMLDB]]&lt;br /&gt;
[[Category:DB]]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File_API_internals&amp;diff=53532</id>
		<title>File API internals</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File_API_internals&amp;diff=53532"/>
		<updated>2017-12-19T13:28:11Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* File storage on disk */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = File API&lt;br /&gt;
|state = Implemented&lt;br /&gt;
|tracker = MDL-14589&lt;br /&gt;
|discussion = n/a&lt;br /&gt;
|assignee = [[User:Petr Škoda (škoďák)|Petr Škoda (škoďák)]]&lt;br /&gt;
}}&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
==Objectives==&lt;br /&gt;
&lt;br /&gt;
The goals of the new File API are: &lt;br /&gt;
&lt;br /&gt;
* allow files to be stored within Moodle, as part of the content (as we do now).&lt;br /&gt;
* use a consistent and flexible approach for all file handling throughout Moodle.&lt;br /&gt;
* give modules control over which users can access a file, using capabilities and other local rules.&lt;br /&gt;
* make it easy to determine which parts of Moodle use which files, to simplify operations like backup and restore.&lt;br /&gt;
* track where files originally came from.&lt;br /&gt;
* avoid redundant storage, when the same file is used twice.&lt;br /&gt;
* fully support Unicode file names, irrespective of the capabilities of the underlying file system.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The File API is a set of core interfaces to allow the rest of Moodle to store, serve and manage files. It applies only to files that are part of the Moodle site&#039;s content. It is not used for internal files, such as those in the following subdirectories of dataroot: temp, lang, cache, environment, filter, search, sessions, upgradelogs, ...&lt;br /&gt;
&lt;br /&gt;
To learn how to use File API, please visit [[Using the File API]].&lt;br /&gt;
&lt;br /&gt;
The API can be subdivided into the following parts:&lt;br /&gt;
; File storage&lt;br /&gt;
: Low level file storage without access control information. Stores the content of files on disc, with metadata in associated database tables. &lt;br /&gt;
; File serving&lt;br /&gt;
: Lets users accessing a Moodle site get the files (file.php, draftfile.php, pluginfile.php, userfile.php, etc.)&lt;br /&gt;
:* Serve the files on request&lt;br /&gt;
:* with appropriate security checks&lt;br /&gt;
; File related user interfaces&lt;br /&gt;
: Provides the interface for (lib/form/file.php, filemanager.php, filepicker.php and files/index.php, draftfiles.php)&lt;br /&gt;
:* Form elements allowing users to select a file using the file picker, and have it stored within Moodle.&lt;br /&gt;
:* UI for users to manage their files, replacing the old course files UI&lt;br /&gt;
; File browsing API&lt;br /&gt;
: Allows code to browse and optionally manipulate the file areas&lt;br /&gt;
:* find information about available files in each area.&lt;br /&gt;
:* print links to files.&lt;br /&gt;
:* optionally move/rename/copy/delete/etc.&lt;br /&gt;
&lt;br /&gt;
== File API internals ==&lt;br /&gt;
&lt;br /&gt;
=== File storage on disk ===&lt;br /&gt;
&lt;br /&gt;
Files are stored in $CFG-&amp;gt;dataroot (also known as moodledata) in the filedir subfolder.&lt;br /&gt;
&lt;br /&gt;
Files are stored according to the SHA1 hash of their content. This means each file with particular contents is stored once, irrespective of how many times it is included in different places, even if it is referred to by different names. (This idea comes from the git version control system.) To relate a file on disc to a user-comprehensible path or filename, you need to use the &#039;&#039;files&#039;&#039; database table. See the next section.&lt;br /&gt;
&lt;br /&gt;
Suppose a file has SHA1 hash 081371cb102fa559e81993fddc230c79205232ce in the files table contenthash field. Then it will be stored in on disc as moodledata/filedir/08/13/081371cb102fa559e81993fddc230c79205232ce.&lt;br /&gt;
&lt;br /&gt;
This means Moodle can not store two files with the same SHA1 hash, luckily it is extremely unlikely that this would ever happen. Technically it is also possible to implement reliable collision tests (with some performance cost), for now we just test file lengths in addition to SHA1 hash.&lt;br /&gt;
&lt;br /&gt;
As files on-disk are named with their sha1 hash, there is a simple way of validating files have not been corrupted since upload by using the &#039;sha1sum&#039; command available in most GNU/Linux distributions. &lt;br /&gt;
In the below example, the user changed directory to the known location of a file on disk (sourced by finding the contenthash of the relevant file in the mdl_files database table). &lt;br /&gt;
Then, in this directory the &#039;sha1sum&#039; command is issued with the file that you wish to hash the content of. Returned from the command is the hash of the content (on the left) and the file name (on the right). &lt;br /&gt;
&lt;br /&gt;
Where a file is NOT corrupted after upload, these two strings will match. &lt;br /&gt;
&lt;br /&gt;
  $ cd /moodlepath/moodledata/filedir/1d/df/&lt;br /&gt;
  $ sha1sum 1ddf5b375fcb74929cdd7efda4f47efc61414edf&lt;br /&gt;
  1ddf5b375fcb74929cdd7efda4f47efc61414edf  1ddf5b375fcb74929cdd7efda4f47efc61414edf&lt;br /&gt;
&lt;br /&gt;
Where a file IS corrupted after upload, these will differ:&lt;br /&gt;
&lt;br /&gt;
  $ cd /moodlepath/moodledata/filedir/42/32/&lt;br /&gt;
  $ sha1sum 42327aac8ce5741f51f42be298fa63686fe81b7a&lt;br /&gt;
  9442188152c02f65267103d78167d122c87002cd  42327aac8ce5741f51f42be298fa63686fe81b7a&lt;br /&gt;
This is a very handy trick as in the case of any disk corruption (shared storage issues, hard drive issues, disk sector issues etc) the corrupted files can be detected without resorting to manually comparing to previous backups.&lt;br /&gt;
&lt;br /&gt;
=== Files table ===&lt;br /&gt;
&lt;br /&gt;
This table contains one entry for each usage of a file. Enough information is kept here so that the file can be fully identified and retrieved again if necessary. It is necessary because some databases have hard limit on index size.&lt;br /&gt;
&lt;br /&gt;
If, for example, the same image is used in a user&#039;s profile, and a forum post, then there will be two rows in this table, one for each use of the file, and Moodle will treat the two as separate files, even though the file is only stored once on disc.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
| int(10)  &lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this file.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;contenthash&#039;&#039;&#039;&lt;br /&gt;
| varchar(40)&lt;br /&gt;
|  &lt;br /&gt;
| The sha1 hash of content.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;pathnamehash&#039;&#039;&#039;&lt;br /&gt;
| varchar(40)&lt;br /&gt;
| &lt;br /&gt;
| The sha1 hash of &amp;quot;/contextid/component/filearea/itemid/filepath/filename.ext&amp;quot; - prevents file duplicates and allows fast lookup.  It is necessary because some databases have hard limit on index size.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;contextid&#039;&#039;&#039; &lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| The context id defined in context table - identifies the instance of plugin owning the file.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;component&#039;&#039;&#039;&lt;br /&gt;
| varchar(50)&lt;br /&gt;
|&lt;br /&gt;
| Like &amp;quot;mod_forum&amp;quot;, &amp;quot;course&amp;quot;, &amp;quot;mod_assignment&amp;quot;, &amp;quot;backup&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;filearea&#039;&#039;&#039;&lt;br /&gt;
| varchar(50)&lt;br /&gt;
|&lt;br /&gt;
| Like &amp;quot;submissions&amp;quot;, &amp;quot;intro&amp;quot; and &amp;quot;content&amp;quot; (images and swf linked from summaries), etc.; &amp;quot;blogs&amp;quot; and &amp;quot;userfiles&amp;quot; are special case that live at the system context.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;itemid&#039;&#039;&#039;&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| Some plugin specific item id (eg. forum post, blog entry or assignment submission or user id for user files)&lt;br /&gt;
|-&lt;br /&gt;
| filepath&lt;br /&gt;
| text&lt;br /&gt;
| &lt;br /&gt;
| relative path to file from module content root, useful in Scorm and Resource mod - most of the mods do not need this&lt;br /&gt;
|-&lt;br /&gt;
| filename&lt;br /&gt;
| varchar(255)&lt;br /&gt;
| &lt;br /&gt;
| The full Unicode name of this file (case sensitive)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;userid&#039;&#039;&#039;&lt;br /&gt;
| int(10)  &lt;br /&gt;
| NULL&lt;br /&gt;
| (optional)  Almost always this is the user that created the file, although some modules may choose to use this field for other purposes.&lt;br /&gt;
|-&lt;br /&gt;
| filesize&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| size of file - bytes&lt;br /&gt;
|-&lt;br /&gt;
| mimetype&lt;br /&gt;
| varchar(100)&lt;br /&gt;
| NULL&lt;br /&gt;
| type of file&lt;br /&gt;
|-&lt;br /&gt;
| status&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| general file status flag - will be used for lost or infected files&lt;br /&gt;
|-&lt;br /&gt;
| source&lt;br /&gt;
| text&lt;br /&gt;
| &lt;br /&gt;
| file source - usually url&lt;br /&gt;
|-&lt;br /&gt;
| author&lt;br /&gt;
| varchar(255)&lt;br /&gt;
|&lt;br /&gt;
| original author of file, used when importing from other systems&lt;br /&gt;
|-&lt;br /&gt;
| license&lt;br /&gt;
| varchar(255)&lt;br /&gt;
|&lt;br /&gt;
| license type, empty means site default&lt;br /&gt;
|-&lt;br /&gt;
| timecreated&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| The time this file was created&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| The last time the file was last modified&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Indexes:&lt;br /&gt;
* non-unique index on (contextid, component, filearea, itemid)&lt;br /&gt;
* non-unique index on (contenthash)&lt;br /&gt;
* unique index on (pathnamehash).&lt;br /&gt;
&lt;br /&gt;
The plugin type does not need to be specified because it can be derived from the context. Items like blog that do not have their own context will use their own file area inside a suitable context. In this case, the user context.&lt;br /&gt;
&lt;br /&gt;
Entries with filename = &#039;.&#039; represent directories. Directory entries like this are created automatically when a file is added within them.&lt;br /&gt;
&lt;br /&gt;
Note: &#039;files&#039; plural is used even thought that goes against the [[Database|coding guidelines]] because &#039;file&#039; is a reserved word in some SQL dialects.&lt;br /&gt;
&lt;br /&gt;
===Implementation of basic operations===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Each plugin may directly access only files in own context and areas!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Low level access API is defined in &#039;&#039;file_storage&#039;&#039; class which is obtained from &amp;lt;code&amp;gt;get_file_storage()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
====Storing a file====&lt;br /&gt;
&lt;br /&gt;
# Calculate the SHA1 hash of the file contents.&lt;br /&gt;
# Check if a file with this SHA1 hash already exists on disc in file directory or file trash. If not, store the file there.&lt;br /&gt;
# Add the record for this file to the files table using the low level address&lt;br /&gt;
&lt;br /&gt;
====Reading a file====&lt;br /&gt;
&lt;br /&gt;
# Fetch the record (which includes the SHA1 hash) for the file you want from the files table. You can fetch either all area files or quickly get one file with a specific contenthash.&lt;br /&gt;
# Retrieve the contents using the SHA1 hash from the file directory.&lt;br /&gt;
&lt;br /&gt;
====Deleting a file====&lt;br /&gt;
&lt;br /&gt;
# Delete the record from the files table.&lt;br /&gt;
# Verify if some other file is still needing the content, if not move the content file into file trash&lt;br /&gt;
# Later, admin/cron.php deletes content files from trash directory&lt;br /&gt;
&lt;br /&gt;
== File serving ==&lt;br /&gt;
&lt;br /&gt;
Deals with serving of files - browser requests file, Moodle sends it back. We have three main files. It is important to setup slasharguments on server properly (file.php/some/thing/xxx.jpg), any content that relies on relative links can not work without it (scorm, uploaded html pages, etc.).&lt;br /&gt;
&lt;br /&gt;
=== legacy file.php ===&lt;br /&gt;
&lt;br /&gt;
Serves legacy course files, the file name and parameter structure is critical for backwards compatibility of existing course content.&lt;br /&gt;
&lt;br /&gt;
 /file.php/courseid/dir/dir/filename.ext&lt;br /&gt;
&lt;br /&gt;
Internally the files are stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;$coursecontextid, &#039;component;=&amp;gt;&#039;course&#039;, &#039;filearea&#039;=&amp;gt;&#039;legacy&#039;, &#039;itemid&#039;=&amp;gt;0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The legacy course files are completely disabled in all new courses created in 2.0. The major problem here is to how to educate our users that they can not make huge piles of files in each course any more.&lt;br /&gt;
&lt;br /&gt;
=== pluginfile.php ===&lt;br /&gt;
All plugins should use this script to serve all files.&lt;br /&gt;
* plugins decide about access control&lt;br /&gt;
* optional XSS protection - student submitted files must not be served with normal headers, we have to force download instead; ideally there should be second wwwroot for serving of untrusted files&lt;br /&gt;
* links to these files are constructed on the fly from the relative links stored in database, this means that plugin may link only own files&lt;br /&gt;
&lt;br /&gt;
Absolute file links need to be rewritten if html editing allowed in plugin. The links are stored internally as relative links. Before editing or display the internal link representation is converted to absolute links using simple str_replace() @@thipluginlink/summary@@/image.jpg --&amp;gt; /pluginfile.php/assignmentcontextid/intro/image.jpg, it is converted back to internal links before saving.&lt;br /&gt;
&lt;br /&gt;
Script parameters are virtual file names, in most cases the parameters match the low level file storage, but they do not have to:&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/contextid/areaname/arbitrary/params/or/dirs/filename.ext&lt;br /&gt;
&lt;br /&gt;
pluginfile.php detects the type of plugin from context table, fetches basic info (like $course or $cm if appropriate) and calls plugin function (or later method) which does the access control and finally sends the file to user. &#039;&#039;areaname&#039;&#039; separates files by type and divides the context into several subtrees - for example &#039;&#039;summary&#039;&#039; files (images used in module intros), post attachments, etc.&lt;br /&gt;
&lt;br /&gt;
==== Assignment example ====&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/mod_assignment/intro/someimage.jpg&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/mod_assignment/submission/submissionid/attachmentname.ext&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/mod_assignment/allsubmissions/groupid/allsubmissionfiles.zip&lt;br /&gt;
&lt;br /&gt;
The last line example of virtual file that should created on the fly, it is not implemented yet.&lt;br /&gt;
&lt;br /&gt;
====scorm example====&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/scormcontextid/mod_scorm/intro/someimage.jpg&lt;br /&gt;
 /pluginfile.php/scormcontextid/mod_scorm/content/revisionnumber/dir/somescormfile.js&lt;br /&gt;
&lt;br /&gt;
The revision counter is incremented when any file changes in order to prevent caching problems.&lt;br /&gt;
&lt;br /&gt;
====quiz example====&lt;br /&gt;
&lt;br /&gt;
 pluginfile.php/quizcontextid/mod_quiz/intro/niceimage.jpg&lt;br /&gt;
&lt;br /&gt;
====questions example====&lt;br /&gt;
&lt;br /&gt;
This section was out of date. See [[File_storage_conversion_Quiz_and_Questions]] for the latest thinking.&lt;br /&gt;
&lt;br /&gt;
====blog example====&lt;br /&gt;
Blog entries or notes in general do not have context id (because they live in system context, SYSCONTEXTID below is the id of system context).&lt;br /&gt;
The note attachments are always served with XSS protection on, ideally we should use separate wwwroot for this. Access control can be hardcoded.&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/SYSCONTEXTID/blog/attachment/blogentryid/attachmentname.ext&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;SYSCONTEXTID, &#039;component&#039;=&amp;gt;&#039;blog&#039;, &#039;filearea&#039;=&amp;gt;&#039;attachment&#039;, &#039;itemid&#039;=&amp;gt;$blogentryid)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/SYSCONTEXTID/blog/post/blogentryid/embeddedimage.ext&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;SYSCONTEXTID, &#039;component&#039;=&amp;gt;&#039;blog&#039;, &#039;filearea&#039;=&amp;gt;&#039;post&#039;, &#039;itemid&#039;=&amp;gt;$blogentryid)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Temporary files ===&lt;br /&gt;
Temporary files are usually used during the lifetime of one script only.&lt;br /&gt;
uses:&lt;br /&gt;
* exports&lt;br /&gt;
* imports&lt;br /&gt;
* processing by executable files (latex, mimetex)&lt;br /&gt;
&lt;br /&gt;
These files should never use utf-8 file names.&lt;br /&gt;
&lt;br /&gt;
=== Legacy file storage and serving ===&lt;br /&gt;
Going to use good-old separate directories in $CFG-&amp;gt;dataroot.&lt;br /&gt;
&lt;br /&gt;
file serving and storage:&lt;br /&gt;
# user avatars - user/pix.php&lt;br /&gt;
# group avatars - user/pixgroup.php&lt;br /&gt;
# tex, algebra - filter/tex/* and filter/algebra/*&lt;br /&gt;
# rss cache (?full rss rewrite soon?) - backwards compatibility only rss/file.php&lt;br /&gt;
&lt;br /&gt;
only storage:&lt;br /&gt;
#sessions&lt;br /&gt;
&lt;br /&gt;
== File browsing API ==&lt;br /&gt;
&lt;br /&gt;
This is what other parts of Moodle use to access files that they do not own.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Class: file_browser ===&lt;br /&gt;
&lt;br /&gt;
=== Class: file_info and subclasses ===&lt;br /&gt;
&lt;br /&gt;
== File related user interfaces ==&lt;br /&gt;
&lt;br /&gt;
All files are obtained through from the file repositories.&lt;br /&gt;
&lt;br /&gt;
=== Formslib fields ===&lt;br /&gt;
* file picker&lt;br /&gt;
* file manager&lt;br /&gt;
* file upload (obsolete, do not use)&lt;br /&gt;
&lt;br /&gt;
=== Integration with the HTML editor ===&lt;br /&gt;
&lt;br /&gt;
Each instance of the HTML editor can be told to store related files in a particular file area.&lt;br /&gt;
&lt;br /&gt;
During editing, files are stored in a draft files area. Then when the form is submitted they are moved into the real file area.&lt;br /&gt;
&lt;br /&gt;
Files are selected using the repository file picker.&lt;br /&gt;
&lt;br /&gt;
=== Legacy file manager ===&lt;br /&gt;
&lt;br /&gt;
Available only for legacy reasons. It is not supposed to be used.&lt;br /&gt;
&lt;br /&gt;
All the contexts, file areas and files now form a single huge tree structure, although each user only has access to certain parts of that tree. The file manager (files/index.php) allow users to browse this tree, and manage files within it, according to the level of permissions they have.&lt;br /&gt;
&lt;br /&gt;
Single pane file manager is hard to implement without drag &amp;amp; drop which is notoriously problematic in web based applications. I propose to implement a two pane commander-style file manager. Two pane manager allows you to easily copy/move files between two different contexts (ex: courses).&lt;br /&gt;
&lt;br /&gt;
File manager must not interact directly with filesystem API, instead each module should return traversable tree of files and directories with both real and localised names (localised names are needed for dirs like backupdata).&lt;br /&gt;
&lt;br /&gt;
== Backwards compatibility ==&lt;br /&gt;
&lt;br /&gt;
=== Content backwards compatibility ===&lt;br /&gt;
&lt;br /&gt;
This should be preserved as much as possible. This will involve rewriting links in content during the upgrade to 2.0. &lt;br /&gt;
&lt;br /&gt;
Some new features (like resource sharing - if implemented) may not work with existing data that still uses files from course files area.&lt;br /&gt;
&lt;br /&gt;
There might be a breakage of links due to special characters stripping in uploaded files which will not match the links in uploaded html files any more. This should not be very common I hope.&lt;br /&gt;
&lt;br /&gt;
===Code backwards compatibility===&lt;br /&gt;
&lt;br /&gt;
Other Moodle code (for example plugins) will have to be converted to the new APIs. See [[Using_the_file_API]] for guidance.&lt;br /&gt;
&lt;br /&gt;
It is not possible to provide backwards-compatibility here. For example, the old $CFG-&amp;gt;dataroot/$courseid/ will no longer exist, and there is no way to emulate that, so we won&#039;t try.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Upgrade and migration ==&lt;br /&gt;
&lt;br /&gt;
When a site is upgraded to Moodle 2.0, all the files in moodledata will have to be migrated. This is going to be a pain, like DML/DDL was :-(&lt;br /&gt;
&lt;br /&gt;
The upgrade process should be interruptible (like the Unicode upgrade was) so it can be stopped/restarted any time.&lt;br /&gt;
&lt;br /&gt;
=== Migration of content ===&lt;br /&gt;
&lt;br /&gt;
* resources - move files to new resource content file area; can be done automatically for pdf, image resources; definitely not accurate for uploaded web pages&lt;br /&gt;
* questions - image file moved to new area, image tag appended to questions&lt;br /&gt;
* moddata files - the easiest part, just move to new storage&lt;br /&gt;
* coursefiles - there might be many outdated files :-( :-(&lt;br /&gt;
* rss feeds links in readers - will be broken, the new security related code would break it anyway&lt;br /&gt;
&lt;br /&gt;
=== Moving files to files table and file pool ===&lt;br /&gt;
&lt;br /&gt;
The migration process must be interruptable because it might take a very long time. The files would be moved from old location, the restarting would be straightforward.&lt;br /&gt;
&lt;br /&gt;
Proposed stages:&lt;br /&gt;
#migration of all course files except moddata - finish marked by some $CFG-&amp;gt;files_migrated=true; - this step breaks the old file manager and html editor integration&lt;br /&gt;
#migration of blog attachments&lt;br /&gt;
#migration of question files&lt;br /&gt;
#migration of moddata files - each module is responsible to copy data from converted coursefiles or directly from moddata which is not converted automatically&lt;br /&gt;
&lt;br /&gt;
Some people use symbolic links in coursefiles - we must make sure that those will be copied to new storage in both places, though they can not be linked any more - anybody wanting to have content synced will need to move the files to some repository and set up the sync again.&lt;br /&gt;
&lt;br /&gt;
::Talked about a double task here, when migrating course files to module areas:&lt;br /&gt;
::# Parse html files to detect all the dependencies and move them together.&lt;br /&gt;
::# Fallback in pluginfile.php so, if something isn&#039;t found in module filearea, search for it in course filearea, copying it and finally, serving it.&lt;br /&gt;
&lt;br /&gt;
:: Also we talked about the possibility of add a new setting to resource in order to define if it should work against old coursefiles or new autocontained file areas. Migrated resources will point to old coursefiles while new ones will enforce autocontained file areas.&lt;br /&gt;
&lt;br /&gt;
:: it seems that only resource files will be really complex (because allow arbitrary HTML inclusion). The rest (labels, intros... doesn&#039;t) and should be easier to parse.&lt;br /&gt;
&lt;br /&gt;
::[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 19:00, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Other issues ==&lt;br /&gt;
&lt;br /&gt;
=== Unicode support in zip format ===&lt;br /&gt;
&lt;br /&gt;
Zip format is an old standard for compressing files. It was created long before Unicode existed, and Unicode support was only recently added. There are several ways used for encoding of non-ASCII characters in path names, but unfortunately it is not very standardised. Most Windows packers use DOS encoding.&lt;br /&gt;
&lt;br /&gt;
Client software:&lt;br /&gt;
* Windows built-in compression - bundled with Windows, non-standard DOS encoding only&lt;br /&gt;
* WinZip - shareware, Unicode option (since v11.2)&lt;br /&gt;
* TotalCommander - shareware, single byte(DOS) encoding only&lt;br /&gt;
* 7-Zip - free, Unicode or DOS encoding depending on characters used in file name (since v4.58beta)&lt;br /&gt;
* Info-ZIP - free, uses some weird character set conversions&lt;br /&gt;
&lt;br /&gt;
PHP extraction:&lt;br /&gt;
* Info-ZIP binary execution - no Unicode support at all, mangles character sets in file names (depends on OS, see docs), files must be copied to temp directory before compression and after extraction&lt;br /&gt;
* PclZip PHP library - reads single byte encoded names only, problems with random problems and higher memory usage.&lt;br /&gt;
* Zip PHP extension - kind of works in latest PHP versions&lt;br /&gt;
&lt;br /&gt;
Large file support:&lt;br /&gt;
PHP running under 32bit operating systems does not support files &amp;gt;2GB (do not expect fix before PHP 6). This might be a potential problem for larger backups.&lt;br /&gt;
&lt;br /&gt;
Tar Alternative:&lt;br /&gt;
* tar with gzip compression - easy to implement in PHP + zlib extension (PclTar, Tar from PEAR or custom code)&lt;br /&gt;
* no problem with unicode in *nix, Windows again expects DOS encoding :-(&lt;br /&gt;
* seems suitable for backup/restore - yay!&lt;br /&gt;
&lt;br /&gt;
Summary:&lt;br /&gt;
# added zip processing class that fully hides the underlying library&lt;br /&gt;
# using single byte encoding &amp;quot;garbage in/garbage out&amp;quot; approach for encoding of files in zip archives; add new &#039;zipencoding&#039; string into lang packs (ex: cp852 DOS charset for Czech locale) and use it during extraction (we might support true unicode later when PHP Zip extension does that)&lt;br /&gt;
&lt;br /&gt;
=== Tar packer ===&lt;br /&gt;
&lt;br /&gt;
A .tar.gz format packer is available from Moodle 2.6 (requires zlib extension) and can be selected for use in Moodle backup via an experimental option. MDL-41838.&lt;br /&gt;
&lt;br /&gt;
The packer is currently limited to ASCII filenames and individual files are limited to 8GB each, but unlike zip there is no limit on the total filesize. It uses the old POSIX format and is compatible with GNU tar using default options.&lt;br /&gt;
&lt;br /&gt;
== Not implemented yet ==&lt;br /&gt;
* antivirus scanning - this needs a different api because the upload of files is now handled via repository plugins&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Using the File API]]&lt;br /&gt;
* [[Repository API]]&lt;br /&gt;
* [[Portfolio API]]&lt;br /&gt;
* [[Resource module file API migration]]&lt;br /&gt;
* MDL-14589 - File API Meta issue&lt;br /&gt;
&lt;br /&gt;
[[Category:Files]]&lt;br /&gt;
[[Category:Interfaces]]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Grunt&amp;diff=53474</id>
		<title>Grunt</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Grunt&amp;diff=53474"/>
		<updated>2017-12-05T15:31:53Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* Install grunt */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.9}}&lt;br /&gt;
&lt;br /&gt;
Grunt is a command line tool used to prepare our javascript and less-generated css for production usage. After making any change to javascript or less files in Moodle, you must run grunt to lint, minify and package the javascript/css properly so that it can be served by Moodle.&lt;br /&gt;
&lt;br /&gt;
== Install grunt ==&lt;br /&gt;
The Javascript modules (AMD and YUI) and less css in Moodle must be processed by some build tools before they will be visible to your web browser. We use &amp;quot;[http://gruntjs.com/ grunt]&amp;quot; as a build tool to wrap our different processes. Grunt is a build tool written in Javascript that runs in the &amp;quot;[http://nodejs.org/ nodejs]&amp;quot; environment. This means you first have to install nodejs - and it&#039;s package manager &amp;quot;[https://www.npmjs.com/ npm]&amp;quot;. The details of how to install those packages will vary by operating system, but on Linux it&#039;s likely to be: &lt;br /&gt;
 apt-get install nodejs npm&lt;br /&gt;
See also: [http://askubuntu.com/questions/594656/how-to-install-the-latest-versions-of-nodejs-and-npm-for-ubuntu-14-04-lts/711976 how to install the latest version of nodejs and npm for ubuntu 14.04]&lt;br /&gt;
&lt;br /&gt;
There are downloadable packages for other operating systems here: http://nodejs.org/download/. &lt;br /&gt;
&lt;br /&gt;
Once this is done, cd into your Moodle directory and run the command: &lt;br /&gt;
 npm install&lt;br /&gt;
to install all of the required tools.&lt;br /&gt;
&lt;br /&gt;
You also need to install the grunt-cli package globally in order to have a &amp;quot;grunt&amp;quot; command in your path. To do this run:&lt;br /&gt;
 npm install -g grunt-cli&lt;br /&gt;
&lt;br /&gt;
== Running grunt ==&lt;br /&gt;
Typical commands:&lt;br /&gt;
&lt;br /&gt;
 grunt amd               # Short-cut for grunt jshint uglify, rebuild all AMD modules.&lt;br /&gt;
 grunt shifter           # Run shifter&lt;br /&gt;
 grunt js                # Short-cut for grunt amd shifter&lt;br /&gt;
 &lt;br /&gt;
 grunt css               # Run less&lt;br /&gt;
 grunt                   # Try to do the right thing:&lt;br /&gt;
                         # * If you are in a folder called amd, do grunt amd&lt;br /&gt;
                         # * If you are in a folder called yui/src/something, do grunt shifter&lt;br /&gt;
                         # * Otherwise build everything (grunt css js).&lt;br /&gt;
 &lt;br /&gt;
 grunt watch             # Watch for changes and re-run grunt tasks depending on what file changes&lt;br /&gt;
 grunt eslint --show-lint-warnings # Show pedantic lint warnings for JS&lt;br /&gt;
&lt;br /&gt;
On Linux/Mac it will build everything in the current folder and below. On Windows, you need to specify the path on the command line like &amp;lt;tt&amp;gt;--root=admin/tool/templatelibrary&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Using Grunt in additional plugins ==&lt;br /&gt;
&lt;br /&gt;
You may want to use Grunt for performing tasks in your custom Moodle plugins. For building AMD and YUI modules in your plugin, the standard configuration Gruntfile.js located in the Moodle root should work well. For building CSS files from LESS templates, you will have to set up a separate Grunt installation in the root of your plugin.&lt;br /&gt;
&lt;br /&gt;
If you do not have it yet, create the package.json file in the root of your plugin:&lt;br /&gt;
&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;moodle-plugintype_pluginname&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Install grunt, grunt less and grunt watch modules. Note that you should put the folder node_modules into your plugin&#039;s .gitignore file, too.&lt;br /&gt;
&lt;br /&gt;
    $ cd /path/to/your/plugin/root&lt;br /&gt;
    $ npm install --save-dev grunt grunt-contrib-less grunt-contrib-watch grunt-load-gruntfile&lt;br /&gt;
&lt;br /&gt;
Create a Gruntfile.js in the root of your plugin and configure the task for building CSS files from LESS:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
&amp;quot;use strict&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
module.exports = function (grunt) {&lt;br /&gt;
&lt;br /&gt;
    // We need to include the core Moodle grunt file too, otherwise we can&#039;t run tasks like &amp;quot;amd&amp;quot;.&lt;br /&gt;
    require(&amp;quot;grunt-load-gruntfile&amp;quot;)(grunt);&lt;br /&gt;
    grunt.loadGruntfile(&amp;quot;../../Gruntfile.js&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    // Load all grunt tasks.&lt;br /&gt;
    grunt.loadNpmTasks(&amp;quot;grunt-contrib-less&amp;quot;);&lt;br /&gt;
    grunt.loadNpmTasks(&amp;quot;grunt-contrib-watch&amp;quot;);&lt;br /&gt;
    grunt.loadNpmTasks(&amp;quot;grunt-contrib-clean&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    grunt.initConfig({&lt;br /&gt;
        watch: {&lt;br /&gt;
            // If any .less file changes in directory &amp;quot;less&amp;quot; then run the &amp;quot;less&amp;quot; task.&lt;br /&gt;
            files: &amp;quot;less/*.less&amp;quot;,&lt;br /&gt;
            tasks: [&amp;quot;less&amp;quot;]&lt;br /&gt;
        },&lt;br /&gt;
        less: {&lt;br /&gt;
            // Production config is also available.&lt;br /&gt;
            development: {&lt;br /&gt;
                options: {&lt;br /&gt;
                    // Specifies directories to scan for @import directives when parsing.&lt;br /&gt;
                    // Default value is the directory of the source, which is probably what you want.&lt;br /&gt;
                    paths: [&amp;quot;less/&amp;quot;],&lt;br /&gt;
                    compress: true&lt;br /&gt;
                },&lt;br /&gt;
                files: {&lt;br /&gt;
                    &amp;quot;styles.css&amp;quot;: &amp;quot;less/styles.less&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
    // The default task (running &amp;quot;grunt&amp;quot; in console).&lt;br /&gt;
    grunt.registerTask(&amp;quot;default&amp;quot;, [&amp;quot;less&amp;quot;]);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now running &amp;quot;grunt&amp;quot; or &amp;quot;grunt less&amp;quot; in your plugin root folder will compile the file less/styles.less and saves it as styles.css. Running &amp;quot;grunt watch&amp;quot; will watch the less/*.less files and if some is changed, it will immediately rebuild the CSS file.&lt;br /&gt;
&lt;br /&gt;
If you are working on a custom theme, you may have multiple less/*.less files that you want to compile to their style/*.css counterparts. You can either define an explicit list all such file pairs, or let that list be created for you by making use of [http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically expand:true feature] of gruntfile.js:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code javascript&amp;gt;&lt;br /&gt;
// This dynamically creates the list of files to be processed.&lt;br /&gt;
files: [&lt;br /&gt;
    {   &lt;br /&gt;
        expand: true,&lt;br /&gt;
        cwd: &amp;quot;less/&amp;quot;,&lt;br /&gt;
        src: &amp;quot;*.less&amp;quot;,&lt;br /&gt;
        dest: &amp;quot;style/&amp;quot;,&lt;br /&gt;
        ext: &amp;quot;.css&amp;quot;&lt;br /&gt;
    }   &lt;br /&gt;
]   &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[YUI/Shifter]]&lt;br /&gt;
* [[Javascript Modules]]&lt;br /&gt;
* [[LESS]]&lt;br /&gt;
&lt;br /&gt;
[[Category:AJAX]]&lt;br /&gt;
[[Category:Javascript]]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Using_images_in_a_theme&amp;diff=53422</id>
		<title>Using images in a theme</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Using_images_in_a_theme&amp;diff=53422"/>
		<updated>2017-11-23T12:02:49Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* Using your images within your layout files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Themes}}&lt;br /&gt;
==A little background==&lt;br /&gt;
Moodle a standard way of making use of images within its pages enabling theme designers to take full control over what images are being used and when possible ensures images are passed through Moodle&#039;s performance caching system to obtain the best possible performance.&lt;br /&gt;
&lt;br /&gt;
==Before we start==&lt;br /&gt;
&lt;br /&gt;
# Make sure you have a theme and images to work with, one that you have a backup of just in case.&lt;br /&gt;
# Take note of your theme&#039;s main layout file e.g. &#039;&#039;standard.php&#039;&#039;.&lt;br /&gt;
# Take note of your theme&#039;s main CSS file e.g. &#039;&#039;core.css&#039;&#039;.&lt;br /&gt;
# Turn on &#039;&#039;&#039;Theme designer mode&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
==Image locations within Moodle==&lt;br /&gt;
&lt;br /&gt;
There are three main areas in which images are located - core, plugin and theme.&lt;br /&gt;
&lt;br /&gt;
Core images are used throughout Moodle and are stored in &#039;&#039;moodle/pix&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The pix directory contains the following subdirectories:&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;a&#039;&#039; - Icons that are not widely used&lt;br /&gt;
:&#039;&#039;c&#039;&#039; - Calendar-related icons&lt;br /&gt;
:&amp;quot;e&amp;quot; - Editor-related icons&lt;br /&gt;
:&#039;&#039;f&#039;&#039; - File icons for different file types&lt;br /&gt;
:&#039;&#039;g&#039;&#039; - Default user icons and thumbnails&lt;br /&gt;
:&#039;&#039;i&#039;&#039; - General icons&lt;br /&gt;
:&#039;&#039;m&#039;&#039; - Currency symbols&lt;br /&gt;
:&#039;&#039;s&#039;&#039; - Smileys&lt;br /&gt;
:&#039;&#039;t&#039;&#039; - General icons&lt;br /&gt;
:&#039;&#039;u&#039;&#039; - User icons and thumbnails&lt;br /&gt;
:&#039;&#039;y&#039;&#039; - YUI icons&lt;br /&gt;
&lt;br /&gt;
Plugin images are used by plugins and are stored within the plugin&#039;s directory e.g. &#039;&#039;mod/forum/*&#039;&#039; or &#039;&#039;blocks/navigation/*&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Theme images are used in themes and are stored in the &#039;&#039;pix&#039;&#039; subdirectory of a theme.&lt;br /&gt;
&lt;br /&gt;
==Adding new images to your theme==&lt;br /&gt;
&lt;br /&gt;
To add new images to your theme, such as a background image or a logo, you should start by creating a pix directory within your themes directory.&lt;br /&gt;
&lt;br /&gt;
Lets assume you have two images, gradient.png and logo.jpg. Copy both files to your themes pix directory.&lt;br /&gt;
&lt;br /&gt;
The plan is to use gradient.png as the background image for your theme (in CSS) and use logo.jpg in the header (in your theme&#039;s layout file).&lt;br /&gt;
&lt;br /&gt;
===Using images within CSS===&lt;br /&gt;
&lt;br /&gt;
Moodle parses all CSS files. When theme designer mode is off, Moodle combines them into a handful of large CSS files that get cached and served. At the same time Moodle also looks at the CSS and replaces special syntax rules. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&#039;padding:10px;background-color:#f7f7f7;border:1px dashed #123456&#039;&amp;gt;&amp;lt;nowiki&amp;gt;[[pix:theme|&amp;lt;/nowiki&amp;gt;&amp;lt;span style=&#039;color:#336699;font-weight:bold;&#039;&amp;gt;&amp;lt;nowiki&amp;gt;path/to/image/&amp;lt;/nowiki&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&#039;color:#33993A;font-weight:bold;&#039;&amp;gt;imagename&amp;lt;/span&amp;gt;&amp;lt;nowiki&amp;gt;]]&amp;lt;/nowiki&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above pattern is looked for by Moodle and gets replaced with the correct image URL when found.&lt;br /&gt;
You should note the following things about this pattern when using your own images within CSS:&lt;br /&gt;
&lt;br /&gt;
# The bits in black don&#039;t change&lt;br /&gt;
# The bit in blue is the path to your image within the pix directory. It shouldn&#039;t start with a / but should end with one.&lt;br /&gt;
# The bit in green is the filename (&#039;&#039;&#039;no need to include the file extension&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
To use gradient.png as the background for our pages, add the following line of CSS to the themes core.css.&lt;br /&gt;
&amp;lt;code css&amp;gt;&lt;br /&gt;
body {background-image:url([[pix:theme|gradient]]);}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Before we look at how to use your own images within your themes layout files we should first look at how this changes if the image is in a sub directory.&lt;br /&gt;
&lt;br /&gt;
Lets assume gradient.png is located here: &#039;&#039;&#039;pix/myimages/gradients/gradient.png&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The CSS would need to be as follows:&lt;br /&gt;
&amp;lt;code css&amp;gt;&lt;br /&gt;
body {background-image:url([[pix:theme|myimages/gradients/gradient]]);}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want to refer to an image belonging to a plugin, you can do it like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code css&amp;gt;&lt;br /&gt;
body {background-image:url([[pix:quiz|icon]]);}&lt;br /&gt;
body {background-image:url([[pix:qtype_ddmarker|grid]]);}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That refers to mod/quiz/pix/icon.png/gif and question/type/ddmarker/pix/grid.png/gif respectively (or whatever icons you have put in your theme to override these).&lt;br /&gt;
&lt;br /&gt;
===Using your images within your layout files===&lt;br /&gt;
&lt;br /&gt;
To use logo.jpg within the a layout php file or layout mustache template you need to add the url for the image to the template context and then include an image tag in the template using the source from the context.&lt;br /&gt;
&lt;br /&gt;
Images:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/pix/logo.jpg&lt;br /&gt;
/pix/myimages/logos/logo.png&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;theme/yourtheme/layout/somelayout.php&#039;&#039;&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
$templatecontext = [&lt;br /&gt;
...&lt;br /&gt;
&#039;imageone&#039; =&amp;gt; $OUTPUT-&amp;gt;image_url(&#039;logo&#039;, &#039;theme&#039;),&lt;br /&gt;
&#039;imagetwo&#039; =&amp;gt; $OUTPUT-&amp;gt;image_url(&#039;myimages/logos/logo&#039;, &#039;theme&#039;),&lt;br /&gt;
...&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;render_from_template(&#039;theme_yourtheme/somelayout&#039;, $templatecontext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;theme/yourtheme/templates/somelayout.mustache&#039;&#039;&lt;br /&gt;
&amp;lt;code handlebars&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;img src=&amp;quot;{{{imageone}}}&amp;quot; alt=&amp;quot;Please give your image alt text or set the role to presentation&amp;quot; width=&amp;quot;50&amp;quot; height=&amp;quot;50&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;img src=&amp;quot;{{{imagetwo}}}&amp;quot; alt=&amp;quot;Please give your image alt text or set the role to presentation&amp;quot; width=&amp;quot;50&amp;quot; height=&amp;quot;50&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overriding images in your theme==&lt;br /&gt;
&lt;br /&gt;
Overriding images within your theme can be an important part of creating and/or modifying themes.&lt;br /&gt;
&lt;br /&gt;
As mentioned, there are three main areas for images within Moodle, core, plugins, and themes. Obviously we won&#039;t be overriding theme images as we are in a theme and have full control over that already which just leaves core and plugin images that you may want to override.&lt;br /&gt;
&lt;br /&gt;
Within your theme create the following two directories:&lt;br /&gt;
; /pix_core/ : This is where your images to override core images will need to be.&lt;br /&gt;
; /pix_plugins/ : This is where images to override plugins will need to be.&lt;br /&gt;
&lt;br /&gt;
Next, copy the images that you wish to override into either the pix_core or pix_plugins directory. You need to replicate the directory structure that the images are located in.&lt;br /&gt;
&lt;br /&gt;
The following two examples illustrate how this works for both core and plugin images.&lt;br /&gt;
&lt;br /&gt;
===Overriding core images===&lt;br /&gt;
For this example I am going to override the following two images:&lt;br /&gt;
# &#039;&#039;&#039;moodle/pix/help.gif&#039;&#039;&#039; This image is used for all help image icons and is shown throughout Moodle.&lt;br /&gt;
# &#039;&#039;&#039;moodle/pix/i/info.gif&#039;&#039;&#039; This image is used in several places, most notably the front page if the combo list is being displayed.&lt;br /&gt;
&lt;br /&gt;
So first up help.gif. This is the most basic example of overriding an image. Because the image is directly within Moodle&#039;s pix directory we can simply place our new help image into our themes pix_core directory. There&#039;s nothing more to it!&lt;br /&gt;
&lt;br /&gt;
The second example if not really any more difficult. Because the image is located within the subdirectory &#039;&#039;&#039;i&#039;&#039;&#039; we must create a sub directory within our pix_core directory into which we will copy our new help image. That is, the image ends up at &#039;&#039;&#039;/pix_core/i/info.png&#039;&#039;&#039; inside your theme folder. And again done!&lt;br /&gt;
&lt;br /&gt;
You should also note that, as with any other image in Moodle, the extension doesn&#039;t matter. If you want to replace help.gif with a help.png you can just put the png into the correct directory. As long as the filename is the same Moodle will find it.&lt;br /&gt;
&lt;br /&gt;
===Overriding plugin images===&lt;br /&gt;
This is a little bit more difficult than overriding core images, but not too much so. For this example, let&#039;s say I want to override the following two plugin images:&lt;br /&gt;
# &#039;&#039;&#039;moodle/mod/forum/icon.gif&#039;&#039;&#039; This is the icon that is used everywhere for the forum.&lt;br /&gt;
# &#039;&#039;&#039;moodle/blocks/customblock/activate.png&#039;&#039;&#039; This is an icon in a custom block I have installed.&lt;br /&gt;
&lt;br /&gt;
Just as with core images, we need to put the image in the correct directory structure. In this case it is going to be within our theme&#039;s &#039;&#039;&#039;pix_plugins&#039;&#039;&#039; directory.&lt;br /&gt;
The correct path must use the plugin type [[Frankenstyle]] prefix and the plugin name.&lt;br /&gt;
&lt;br /&gt;
For the examples above the paths to override the plugin images will be:&lt;br /&gt;
# &#039;&#039;&#039;pix_plugins/mod/forum/&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;pix_plugins/block/customblock/&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When looking for an overriding image the following path is used.&lt;br /&gt;
&lt;br /&gt;
# theme (static)&lt;br /&gt;
# themename =&amp;gt; mytheme&lt;br /&gt;
# area =&amp;gt; pix_plugins for plugin images, pix_core for core images, pix for theme images.&lt;br /&gt;
# plugin type =&amp;gt; booktool for the book tool sub plugin for example.&lt;br /&gt;
# plugin name =&amp;gt; print for the name of the sub plugin.&lt;br /&gt;
# file name =&amp;gt; book for the name of the image.&lt;br /&gt;
&lt;br /&gt;
It then searches for the image with the following extensions gif, png, jpg, jpeg in that order.&lt;br /&gt;
&lt;br /&gt;
So you end up with:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/theme/themename/area/plugintype/pluginname/filename.gif&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Plugins]]&lt;br /&gt;
* [http://youtu.be/g_9VM9OMs3Y Adding Images to your Theme]  MoodleBites video on YouTube&lt;br /&gt;
&lt;br /&gt;
Using Moodle forum discussions:&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=151581 Using images within your themes (and the silk icon theme)]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=151581#p689883 choosing images based on the users current language]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=157935&amp;amp;parent=691903 how to add a category(categorias) img-bullet]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Using_images_in_a_theme&amp;diff=52497</id>
		<title>Using images in a theme</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Using_images_in_a_theme&amp;diff=52497"/>
		<updated>2017-05-22T14:29:24Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* Using your images within your layout files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Themes}}&lt;br /&gt;
==A little background==&lt;br /&gt;
Moodle a standard way of making use of images within its pages enabling theme designers to take full control over what images are being used and when possible ensures images are passed through Moodle&#039;s performance caching system to obtain the best possible performance.&lt;br /&gt;
&lt;br /&gt;
==Before we start==&lt;br /&gt;
&lt;br /&gt;
# Make sure you have a theme and images to work with, one that you have a backup of just in case.&lt;br /&gt;
# Take note of your theme&#039;s main layout file e.g. &#039;&#039;standard.php&#039;&#039;.&lt;br /&gt;
# Take note of your theme&#039;s main CSS file e.g. &#039;&#039;core.css&#039;&#039;.&lt;br /&gt;
# Turn on &#039;&#039;&#039;Theme designer mode&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
==Image locations within Moodle==&lt;br /&gt;
&lt;br /&gt;
There are three main areas in which images are located - core, plugin and theme.&lt;br /&gt;
&lt;br /&gt;
Core images are used throughout Moodle and are stored in &#039;&#039;moodle/pix&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The pix directory contains the following subdirectories:&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;a&#039;&#039; - Icons that are not widely used&lt;br /&gt;
:&#039;&#039;c&#039;&#039; - Calendar-related icons&lt;br /&gt;
:&amp;quot;e&amp;quot; - Editor-related icons&lt;br /&gt;
:&#039;&#039;f&#039;&#039; - File icons for different file types&lt;br /&gt;
:&#039;&#039;g&#039;&#039; - Default user icons and thumbnails&lt;br /&gt;
:&#039;&#039;i&#039;&#039; - General icons&lt;br /&gt;
:&#039;&#039;m&#039;&#039; - Currency symbols&lt;br /&gt;
:&#039;&#039;s&#039;&#039; - Smileys&lt;br /&gt;
:&#039;&#039;t&#039;&#039; - General icons&lt;br /&gt;
:&#039;&#039;u&#039;&#039; - User icons and thumbnails&lt;br /&gt;
:&#039;&#039;y&#039;&#039; - YUI icons&lt;br /&gt;
&lt;br /&gt;
Plugin images are used by plugins and are stored within the plugin&#039;s directory e.g. &#039;&#039;mod/forum/*&#039;&#039; or &#039;&#039;blocks/navigation/*&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Theme images are used in themes and are stored in the &#039;&#039;pix&#039;&#039; subdirectory of a theme.&lt;br /&gt;
&lt;br /&gt;
==Adding new images to your theme==&lt;br /&gt;
&lt;br /&gt;
To add new images to your theme, such as a background image or a logo, you should start by creating a pix directory within your themes directory.&lt;br /&gt;
&lt;br /&gt;
Lets assume you have two images, gradient.png and logo.jpg. Copy both files to your themes pix directory.&lt;br /&gt;
&lt;br /&gt;
The plan is to use gradient.png as the background image for your theme (in CSS) and use logo.jpg in the header (in your theme&#039;s layout file).&lt;br /&gt;
&lt;br /&gt;
===Using images within CSS===&lt;br /&gt;
&lt;br /&gt;
Moodle parses all CSS files. When theme designer mode is off, Moodle combines them into a handful of large CSS files that get cached and served. At the same time Moodle also looks at the CSS and replaces special syntax rules. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&#039;padding:10px;background-color:#f7f7f7;border:1px dashed #123456&#039;&amp;gt;&amp;lt;nowiki&amp;gt;[[pix:theme|&amp;lt;/nowiki&amp;gt;&amp;lt;span style=&#039;color:#336699;font-weight:bold;&#039;&amp;gt;&amp;lt;nowiki&amp;gt;path/to/image/&amp;lt;/nowiki&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&#039;color:#33993A;font-weight:bold;&#039;&amp;gt;imagename&amp;lt;/span&amp;gt;&amp;lt;nowiki&amp;gt;]]&amp;lt;/nowiki&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above pattern is looked for by Moodle and gets replaced with the correct image URL when found.&lt;br /&gt;
You should note the following things about this pattern when using your own images within CSS:&lt;br /&gt;
&lt;br /&gt;
# The bits in black don&#039;t change&lt;br /&gt;
# The bit in blue is the path to your image within the pix directory. It shouldn&#039;t start with a / but should end with one.&lt;br /&gt;
# The bit in green is the filename (&#039;&#039;&#039;no need to include the file extension&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
To use gradient.png as the background for our pages, add the following line of CSS to the themes core.css.&lt;br /&gt;
&amp;lt;code css&amp;gt;&lt;br /&gt;
body {background-image:url([[pix:theme|gradient]]);}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Before we look at how to use your own images within your themes layout files we should first look at how this changes if the image is in a sub directory.&lt;br /&gt;
&lt;br /&gt;
Lets assume gradient.png is located here: &#039;&#039;&#039;pix/myimages/gradients/gradient.png&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The CSS would need to be as follows:&lt;br /&gt;
&amp;lt;code css&amp;gt;&lt;br /&gt;
body {background-image:url([[pix:theme|myimages/gradients/gradient]]);}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want to refer to an image belonging to a plugin, you can do it like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code css&amp;gt;&lt;br /&gt;
body {background-image:url([[pix:quiz|icon]]);}&lt;br /&gt;
body {background-image:url([[pix:qtype_ddmarker|grid]]);}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That refers to mod/quiz/pix/icon.png/gif and question/type/ddmarker/pix/grid.png/gif respectively (or whatever icons you have put in your theme to override these).&lt;br /&gt;
&lt;br /&gt;
===Using your images within your layout files===&lt;br /&gt;
&lt;br /&gt;
To use logo.jpg within the a layout php file or layout mustache template you need to add the url for the image to the template context and then include an image tag in the template using the source from the context.&lt;br /&gt;
&lt;br /&gt;
Images:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/pix/logo.jpg&lt;br /&gt;
/pix/myimages/logos/logo.png&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;theme/yourtheme/layout/somelayout.php&#039;&#039;&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
$templatecontext = [&lt;br /&gt;
...&lt;br /&gt;
&#039;imageone&#039; =&amp;gt; $OUTPUT-&amp;gt;pix_url(&#039;logo&#039;, &#039;theme&#039;),&lt;br /&gt;
&#039;imagetwo&#039; =&amp;gt; $OUTPUT-&amp;gt;pix_url(&#039;myimages/logos/logo&#039;, &#039;theme&#039;),&lt;br /&gt;
...&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;render_from_template(&#039;theme_yourtheme/somelayout&#039;, $templatecontext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;theme/yourtheme/templates/somelayout.mustache&#039;&#039;&lt;br /&gt;
&amp;lt;code handlebars&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;img src=&amp;quot;{{{imageone}}}&amp;quot; alt=&amp;quot;Please give your image alt text or set the role to presentation&amp;quot; width=&amp;quot;50&amp;quot; height=&amp;quot;50&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;img src=&amp;quot;{{{imagetwo}}}&amp;quot; alt=&amp;quot;Please give your image alt text or set the role to presentation&amp;quot; width=&amp;quot;50&amp;quot; height=&amp;quot;50&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overriding images in your theme==&lt;br /&gt;
&lt;br /&gt;
Overriding images within your theme can be an important part of creating and/or modifying themes.&lt;br /&gt;
&lt;br /&gt;
As mentioned, there are three main areas for images within Moodle, core, plugins, and themes. Obviously we won&#039;t be overriding theme images as we are in a theme and have full control over that already which just leaves core and plugin images that you may want to override.&lt;br /&gt;
&lt;br /&gt;
Within your theme create the following two directories:&lt;br /&gt;
; /pix_core/ : This is where your images to override core images will need to be.&lt;br /&gt;
; /pix_plugins/ : This is where images to override plugins will need to be.&lt;br /&gt;
&lt;br /&gt;
Next, copy the images that you wish to override into either the pix_core or pix_plugins directory. You need to replicate the directory structure that the images are located in.&lt;br /&gt;
&lt;br /&gt;
The following two examples illustrate how this works for both core and plugin images.&lt;br /&gt;
&lt;br /&gt;
===Overriding core images===&lt;br /&gt;
For this example I am going to override the following two images:&lt;br /&gt;
# &#039;&#039;&#039;moodle/pix/help.gif&#039;&#039;&#039; This image is used for all help image icons and is shown throughout Moodle.&lt;br /&gt;
# &#039;&#039;&#039;moodle/pix/i/info.gif&#039;&#039;&#039; This image is used in several places, most notably the front page if the combo list is being displayed.&lt;br /&gt;
&lt;br /&gt;
So first up help.gif. This is the most basic example of overriding an image. Because the image is directly within Moodle&#039;s pix directory we can simply place our new help image into our themes pix_core directory. There&#039;s nothing more to it!&lt;br /&gt;
&lt;br /&gt;
The second example if not really any more difficult. Because the image is located within the subdirectory &#039;&#039;&#039;i&#039;&#039;&#039; we must create a sub directory within our pix_core directory into which we will copy our new help image. That is, the image ends up at &#039;&#039;&#039;/pix_core/i/info.png&#039;&#039;&#039; inside your theme folder. And again done!&lt;br /&gt;
&lt;br /&gt;
You should also note that, as with any other image in Moodle, the extension doesn&#039;t matter. If you want to replace help.gif with a help.png you can just put the png into the correct directory. As long as the filename is the same Moodle will find it.&lt;br /&gt;
&lt;br /&gt;
===Overriding plugin images===&lt;br /&gt;
This is a little bit more difficult than overriding core images, but not too much so. For this example, let&#039;s say I want to override the following two plugin images:&lt;br /&gt;
# &#039;&#039;&#039;moodle/mod/forum/icon.gif&#039;&#039;&#039; This is the icon that is used everywhere for the forum.&lt;br /&gt;
# &#039;&#039;&#039;moodle/blocks/customblock/activate.png&#039;&#039;&#039; This is an icon in a custom block I have installed.&lt;br /&gt;
&lt;br /&gt;
Just as with core images, we need to put the image in the correct directory structure. In this case it is going to be within our theme&#039;s &#039;&#039;&#039;pix_plugins&#039;&#039;&#039; directory.&lt;br /&gt;
The correct path must use the plugin type [[Frankenstyle]] prefix and the plugin name.&lt;br /&gt;
&lt;br /&gt;
For the examples above the paths to override the plugin images will be:&lt;br /&gt;
# &#039;&#039;&#039;pix_plugins/mod/forum/&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;pix_plugins/block/customblock/&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When looking for an overriding image the following path is used.&lt;br /&gt;
&lt;br /&gt;
# theme (static)&lt;br /&gt;
# themename =&amp;gt; mytheme&lt;br /&gt;
# area =&amp;gt; pix_plugins for plugin images, pix_core for core images, pix for theme images.&lt;br /&gt;
# plugin type =&amp;gt; booktool for the book tool sub plugin for example.&lt;br /&gt;
# plugin name =&amp;gt; print for the name of the sub plugin.&lt;br /&gt;
# file name =&amp;gt; book for the name of the image.&lt;br /&gt;
&lt;br /&gt;
It then searches for the image with the following extensions gif, png, jpg, jpeg in that order.&lt;br /&gt;
&lt;br /&gt;
So you end up with:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/theme/themename/area/plugintype/pluginname/filename.gif&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Plugins]]&lt;br /&gt;
* [http://youtu.be/g_9VM9OMs3Y Adding Images to your Theme]  MoodleBites video on YouTube&lt;br /&gt;
&lt;br /&gt;
Using Moodle forum discussions:&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=151581 Using images within your themes (and the silk icon theme)]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=151581#p689883 choosing images based on the users current language]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=157935&amp;amp;parent=691903 how to add a category(categorias) img-bullet]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Access_API&amp;diff=51025</id>
		<title>Access API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Access_API&amp;diff=51025"/>
		<updated>2016-11-03T10:37:55Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* isguestuser(), isloggedin() and is_siteadmin() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.2}}The Access API gives you functions so you can determine what the current user is allowed to do. It also allows modules to extend Moodle with new capabilities. &lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
Moodle is using a role based access control model. Most entities in Moodle (system, users, course categories, courses, modules and blocks) are represented by contexts that are arranged in a tree like hierarchy called context tree. Role is a set of capability definitions, each capability usually represents an ability of user to do something. Roles are defined at the top most system context level. Role definitions can be overridden at lower context levels. User access control is calculated from the definitions of roles assigned to users.&lt;br /&gt;
&lt;br /&gt;
All users that did not log-in yet automatically get the default role defined in $CFG-&amp;gt;notloggedinroleid, it is not possible to assign any other role to this non-existent user id. There is one special guest user account that is used when user logs in using the guest login button or when guest autologin is enabled. Again you can not assign any roles to the guest account directly, this account gets the $CFG-&amp;gt;guestroleid automatically. All other authenticated users get the default user role specified in $CFG-&amp;gt;defaultuserroleid and in the frontpage context the role specified in $CFG-&amp;gt;defaultfrontpageroleid.&lt;br /&gt;
&lt;br /&gt;
==How to define new capabilities in plugins==&lt;br /&gt;
&lt;br /&gt;
Capabilities are defined by $capabilities array defined in db/access.php files. The name of the capability consists of &amp;quot;plugintype/pluginname:capabilityname&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
 $capabilities = array(&lt;br /&gt;
    &#039;mod/folder:managefiles&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;riskbitmask&#039; =&amp;gt; RISK_SPAM,&lt;br /&gt;
        &#039;captype&#039; =&amp;gt; &#039;write&#039;,&lt;br /&gt;
        &#039;contextlevel&#039; =&amp;gt; CONTEXT_MODULE,&lt;br /&gt;
        &#039;archetypes&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;editingteacher&#039; =&amp;gt; CAP_ALLOW&lt;br /&gt;
        )&lt;br /&gt;
    ),&lt;br /&gt;
 );&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where the meaning of array keys is:&lt;br /&gt;
* riskbitmask - associated risks. These are explained on [[Hardening new Roles system]].&lt;br /&gt;
* captype - &#039;&#039;read&#039;&#039; or &#039;&#039;write&#039;&#039; capability type, for security reasons system prevents all write capabilities for guest account and not-logged-in users&lt;br /&gt;
* contextlevel - specified as context level constant. Declares the typical context level where this capability is checked. It is the lowest level where this capability can be tweaked (overridden) via the permissions UI.&lt;br /&gt;
* archetypes - specifies defaults for roles with standard archetypes, this is used in installs, upgrades and when resetting roles (it is recommended to use only CAP_ALLOW here).  Archetypes are defined in mdl_role table.  See also [[Role archetypes]].&lt;br /&gt;
* clonepermissionsfrom - when you are adding a new capability, you can tell Moodle to copy the permissions for each role from the current settings for another capabilty. This may give better defaults than just using archetypes for administrators who have heavily customised their roles configuration. The full syntax is: &amp;lt;tt&amp;gt;&#039;clonepermissionsfrom&#039; =&amp;gt; &#039;moodle/quiz:attempt&#039;,&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &#039;&#039;In releases before May 2012 clonepermissionsfrom works only inside individual plugins or only in core, in later releases plugins may also clone permissions from core, success of other cloning operations depends on upgrade order.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It is necessary to bump up plugin version number after any change in db/access.php, so that the upgrade scripts can make the necessary changes to the database.  To run the upgrade scripts, log in to Moodle as administrator, navigate to the site home page, and follow the instructions.  (If you need to test the upgrade script without changing the plugin version, it is also possible to set back the version number in the mdl_block or mdl_modules table in the database.)&lt;br /&gt;
&lt;br /&gt;
The capability names are defined in plugin language files, the name of the string consists of &amp;quot;pluginname:capabilityname&amp;quot;, in the example above it would be:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;folder:managefiles&#039;] = &#039;Manage files in folder module&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful functions and classes==&lt;br /&gt;
&lt;br /&gt;
===Context fetching===&lt;br /&gt;
&lt;br /&gt;
In plugins context instances are usually only instantiated because they are instantiated and deleted automatically by the system.&lt;br /&gt;
&lt;br /&gt;
Fetching by object id:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$systemcontext = context_system::instance();&lt;br /&gt;
$usercontext = context_user::instance($user-&amp;gt;id);&lt;br /&gt;
$categorycontext = context_coursecat::instance($category-&amp;gt;id);&lt;br /&gt;
$coursecontext = context_course::instance($course-&amp;gt;id);&lt;br /&gt;
$contextmodule = context_module::instance($cm-&amp;gt;id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fetching by context id:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$context = context::instance_by_id($contextid);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
* by default exception is thrown if context can not be created&lt;br /&gt;
* deleted users do not have contexts any more&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There are multiple deprecated context related functions since 2.2, it is not necessary to replace them immediately. The following two functions are equivalent to the context fetching examples above:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function get_context_instance($contextlevel, $instance = 0, $strictness = IGNORE_MISSING)&lt;br /&gt;
function get_context_instance_by_id($id, $strictness = IGNORE_MISSING)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Determining that a user has a given capability===&lt;br /&gt;
&lt;br /&gt;
When implementing access control always ask &amp;quot;Does the user have capability to do something?&amp;quot;. It is incorrect to ask &amp;quot;Does the user have a role somewhere?&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
====has_capability()====&lt;br /&gt;
has_capability() is the most important function:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
 function has_capability($capability, context $context, $user = null, $doanything = true)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check whether a user has a particular capability in a given context. For example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$context = context_module::instance($cm-&amp;gt;id);&lt;br /&gt;
has_capability(&#039;mod/folder:managefiles&#039;, $context)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By default checks the capabilities of the current user, but you can pass a different userid. By default will return true for admin users, it is not recommended to use false here.&lt;br /&gt;
&lt;br /&gt;
====require_capability()====&lt;br /&gt;
Function require_capability() is very similar, it is throwing access control exception if user does not have the capability.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function require_capability($capability, context $context, $userid = null, $doanything = true, $errormessage = &#039;nopermissions&#039;, $stringfile = &#039;&#039;) {&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Enrolment functions===&lt;br /&gt;
&lt;br /&gt;
See [[Enrolment API]].&lt;br /&gt;
&lt;br /&gt;
===Other related functions===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
 function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false)&lt;br /&gt;
 function require_course_login($courseorid, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false)&lt;br /&gt;
 function get_users_by_capability(context $context, $capability, $fields = &#039;&#039;, $sort = &#039;&#039;, $limitfrom = &#039;&#039;, $limitnum = &#039;&#039;,&lt;br /&gt;
                                  $groups = &#039;&#039;, $exceptions = &#039;&#039;, $doanything_ignored = null, $view_ignored = null, $useviewallgroups = false)&lt;br /&gt;
 function isguestuser($user = null)&lt;br /&gt;
 function isloggedin()&lt;br /&gt;
 function is_siteadmin($user_or_id = null)&lt;br /&gt;
 function is_guest(context $context, $user = null)&lt;br /&gt;
 function is_viewing(context $context, $user = null, $withcapability = &#039;&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====require_login()====&lt;br /&gt;
&lt;br /&gt;
Each plugin script should include require_login() or require_course_login() after setting up PAGE-&amp;gt;url.&lt;br /&gt;
&lt;br /&gt;
This function does following:&lt;br /&gt;
* it verifies that user is logged in before accessing any course or activities (not-logged-in users can not enter any courses).&lt;br /&gt;
* user is logged in as gu&lt;br /&gt;
* verify access to hidden courses and activities&lt;br /&gt;
* verify experimental groupmembersonly access&lt;br /&gt;
* verify that user is either enrolled or has capability &#039;moodle/course:view&#039; or some enrol plugin gives them temporary guest access&lt;br /&gt;
* logs access to courses&lt;br /&gt;
&lt;br /&gt;
====require_course_login()====&lt;br /&gt;
&lt;br /&gt;
This function is supposed to be used only in activities that want to allow read access to content on the frontpage without logging-in. For example view resource files, reading of glossary  entries, etc.&lt;br /&gt;
&lt;br /&gt;
====isguestuser(), isloggedin() and is_siteadmin()====&lt;br /&gt;
&lt;br /&gt;
These function were previously needed for limiting of access of special accounts. It is usually not necessary any more, because any &#039;&#039;&#039;write&#039;&#039;&#039; or &#039;&#039;&#039;risky&#039;&#039;&#039; capabilities are now automatically prevented in has_capability().&lt;br /&gt;
&lt;br /&gt;
It is strongly discouraged to use is_siteadmin() in activity modules, please use standard capabilities and enrolment status instead.&lt;br /&gt;
&lt;br /&gt;
====is_guest(), is_viewing() and is_enrolled()====&lt;br /&gt;
&lt;br /&gt;
In order to access course data one of these functions must return true for user:&lt;br /&gt;
* is_enrolled() - user has active record in user_enrolments table&lt;br /&gt;
* is_viewing() - user has &#039;moodle/course:view&#039; capability (may access course, but is not considered to be participant)&lt;br /&gt;
* is_guest() - user was given temporary guest access by some enrolment plugin&lt;br /&gt;
&lt;br /&gt;
====get_users_by_capability()====&lt;br /&gt;
&lt;br /&gt;
This method returns list of users with given capability, it ignores enrolment status and should be used only above the course context.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
* [[Core APIs]]&lt;br /&gt;
* [[Roles]]&lt;br /&gt;
* [[Role archetypes]]&lt;br /&gt;
* [[Hardening new Roles system]]&lt;br /&gt;
* [[Roles and modules]]&lt;br /&gt;
* [[NEWMODULE Adding capabilities]]&lt;br /&gt;
* [[New permissions evaluation in 2.0]]&lt;br /&gt;
&lt;br /&gt;
[[Category:API]]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=lib/formslib.php_Form_Definition&amp;diff=49303</id>
		<title>lib/formslib.php Form Definition</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=lib/formslib.php_Form_Definition&amp;diff=49303"/>
		<updated>2016-01-28T09:48:09Z</updated>

		<summary type="html">&lt;p&gt;Demsley: /* select */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Formslib}}&lt;br /&gt;
== &#039;&#039;definition()&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
The definition of the elements to be included in the form, their &#039;types&#039; (PARAM_*), helpbuttons included, etc. is all included in a function you must define in your class.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;definition()&#039;&#039; is used to define the elements in the form and &#039;&#039;&#039;this definition will be used for validating data submitted as well as for printing the form.&#039;&#039;&#039; For select and checkbox type elements only data that could have been selected will be allowed. And only data that corresponds to a form element in the definition will be accepted as submitted data.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;definition()&#039;&#039; should include all elements that are going to be used on form, some elements may be removed or tweaked later in &#039;&#039;definition_after_data()&#039;&#039;. Please do not create conditional elements in &#039;&#039;definition()&#039;&#039;, the definition() should not directly depend on the submitted data.&lt;br /&gt;
&lt;br /&gt;
Note that the definition function is called when the form class is instantiated. There is no option to (say) manipulate data in the class (that may affect the rendering of the form) between instantiating the form and calling any other methods. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require_once(&amp;quot;$CFG-&amp;gt;libdir/formslib.php&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
class simplehtml_form extends moodleform {&lt;br /&gt;
&lt;br /&gt;
    function definition() {&lt;br /&gt;
        global $CFG;&lt;br /&gt;
       &lt;br /&gt;
        $mform = $this-&amp;gt;_form; // Don&#039;t forget the underscore! &lt;br /&gt;
&lt;br /&gt;
        $mform-&amp;gt;addElement()... // Add elements to your form&lt;br /&gt;
            ...&lt;br /&gt;
    }                           // Close the function&lt;br /&gt;
}                               // Close the class&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
===Passing parameters to the Form===&lt;br /&gt;
&lt;br /&gt;
The constructor for &#039;&#039;moodleform&#039;&#039; allows a number of parameters including one (&#039;&#039;$customdata&#039;&#039;) to permit an array of arbitrary data to be passed to your form. &lt;br /&gt;
&lt;br /&gt;
For example, you can pass the data &amp;quot;$email&amp;quot; and &amp;quot;$username&amp;quot; to the Form&#039;s class for use inside (say) the definition.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
 $mform_simple = new simplehtml_form( null, array(&#039;email&#039;=&amp;gt;$email, &#039;username&#039;=&amp;gt;$username ) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
(the first parameter is $action, &#039;&#039;null&#039;&#039; will case the form action to be determined automatically)&lt;br /&gt;
&lt;br /&gt;
Secondly, inside the form definition you can use those parameters to set the default values to some of the form&#039;s fields&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
 $mform-&amp;gt;addElement(&#039;text&#039;, &#039;email&#039;, get_string(&#039;email&#039;), &#039;maxlength=&amp;quot;100&amp;quot; size=&amp;quot;25&amp;quot; &#039;);&lt;br /&gt;
 $mform-&amp;gt;setType(&#039;email&#039;, PARAM_NOTAGS);&lt;br /&gt;
 $mform-&amp;gt;addRule(&#039;email&#039;, get_string(&#039;missingemail&#039;), &#039;required&#039;, null, &#039;server&#039;);&lt;br /&gt;
 // Set default value by using a passed parameter&lt;br /&gt;
 $mform-&amp;gt;setDefault(&#039;email&#039;,$this-&amp;gt;_customdata[&#039;email&#039;]);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Use Fieldsets to group Form Elements==&lt;br /&gt;
&lt;br /&gt;
You use code like this to open a fieldset with a &#039;&#039;legend&#039;&#039;. &amp;lt;br /&amp;gt;&lt;br /&gt;
(&#039;&#039;&#039;Note&#039;&#039;&#039;: Some themes turn off legends on admin setting pages by using CSS: &amp;lt;nowiki&amp;gt;#adminsettings legend {display:none;}&amp;lt;/nowiki&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;header&#039;, &#039;nameforyourheaderelement&#039;, get_string(&#039;titleforlegened&#039;, &#039;modulename&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can&#039;t yet nest these visible fieldsets unfortunately. But in fact groups of elements are wrapped in invisible fieldsets.&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.5}}&lt;br /&gt;
Since Moodle 2.5 fieldsets without any required fields are collapsed by default. To display these fieldsets on page load, use:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
 $mform-&amp;gt;setExpanded(&#039;foo&#039;)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You close a fieldset with moodle_form&#039;s closeHeaderBefore method. You tell closeHeaderBefore the element before you wish to end the fieldset. A fieldset is automatically closed if you open a new one. You need to use this code only if you want to close a fieldset and the subsequent form elements are not to be enclosed by a visible fieldset (they are still enclosed with an invisibe one with no legend) :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;closeHeaderBefore(&#039;buttonar&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==addElement==&lt;br /&gt;
&lt;br /&gt;
Use the addElement method to add an element to a form. The first few arguments are always the same. The first param is the type of the element to add. The second is the elementname to use which is normally the html name of the element in the form. The third is often the text for the label for the element.&lt;br /&gt;
&lt;br /&gt;
Some examples are below :&lt;br /&gt;
=== button ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;button&#039;, &#039;intro&#039;, get_string(&amp;quot;buttonlabel&amp;quot;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A button element. If you want a submit or cancel button see &#039;submit&#039; element.&lt;br /&gt;
&lt;br /&gt;
=== checkbox ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;checkbox&#039;, &#039;ratingtime&#039;, get_string(&#039;ratingtime&#039;, &#039;forum&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is a simple checkbox. The third parameter for this element is the label to display on the left side of the form. You can also supply a string as a fourth parameter to specify a label that will appear on the right of the element. Checkboxes and radio buttons can be grouped and have individual labels on their right.&lt;br /&gt;
&lt;br /&gt;
You can have a 5th parameter $attributes, as on other elements.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;BEWARE:&#039;&#039;&#039; Unchecked checkboxes return nothing at all (as if they didn&#039;t exist). This can surprise the unwary. You may wish to use advcheckbox instead, which does return a value when not checked. &#039;Advcheckbox&#039; eliminates this problem. &lt;br /&gt;
&lt;br /&gt;
==== advcheckbox ====&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;advcheckbox&#039;, &#039;ratingtime&#039;, get_string(&#039;ratingtime&#039;, &#039;forum&#039;), &#039;Label displayed after checkbox&#039;, array(&#039;group&#039; =&amp;gt; 1), array(0, 1));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Similar to the checkbox above, but with some important improvements:&lt;br /&gt;
&lt;br /&gt;
# The (optional) 5th parameter is a normal $attributes array, normally used to set HTML attributes for the &amp;lt;input&amp;gt; element. However, a special value of &#039;group&#039; can be given, which will add a class name to the element, and enable its grouping for a [[lib/formslib.php_add_checkbox_controller|checkbox controller]]&lt;br /&gt;
#The (optional) 6th parameter is an array of values that will be associated with the checked/unchecked state of the checkbox. With a normal checkbox you cannot choose that value, and in fact an unchecked checkbox will not even be sent with the form data.&lt;br /&gt;
#It returns a 0 value when unchecked. Compare with the ordinary checkbox which does not return anything at all.&lt;br /&gt;
&lt;br /&gt;
=== choosecoursefile ===&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;choosecoursefile&#039;, &#039;mediafile&#039;, get_string(&#039;mediafile&#039;, &#039;lesson&#039;), array(&#039;courseid&#039;=&amp;gt;$COURSE-&amp;gt;id));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Choose a file from the course files area. The fourth option is a list of options for the element. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note: This has been superceded by [[#filepicker|filepicker]] in Moodle 2.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
array(&#039;courseid&#039; =&amp;gt;null,  //if it is null (default then use global $COURSE&lt;br /&gt;
      &#039;height&#039;   =&amp;gt;500,   // height of the popup window&lt;br /&gt;
      &#039;width&#039;    =&amp;gt;750,   // width of the popup window&lt;br /&gt;
      &#039;options&#039;  =&amp;gt;&#039;none&#039;); //options string for the pop up window &lt;br /&gt;
                          //eg. &#039;menubar=0,location=0,scrollbars,resizable&#039;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also pass an optional 5th parameter of attributes, as for other elements. The most useful way of using that is something like &lt;br /&gt;
&amp;lt;code php&amp;gt;array(&#039;maxlength&#039; =&amp;gt; 255, &#039;size&#039; =&amp;gt; 48)&amp;lt;/code&amp;gt;&lt;br /&gt;
to control the maxlength / size of the text box (note size will default to 48 if not specified)&lt;br /&gt;
&lt;br /&gt;
Finally, as this element is a group containing two elements (button + value), you can add validation rules by using the &#039;&#039;&#039;addGroupRule()&#039;&#039;&#039; method in this (complex) way:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;$mform-&amp;gt;addGroupRule(&#039;elementname&#039;, array(&#039;value&#039; =&amp;gt; array(array(list, of, rule, params, but, fieldname))));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where: &#039;&#039;&#039;&amp;quot;elementname&amp;quot;&#039;&#039;&#039; is the name of the choosecoursefile group element, &#039;&#039;&#039;&amp;quot;value&amp;quot;&#039;&#039;&#039; is the name of the text field within the group and the &#039;&#039;&#039;&amp;quot;list, of, addrule, params, but, fieldname&amp;quot;&#039;&#039;&#039; is exactly that, the list of fields in the normal addRule() function but ommiting the first one, the fieldname.&lt;br /&gt;
&lt;br /&gt;
For example, the [http://cvs.moodle.org/moodle/mod/resource/type/file/resource.class.php?view=markup file/url resource type], uses one &amp;quot;choosecoursefile&amp;quot; element, and it controls the maximum length of the field (255) with this use of addGroupRule():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;$mform-&amp;gt;addGroupRule(&#039;reference&#039;, array(&#039;value&#039; =&amp;gt; array(array(get_string(&#039;maximumchars&#039;, &#039;&#039;, 255), &#039;maxlength&#039;, 255, &#039;client&#039;))));&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== date_selector ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;date_selector&#039;, &#039;assesstimefinish&#039;, get_string(&#039;to&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is a date selector. You can select a Day, Month and Year using a group of select boxes. The fourth param here is an array of options. The defaults for the options are :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
array(&lt;br /&gt;
    &#039;startyear&#039; =&amp;gt; 1970, &lt;br /&gt;
    &#039;stopyear&#039;  =&amp;gt; 2020,&lt;br /&gt;
    &#039;timezone&#039;  =&amp;gt; 99,&lt;br /&gt;
    &#039;optional&#039;  =&amp;gt; false&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can override these defaults by supplying an array as fourth param with one or more keys with a value to override the default. You can supply a fifth param of attributes here as well.&lt;br /&gt;
&lt;br /&gt;
=== date_time_selector ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;date_time_selector&#039;, &#039;assesstimestart&#039;, get_string(&#039;from&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is a group of select boxes to select a date (Day Month and Year) and time (Hour and Minute). When submitted, submitted data is processed and a timestamp is passed to $form-&amp;gt;get_data(); the fourth param here is an array of options. The defaults for the options are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
array(&lt;br /&gt;
    &#039;startyear&#039; =&amp;gt; 1970, &lt;br /&gt;
    &#039;stopyear&#039;  =&amp;gt; 2020,&lt;br /&gt;
    &#039;timezone&#039;  =&amp;gt; 99,&lt;br /&gt;
    &#039;step&#039;      =&amp;gt; 5&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can override these defaults by supplying an array as fourth param with one or more keys with a value to override the default. You can supply a fifth param of attributes here as well.&lt;br /&gt;
&lt;br /&gt;
===duration===&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;timelimit&#039;, get_string(&#039;timelimit&#039;, &#039;quiz&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
This field type lets the user input an interval of time. It comprises a text field, where you can type a number, and a dropdown for selecting a unit (days, hours, minutes or seconds). When submitted the value is converted to a number of seconds.&lt;br /&gt;
&lt;br /&gt;
You can add a fourth parameter to give options. At the moment the only option supported is here is an array of options. The defaults for the options is:&lt;br /&gt;
&amp;lt;code php&amp;gt;array(&#039;optional&#039; =&amp;gt; true)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also pass an optional 5th parameter of attributes, as for other elements. The most useful way of using that is something like &lt;br /&gt;
&amp;lt;code php&amp;gt;array(&#039;size&#039; =&amp;gt; 5)&amp;lt;/code&amp;gt;&lt;br /&gt;
to control the size of the text box.&lt;br /&gt;
&lt;br /&gt;
=== editor ===&lt;br /&gt;
&lt;br /&gt;
This replaces the old htmleditor field type. It allows the user to enter rich text content in a variety of formats.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;fieldname&#039;, get_string(&#039;labeltext&#039;, &#039;langfile&#039;));&lt;br /&gt;
$mform-&amp;gt;setType(&#039;fieldname&#039;, PARAM_RAW);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NOTE: It won&#039;t work properly without the setType() as shown.&lt;br /&gt;
&lt;br /&gt;
If you would like to let the user use the filepicker to upload images etc. that are used in the content, then see [[Using_the_File_API_in_Moodle_forms]].&lt;br /&gt;
&lt;br /&gt;
You can supply a fourth param to htmleditor of an array of options that are mostly related to file handling:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
array(&lt;br /&gt;
    &#039;subdirs&#039;=&amp;gt;0,&lt;br /&gt;
    &#039;maxbytes&#039;=&amp;gt;0,&lt;br /&gt;
    &#039;maxfiles&#039;=&amp;gt;0,&lt;br /&gt;
    &#039;changeformat&#039;=&amp;gt;0,&lt;br /&gt;
    &#039;context&#039;=&amp;gt;null,&lt;br /&gt;
    &#039;noclean&#039;=&amp;gt;0,&lt;br /&gt;
    &#039;trusttext&#039;=&amp;gt;0,&lt;br /&gt;
    &#039;enable_filemanagement&#039; =&amp;gt; true);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The option &#039;enable_filemanagement&#039; will display the file management button on true and remove it on false.&lt;br /&gt;
&lt;br /&gt;
To save the data if you don&#039;t care about files:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$formdata = $mform-&amp;gt;get_data();&lt;br /&gt;
$text     = $formdata-&amp;gt;fieldname[&#039;text&#039;];&lt;br /&gt;
$format   = $formdata-&amp;gt;fieldname[&#039;format&#039;];&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: Because the text editor might be &amp;quot;Atto&amp;quot; (depending on user preferences) and Atto has an &amp;quot;autosave&amp;quot; feature - it requires that the combination of $PAGE-&amp;gt;url and this elementid are unique. If not, the autosaved text for a different form may be restored into this form.&lt;br /&gt;
&lt;br /&gt;
=== file ===&lt;br /&gt;
&lt;br /&gt;
File upload input box with browse button. In the form definition type&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
after form submission and validation use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
      ...&lt;br /&gt;
    $mform-&amp;gt;save_files($destination_directory);&lt;br /&gt;
      ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there is no requirement to save the file, you can read the file contents directly into a string as follows...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    $mform-&amp;gt;get_file_content(&#039;attachment&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need advanced settings such as required file, different max upload size or name of uploaded file&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$this-&amp;gt;set_upload_manager(new upload_manager(&#039;attachment&#039;, true, false, $COURSE, false, 0, true, true, false));&lt;br /&gt;
            $mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
            $mform-&amp;gt;addRule(&#039;attachment&#039;, null, &#039;required&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
      ...&lt;br /&gt;
    $mform-&amp;gt;save_files($destination_directory);&lt;br /&gt;
    $newfilename = $mform-&amp;gt;get_new_filename();&lt;br /&gt;
      ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When porting old code it is also possible to use the upload manager directly for processing of uploaded files.&lt;br /&gt;
&lt;br /&gt;
Please note that if using set_upload_manager() it must be before addElement(&#039;file&#039;,..).&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
File uploading was rewritten in 2.0. Please see inline docs for now. This page will be updated when the new API stabilises.&lt;br /&gt;
&lt;br /&gt;
===filepicker===&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
General replacement of &#039;&#039;file&#039;&#039; element.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filepicker&#039;, &#039;userfile&#039;, get_string(&#039;file&#039;), null, array(&#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;accepted_types&#039; =&amp;gt; &#039;*&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
See also [[Using the File API in Moodle forms]]&lt;br /&gt;
&lt;br /&gt;
=== hidden ===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;reply&#039;, &#039;yes&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A hidden element. Set the element name (in this case &#039;&#039;&#039;reply&#039;&#039;&#039;) to the stated value (in this case &#039;&#039;&#039;yes&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
=== html ===&lt;br /&gt;
You can add arbitrary HTML to your Moodle form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;html&#039;, &#039;&amp;lt;div class=&amp;quot;qheader&amp;quot;&amp;gt;&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See [http://moodle.org/mod/forum/discuss.php?d=126935 &amp;quot;Question: Can I put a moodleform inside a table td?&amp;quot;] for a concrete example.&lt;br /&gt;
&lt;br /&gt;
=== htmleditor &amp;amp; format ===&lt;br /&gt;
&lt;br /&gt;
These elements are now deprecated. Please use the [[#editor|editor]] field type instead.&lt;br /&gt;
&lt;br /&gt;
===modgrade===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;modgrade&#039;, &#039;scale&#039;, get_string(&#039;grade&#039;), false);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is a custom element for selecting a grade for any activity module. The fourth argument is whether to include an option for no grade which has a value 0. This select box does include scales. The default is true, include no grade option.&lt;br /&gt;
&lt;br /&gt;
A helpbutton is automatically added.&lt;br /&gt;
&lt;br /&gt;
===modvisible===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;modvisible&#039;, &#039;visible&#039;, get_string(&#039;visible&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is a custom element for selecting a grade visibility in an activity mod update form.&lt;br /&gt;
&lt;br /&gt;
===password===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;password&#039;, &#039;password&#039;, get_string(&#039;label&#039;), $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A password element. Fourth param is an array or string of attributes.&lt;br /&gt;
&lt;br /&gt;
===passwordunmask===&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;passwordunmask&#039;, &#039;password&#039;, get_string(&#039;label&#039;), $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A password element with option to show the password in plaintext. Fourth param is an array or string of attributes.&lt;br /&gt;
&lt;br /&gt;
=== radio ===&lt;br /&gt;
{{Moodle 2.3}}&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$radioarray=array();&lt;br /&gt;
$radioarray[] = $mform-&amp;gt;createElement(&#039;radio&#039;, &#039;yesno&#039;, &#039;&#039;, get_string(&#039;yes&#039;), 1, $attributes);&lt;br /&gt;
$radioarray[] = $mform-&amp;gt;createElement(&#039;radio&#039;, &#039;yesno&#039;, &#039;&#039;, get_string(&#039;no&#039;), 0, $attributes);&lt;br /&gt;
$mform-&amp;gt;addGroup($radioarray, &#039;radioar&#039;, &#039;&#039;, array(&#039; &#039;), false);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Second param names the radio button and should be the same for each button in the group in order to toggle correctly. Third param would be the label for the form element but is generally ignored as this element will always be in a group which has it&#039;s own label. Fourth param is a string, a label to be displayed on the right of the element. The fifth is the value for this radio button. $attributes can be a string or an array of attributes.&lt;br /&gt;
&lt;br /&gt;
It is possible to add help to individual radio buttons but this requires a custom template to be defined for the group elements. See MDL-15571.&lt;br /&gt;
&lt;br /&gt;
Since 2.3 it cannot be statically called anymore, so we need to call createElement from $mform reference.&lt;br /&gt;
&lt;br /&gt;
==== setDefault ====&lt;br /&gt;
&lt;br /&gt;
To set the default for a radio button group as above use the following :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;setDefault(&#039;yesno&#039;, 0);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would make the default &#039;no&#039;.&lt;br /&gt;
&lt;br /&gt;
===select===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;select&#039;, &#039;type&#039;, get_string(&#039;forumtype&#039;, &#039;forum&#039;), $FORUM_TYPES, $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The fourth param for this element is an array of options for the select box. The keys are the values for the option and the value of the array is the text for the option. The fifth param $attributes is optional, see text element for description of attributes param.&lt;br /&gt;
&lt;br /&gt;
It is also possible to create a select with certain options disabled, using [http://stackoverflow.com/questions/2138089/how-can-i-use-quickform-to-add-disabled-select-options/2150275#2150275 this technique].&lt;br /&gt;
&lt;br /&gt;
You can set an &#039;onchange&#039; attribute when adding or creating the select element: &lt;br /&gt;
&lt;br /&gt;
$form-&amp;gt;addElement(&#039;select&#039;, &#039;iselTest&#039;, &#039;Test Select:&#039;, $arrayOfOptions, array(&#039;onchange&#039; =&amp;gt; &#039;javascript:myFunctionToDoSomething();&#039;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====multi-select====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$select = $mform-&amp;gt;addElement(&#039;select&#039;, &#039;colors&#039;, get_string(&#039;colors&#039;), array(&#039;red&#039;, &#039;blue&#039;, &#039;green&#039;), $attributes);&lt;br /&gt;
$select-&amp;gt;setMultiple(true);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====setSelected=====&lt;br /&gt;
&lt;br /&gt;
To set the default selected item in a select element, you can use the &#039;setSelected&#039; method. The &#039;setSelected&#039; can either get a value or an array of values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$options = array(&lt;br /&gt;
    &#039;ff0000&#039; =&amp;gt; &#039;Red&#039;,&lt;br /&gt;
    &#039;00ff00&#039; =&amp;gt; &#039;Green&#039;,&lt;br /&gt;
    &#039;0000ff&#039; =&amp;gt; &#039;Blue&#039;&lt;br /&gt;
);&lt;br /&gt;
$select = $mform-&amp;gt;addElement(&#039;select&#039;, &#039;colors&#039;, get_string(&#039;colors&#039;), $options);&lt;br /&gt;
// This will select the colour blue.&lt;br /&gt;
$select-&amp;gt;setSelected(&#039;0000ff&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or for multpi-selection:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$skillsarray = array(&lt;br /&gt;
    &#039;val1&#039; =&amp;gt; &#039;Skill A&#039;,&lt;br /&gt;
    &#039;val2&#039; =&amp;gt; &#039;Skill B&#039;,&lt;br /&gt;
    &#039;val3&#039; =&amp;gt; &#039;Skill C&#039;&lt;br /&gt;
);&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;select&#039;, &#039;md_skills&#039;, get_string(&#039;skills&#039;, &#039;metadata&#039;), $skillsarray);&lt;br /&gt;
$mform-&amp;gt;getElement(&#039;md_skills&#039;)-&amp;gt;setMultiple(true);&lt;br /&gt;
// This will select the skills A and B.&lt;br /&gt;
$mform-&amp;gt;getElement(&#039;md_skills&#039;)-&amp;gt;setSelected(array(&#039;val1&#039;, &#039;val2&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However you probably don&#039;t want to do this. Instead you probably want to use setDefault, or set it using the form&#039;s setData method.&lt;br /&gt;
&lt;br /&gt;
===selectyesno===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;maxbytes&#039;, get_string(&#039;maxattachmentsize&#039;, &#039;forum&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want a yes / no select box this one automatically translates itself and has value 1 for yes and 0 for no.&lt;br /&gt;
&lt;br /&gt;
===selectwithlink===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$options = array();&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;selectwithlink&#039;, &#039;scaleid&#039;, get_string(&#039;scale&#039;), $options, null, &lt;br /&gt;
    array(&#039;link&#039; =&amp;gt; $CFG-&amp;gt;wwwroot.&#039;/grade/edit/scale/edit.php?courseid=&#039;.$COURSE-&amp;gt;id, &#039;label&#039; =&amp;gt; get_string(&#039;scalescustomcreate&#039;)));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
select type element with options containing link&lt;br /&gt;
===static===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;static&#039;, &#039;description&#039;, get_string(&#039;description&#039;, &#039;exercise&#039;),&lt;br /&gt;
    get_string(&#039;descriptionofexercise&#039;, &#039;exercise&#039;, $COURSE-&amp;gt;students));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is a static element. It should be used with care if it is used to display a static piece of text with a label. The third param is the label and the fourth is the static text itself.&lt;br /&gt;
&lt;br /&gt;
===submit, reset and cancel===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//normally you use add_action_buttons instead of this code&lt;br /&gt;
$buttonarray=array();&lt;br /&gt;
$buttonarray[] = &amp;amp;$mform-&amp;gt;createElement(&#039;submit&#039;, &#039;submitbutton&#039;, get_string(&#039;savechanges&#039;));&lt;br /&gt;
$buttonarray[] = &amp;amp;$mform-&amp;gt;createElement(&#039;reset&#039;, &#039;resetbutton&#039;, get_string(&#039;revert&#039;));&lt;br /&gt;
$buttonarray[] = &amp;amp;$mform-&amp;gt;createElement(&#039;cancel&#039;);&lt;br /&gt;
$mform-&amp;gt;addGroup($buttonarray, &#039;buttonar&#039;, &#039;&#039;, array(&#039; &#039;), false);&lt;br /&gt;
$mform-&amp;gt;closeHeaderBefore(&#039;buttonar&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A &#039;Submit&#039; type element is a submit type form element which will submit the form. A &#039;Reset&#039; will not submit the form but will reset any changes the user has made to form contents. A &#039;Cancel&#039; element cancels form submission. You need to have a branch in your code before you check for get_data() to check if submission has been cancelled with is_cancelled(); See the example on the usage page.&lt;br /&gt;
&lt;br /&gt;
You should name your submit and reset buttons &#039;submitbutton&#039; and &#039;resetbutton&#039; or something similar (not &#039;submit&#039; and &#039;reset&#039;). This avoids problems in JavaScript of collisions between form element names and names of JavaScript methods of the form object.&lt;br /&gt;
&lt;br /&gt;
====add_action_buttons($cancel = true, $submitlabel=null);====&lt;br /&gt;
&lt;br /&gt;
You will normally use this helper function which is a method of moodleform to add all the &#039;action&#039; buttons to the end of your form. A boolean parameter allow you to specify whether to include a cancel button and specify the label for your submit button (pass the result of get_string). Default for the submit button label is get_string(&#039;savechanges&#039;). Note the &#039;&#039;&#039;$this&#039;&#039;&#039; not &#039;&#039;&#039;$mform&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;add_action_buttons();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===text===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;text&#039;, &#039;name&#039;, get_string(&#039;forumname&#039;, &#039;forum&#039;), $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For a simple text element. Your fourth parameter here can be a string or array of attributes for the text element. The following are equivalent :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$attributes=&#039;size=&amp;quot;20&amp;quot;&#039;;&lt;br /&gt;
$attributes=array(&#039;size&#039;=&amp;gt;&#039;20&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Generally you are encouraged to use CSS instead of using attributes for styling.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A format element can be used as a format select box. It will be non-selectable if you&#039;re using an html editor.&lt;br /&gt;
&lt;br /&gt;
The third param for this element is $useHtmlEditor and it defaults to null in which case an html editor is used if the browser and user profile support it.&lt;br /&gt;
&lt;br /&gt;
===textarea===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;textarea&#039;, &#039;introduction&#039;, get_string(&amp;quot;introtext&amp;quot;, &amp;quot;survey&amp;quot;), &#039;wrap=&amp;quot;virtual&amp;quot; rows=&amp;quot;20&amp;quot; cols=&amp;quot;50&amp;quot;&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A textarea element. If you want an htmleditor use htmleditor element. Fourth element here is a string or array of attributes.&lt;br /&gt;
&lt;br /&gt;
===recaptcha===&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;recaptcha&#039;, &#039;recaptcha_field_name&#039;, $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use this recaptcha element to reduce the spam risk in your forms. Third element here is a string or array of attributes. Take care to get an API key from http://recaptcha.net/api/getkey before using this element.&lt;br /&gt;
&lt;br /&gt;
To check whether recaptcha is enabled at site level use:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (!empty($CFG-&amp;gt;recaptchapublickey) &amp;amp;&amp;amp; !empty($CFG-&amp;gt;recaptchaprivatekey)) {&lt;br /&gt;
    //recaptcha is enabled&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===tags===&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;tags&#039;, &#039;field_name&#039;, $lable, $options, $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used for editing a list of tags, for example on a blog post.&lt;br /&gt;
&lt;br /&gt;
There is only one option available, &#039;display&#039;, which should be set to one of the contstants MoodleQuickForm_tags::ONLYOFFICIAL, NOOFFICIAL or DEFAULTUI. This controls whether the official tags are listed for easy selection, or a text area where arbitrary tags may be typed, or both. The default is both.&lt;br /&gt;
&lt;br /&gt;
The value should be set/returned as an array of tags.&lt;br /&gt;
&lt;br /&gt;
===grading===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;grading&#039;, &#039;advancedgrading&#039;, get_string(&#039;grade&#039;).&#039;:&#039;, array(&#039;gradinginstance&#039; =&amp;gt; $gradinginstance));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Custom element for advanced grading plugins.&lt;br /&gt;
&lt;br /&gt;
When adding the &#039;grading&#039; element to the form, developer must pass an object of class gradingform_instance as $attributes[&#039;gradinginstance&#039;]. Otherwise an exception will be thrown.&lt;br /&gt;
&lt;br /&gt;
===questioncategory===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;questioncategory&#039;, &#039;category&#039;, get_string(&#039;category&#039;, &#039;question&#039;),&lt;br /&gt;
    array(&#039;contexts&#039;=&amp;gt;$contexts, &#039;top&#039;=&amp;gt;true, &#039;currentcat&#039;=&amp;gt;$currentcat, &#039;nochildrenof&#039;=&amp;gt;$currentcat));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Creates a drop down element to select a question category.&lt;br /&gt;
&lt;br /&gt;
Options are:&lt;br /&gt;
&#039;&#039;&#039;contexts&#039;&#039;&#039; - (required) context in which question appears&lt;br /&gt;
&#039;&#039;&#039;currentcat&#039;&#039;&#039; - (optional) course category&lt;br /&gt;
&#039;&#039;&#039;top&#039;&#039;&#039; - (optional) if true will put top categories on top&lt;br /&gt;
&#039;&#039;&#039;nochildrenof&#039;&#039;&#039; - (optional) Format categories into an indented list reflecting the tree structure&lt;br /&gt;
&lt;br /&gt;
==addGroup==&lt;br /&gt;
&lt;br /&gt;
A &#039;group&#039; in formslib is just a group of elements that will have a label and will be included on one line. &lt;br /&gt;
&lt;br /&gt;
For example typical code to include a submit and cancel button on the same line : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$buttonarray=array();&lt;br /&gt;
$buttonarray[] =&amp;amp; $mform-&amp;gt;createElement(&#039;submit&#039;, &#039;submitbutton&#039;, get_string(&#039;savechanges&#039;));&lt;br /&gt;
$buttonarray[] =&amp;amp; $mform-&amp;gt;createElement(&#039;submit&#039;, &#039;cancel&#039;, get_string(&#039;cancel&#039;));&lt;br /&gt;
$mform-&amp;gt;addGroup($buttonarray, &#039;buttonar&#039;, &#039;&#039;, array(&#039; &#039;), false);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You use the same arguments for createElement as you do for addElement. Any label for the element in the third argument is normally ignored (but not in the case of the submit buttons above where the third argument is not for a label but is the text for the button).&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a bad example (don&#039;t do this for real, use the &#039;optional&#039; =&amp;gt; true option of the date element): putting a date_selector (which is itself a group of elements) and a checkbox on the same line, note that you can disable every element in the group using the group name &#039;availablefromgroup&#039; but it doesn&#039;t disable the controlling element the &#039;availablefromenabled&#039; checkbox:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$availablefromgroup=array();&lt;br /&gt;
$availablefromgroup[] =&amp;amp; $mform-&amp;gt;createElement(&#039;date_selector&#039;, &#039;availablefrom&#039;, &#039;&#039;);&lt;br /&gt;
$availablefromgroup[] =&amp;amp; $mform-&amp;gt;createElement(&#039;checkbox&#039;, &#039;availablefromenabled&#039;, &#039;&#039;, get_string(&#039;enable&#039;));&lt;br /&gt;
$mform-&amp;gt;addGroup($availablefromgroup, &#039;availablefromgroup&#039;, get_string(&#039;availablefromdate&#039;, &#039;data&#039;), &#039;&amp;amp;nbsp;&#039;, false);&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;availablefromgroup&#039;, &#039;availablefromenabled&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* If you want to put a group inside another array so that you can repeat items, use createElement instead of addGroup:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$group = $mform-&amp;gt;createElement(&#039;group&#039;, &#039;groupname&#039;, get_string(&#039;label&#039;), $groupitems);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* By default, groups modify the names of elements inside them by appending a number. This is often unhelpful, for example if you want to use disabledIf on the element. To prevent this behaviour, set the last parameter to false when creating a group.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$group = $mform-&amp;gt;createElement(&#039;group&#039;, &#039;groupname&#039;, get_string(&#039;label&#039;), $groupitems, null, false);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==addRule==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;elementname&#039;, get_string(&#039;error&#039;), &#039;rule type&#039;, &#039;extraruledata&#039;, &#039;server&#039;(default), false, false);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first param(element) is an element name and second(message) is the error message that will be displayed to the user.&lt;br /&gt;
The third parameter(type) is the type of rule. The fourth param(format) is used for extra data needed with some rules such as minlength and regex. The fifth parameter(validation) validates input data on server or client side, if validation is done on client side then it will be checked on the server side as well.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 * @param    string     $element       Form element name&lt;br /&gt;
 * @param    string     $message       Message to display for invalid data&lt;br /&gt;
 * @param    string     $type          Rule type, use getRegisteredRules() to get types&lt;br /&gt;
 * @param    string     $format        (optional)Required for extra rule data&lt;br /&gt;
 * @param    string     $validation    (optional)Where to perform validation: &amp;quot;server&amp;quot;, &amp;quot;client&amp;quot;&lt;br /&gt;
 * @param    boolean    $reset         Client-side validation: reset the form element to its original value if there is an error?&lt;br /&gt;
 * @param    boolean    $force         Force the rule to be applied, even if the target form element does not exist&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Common Rule Types&#039;&#039;&#039;&lt;br /&gt;
* required &lt;br /&gt;
* maxlength&lt;br /&gt;
* minlength&lt;br /&gt;
* rangelength&lt;br /&gt;
* email&lt;br /&gt;
* regex&lt;br /&gt;
* lettersonly&lt;br /&gt;
* alphanumeric&lt;br /&gt;
* numeric&lt;br /&gt;
* nopunctuation&lt;br /&gt;
* nonzero&lt;br /&gt;
* callback&lt;br /&gt;
* compare&lt;br /&gt;
&lt;br /&gt;
===Server side and Client side===&lt;br /&gt;
In case you use the &#039;&#039;Client side&#039;&#039; validation option, you can mainly check for an empty or not input field. unless you write some &#039;&#039;Client side&#039;&#039; code which will probably be JavaScript functions to verify the data inside the input fields before it is submitted to the server. It could save some time if those functions are short, simple and quick to compute.&lt;br /&gt;
In case you need a more complex validation checks which relay on Moodle&#039;s internal PHP libraries (or other/external PHP libraries) you better use the &#039;&#039;Server side&#039;&#039; validation checks. Where you can query the DB, write complex PHP validation functions and much much more, that are not available (easily) when using JavaScript on the client&#039;s side.&lt;br /&gt;
&lt;br /&gt;
==setHelpButton==&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;lessondefault&#039;, array(&#039;lessondefault&#039;, get_string(&#039;lessondefault&#039;, &#039;lesson&#039;), &#039;lesson&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
First param is an elementname and the second param is an array of params that are passed to helpbutton in weblib.php. Params are :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 * @param string $page  The keyword that defines a help page&lt;br /&gt;
 * @param string $title The title of links, rollover tips, alt tags etc&lt;br /&gt;
 *           &#039;Help with&#039; (or the language equivalent) will be prefixed and &#039;...&#039; will be stripped.&lt;br /&gt;
 * @param string $module Which module is the page defined in&lt;br /&gt;
 * @param mixed $image Use a help image for the link?  (true/false/&amp;quot;both&amp;quot;)&lt;br /&gt;
 * @param boolean $linktext If true, display the title next to the help icon.&lt;br /&gt;
 * @param string $text If defined then this text is used in the page, and&lt;br /&gt;
 *           the $page variable is ignored.&lt;br /&gt;
 * @param boolean $return If true then the output is returned as a string, if false it is printed to the current page.&lt;br /&gt;
 * @param string $imagetext The full text for the helpbutton icon. If empty use default help.gif&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Make sure you don&#039;t set boolean $return to false. &lt;br /&gt;
&lt;br /&gt;
You need to do use this method after addElement();&lt;br /&gt;
&lt;br /&gt;
==addHelpButton==&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addHelpButton(&#039;api_key_field&#039;, &#039;api_key&#039;, &#039;block_extsearch&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 the &amp;quot;setHelpButton&amp;quot; method has been deprecated in favor of the &amp;quot;addHelpButton&amp;quot; method, which has a simplified interface and uses $OUTPUT-&amp;gt;help_icon() on the back end. The following parameters are expected:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param $elementname The name of the form element to add the help button for&lt;br /&gt;
 * @param $identifier The identifier for the help string and its title (see below)&lt;br /&gt;
 * @param $component The component name to look for the help string in&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Unlike in Moodle 1.9, it is no longer necessary to put your help pages in separate HTML files. Instead, the function looks for two strings:&lt;br /&gt;
&lt;br /&gt;
# get_string($identifier, $component) // The title of the help page&lt;br /&gt;
# get_string(&amp;quot;{$identifier}_help&amp;quot;, $component) // The content of the help page&lt;br /&gt;
&lt;br /&gt;
So you will need to have &#039;&#039;&#039;$identifier&#039;&#039;&#039; and &#039;&#039;&#039;{$identifier}_help&#039;&#039;&#039; defined in order for the help button to be created properly. For example the multiple choice question editing form has a button for shuffling the answers. &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addHelpButton(&#039;shuffleanswers&#039;, &#039;shuffleanswers&#039;, &#039;qtype_multichoice&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
and so the language file includes the strings&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;shuffleanswers&#039;] = &#039;Shuffle the choices?&#039;; &lt;br /&gt;
$string[&#039;shuffleanswers_help&#039;] = &#039;If enabled,.....&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
You can also add the language string like&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;shuffleanswers_link&#039;] = &#039;question/shuffleanswers&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
to add a link to more help on Moodle docs. See [[String_API]] for more information about help icons.&lt;br /&gt;
&lt;br /&gt;
==setDefault==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;select&#039;, &#039;grade&#039;, get_string(&#039;gradeforsubmission&#039;, &#039;exercise&#039;), $grades);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;grade&#039;, array(&#039;grade&#039;, get_string(&#039;gradeforsubmission&#039;, &#039;exercise&#039;), &#039;exercise&#039;));&lt;br /&gt;
$mform-&amp;gt;setDefault(&#039;grade&#039;, 100);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set the default of the form value with setDefault($elementname, $value); where elementname is the elementname whose default you want to set and $value is the default to set. We set the defaults for the form in definition(). This default is what is used if no data is loaded into the form with set_data(); eg. on display of the form for an &#039;add&#039; rather than &#039;update&#039; function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;desc&#039;, get_string(&#039;description&#039;));     &lt;br /&gt;
$mform-&amp;gt;setDefault(&#039;desc&#039;, array(&#039;text&#039;=&amp;gt;$defaulttext));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that when setting the default for an editor element you must use an array to define the default &amp;quot;text&amp;quot; value as shown above.&lt;br /&gt;
&lt;br /&gt;
==disabledIf==&lt;br /&gt;
&lt;br /&gt;
For any element or groups of element in a form you can conditionally disable the group or individual element depending on conditions.&lt;br /&gt;
&lt;br /&gt;
You can use $mform-&amp;gt;disabledIf($elementName, $dependentOn, $condition = &#039;notchecked&#039;, $value=&#039;1&#039;)&lt;br /&gt;
&lt;br /&gt;
* elementname can be a group. If you specify a group all elements in the group will be disabled (if dependentOn is in elementname group that is ignored and not disabled). These are the element names you&#039;ve used as the second argument in addElement or addGroup.&lt;br /&gt;
* dependentOn is the actual name of the element as it will appear in html. This can be different to the name used in addGroup particularly but also addElement where you&#039;re adding a complex element like a date_selector. Check the html of your page. You typically make the depedentOn a checkbox or select box.&lt;br /&gt;
* $condition will be &#039;notchecked&#039;, &#039;checked&#039;, &#039;noitemselected&#039;, &#039;eq&#039;, &#039;in&#039; or, if it is anything else, we test for &#039;neq&#039;.&lt;br /&gt;
** If $condition is &#039;eq&#039; or &#039;neq&#039; then we check the value of the dependentOn field and check for equality (==) or nonequality (!=) in js&lt;br /&gt;
** If $condition is &#039;checked&#039; or &#039;notchecked&#039; then we check to see if a checkbox is checked or not.&lt;br /&gt;
** If $condition is &#039;in&#039; then we check to see if a selected item is in the given list or not. (This was introduced in Moodle 2.7+)&lt;br /&gt;
** If $condition is &#039;noitemselected&#039; then we check to see whether nothing is selected in a dropdown list.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
 // Disable my control unless a checkbox is checked.&lt;br /&gt;
 $mform-&amp;gt;disabledIf(&#039;mycontrol&#039;, &#039;somecheckbox&#039;);&lt;br /&gt;
 &lt;br /&gt;
 // Disable my control if a checkbox &#039;&#039;&#039;is&#039;&#039;&#039; checked.&lt;br /&gt;
 $mform-&amp;gt;disabledIf(&#039;mycontrol&#039;, &#039;somecheckbox&#039;, &#039;checked&#039;);&lt;br /&gt;
 &lt;br /&gt;
 // Disable my control when a dropdown has value 42.&lt;br /&gt;
 $mform-&amp;gt;disabledIf(&#039;mycontrol&#039;, &#039;someselect&#039;, &#039;eq&#039;, 42);&lt;br /&gt;
&lt;br /&gt;
 // Disable my control unless a dropdown has value 42.&lt;br /&gt;
 $mform-&amp;gt;disabledIf(&#039;mycontrol&#039;, &#039;someselect&#039;, &#039;neq&#039;, 42);&lt;br /&gt;
&lt;br /&gt;
The possible choices here are in the dependency manager in lib/form/form.js.&lt;br /&gt;
===A tricky case===&lt;br /&gt;
&lt;br /&gt;
You need to take care with disabledIf if you plan to use it with groups of checkboxes.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say you have a group of 5 checkboxes and you want to enable a depending item such as a drop down menu only when the first and the last checkboxes are selected.&lt;br /&gt;
&lt;br /&gt;
To fix ideas:&lt;br /&gt;
&lt;br /&gt;
If the selection in the checkboxes group is:&lt;br /&gt;
&lt;br /&gt;
 mycheck_01 == 1&lt;br /&gt;
 mycheck_02 == 0&lt;br /&gt;
 mycheck_03 == 0&lt;br /&gt;
 mycheck_04 == 0&lt;br /&gt;
 mycheck_05 == 1&lt;br /&gt;
&lt;br /&gt;
the depending item must be enabled while ANY OTHER COMBINATION must disable the drop down menu.&lt;br /&gt;
&lt;br /&gt;
The following code will, apparently, fail:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;dropdownmenu&#039;, &#039;mycheck_01&#039;, &#039;neq&#039;, &#039;1&#039;);&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;dropdownmenu&#039;, &#039;mycheck_02&#039;, &#039;neq&#039;, &#039;0&#039;);&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;dropdownmenu&#039;, &#039;mycheck_03&#039;, &#039;neq&#039;, &#039;0&#039;);&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;dropdownmenu&#039;, &#039;mycheck_04&#039;, &#039;neq&#039;, &#039;0&#039;);&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;dropdownmenu&#039;, &#039;mycheck_05&#039;, &#039;neq&#039;, &#039;1&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fact, once you get the drop down menu enabled, you are free to unselect mycheck_01 whilst still having the depending item enabled.&lt;br /&gt;
This apparent bug occurs because a non-checked checkbox behaves like a non existing mform element. So the js code will not find the element &amp;quot;mycheck_01&amp;quot; and will not apply the corresponding rule.&lt;br /&gt;
&lt;br /&gt;
A working solution for this kind of issue seems to be:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;dropdownmenu&#039;, &#039;mycheck_01&#039;, &#039;notchecked&#039;);&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;dropdownmenu&#039;, &#039;mycheck_02&#039;, &#039;checked&#039;);&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;dropdownmenu&#039;, &#039;mycheck_03&#039;, &#039;checked&#039;);&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;dropdownmenu&#039;, &#039;mycheck_04&#039;, &#039;checked&#039;);&lt;br /&gt;
$mform-&amp;gt;disabledIf(&#039;dropdownmenu&#039;, &#039;mycheck_05&#039;, &#039;notchecked&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
To see a failing example as the one described, try the attachments provided in MDL-38975. See also in MDL-38975 for the working solution in action with modifications suggested by Eloy.&lt;br /&gt;
&lt;br /&gt;
==setType==&lt;br /&gt;
&lt;br /&gt;
PARAM_* types are used to specify how a submitted variable should be cleaned. These should be used for get parameters such as id, course etc. which are used to load a page and also with setType(); method. Every form element should have a type specified except select, radio box and checkbox elements, these elements do a good job of cleaning themselves (only specified options are allowed as user input).&lt;br /&gt;
&lt;br /&gt;
===Most Commonly Used PARAM_* Types===&lt;br /&gt;
&lt;br /&gt;
These are the most commonly used PARAM_* types and their proper uses. More types can be seen in moodlelib.php starting around line 100.&lt;br /&gt;
&lt;br /&gt;
* PARAM_CLEAN is deprecated and you should try to use a more specific type.&lt;br /&gt;
* PARAM_TEXT should be used for cleaning data that is expected to contain multi-lang content. It will strip all html tags. But will still let tags for multilang support through.&lt;br /&gt;
* PARAM_NOTAGS should be used for cleaning data that is expected to be plain text. It will strip *all* html type tags. It will *not* let tags for multilang support through. This should be used for instance for email addresses where no multilang support is appropriate.&lt;br /&gt;
* PARAM_RAW means no cleaning whatsoever, it is used mostly for data from the html editor. Data from the editor is later cleaned before display using format_text() function. PARAM_RAW can also be used for data that is validated by some other way or printed by p() or s().&lt;br /&gt;
* PARAM_INT should be used for integers. PARAM_FLOAT is also available for decimal numbers but is not recommended for user input since it does not work for languages that use , as a decimal separator.&lt;br /&gt;
* PARAM_ACTION is an alias of PARAM_ALPHA and is used for hidden fields specifying form actions.&lt;br /&gt;
&lt;br /&gt;
==disable_form_change_checker==&lt;br /&gt;
&lt;br /&gt;
By default, any Moodle form will pop-up an &amp;quot;Are you sure?&amp;quot; alert if you make some changes and then try to leave the page without saving. Occasionally, that is undesirable, in which case you can call&lt;br /&gt;
&lt;br /&gt;
 $mform-&amp;gt;disable_form_change_checker()&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
* [http://www.midnighthax.com/quickform.php PEAR HTML QuickForm Getting Started Guide] by Keith Edmunds of Midnighthax.com&lt;br /&gt;
* [http://pear.php.net/manual/en/package.html.html-quickform.php PEAR::HTML_QuickForm manual]&lt;br /&gt;
&lt;br /&gt;
[[Category:Formslib]]&lt;br /&gt;
[[Category:Interfaces]]&lt;/div&gt;</summary>
		<author><name>Demsley</name></author>
	</entry>
</feed>