<?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=Nakohdo</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=Nakohdo"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/Special:Contributions/Nakohdo"/>
	<updated>2026-08-06T17:05:56Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Filters&amp;diff=42634</id>
		<title>Filters</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Filters&amp;diff=42634"/>
		<updated>2013-10-16T10:03:28Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Trying out your filter */  corrected typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;p class=&amp;quot;note&amp;quot;&amp;gt;&#039;&#039;&#039;Please note:&#039;&#039;&#039; This page contains information for developers. You may prefer to read the [[:en:Filters| information about filters for teachers and administrators]].&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Filters&#039;&#039;&#039; are a way to automatically transform content before it is output. For example&lt;br /&gt;
* render embedded equations to images (the TeX filter)&lt;br /&gt;
* Links to media files can be automatically converted to an embedded applet for playing the media.&lt;br /&gt;
* Mentions of glossary terms can be automatically converted to links.&lt;br /&gt;
The possibilities are endless. There are a number of standard filters included with Moodle, or you can create your own. Filters are one of the easiest types of plugin to create. This page explains how.&lt;br /&gt;
&lt;br /&gt;
==Before you start==&lt;br /&gt;
&lt;br /&gt;
Go to  Site administration ▶ Plugins ▶ Filters ▶ Common filter settings and set Text cache lifetime to 0 (&amp;quot;No&amp;quot;) while you do development. Otherwise, you will not be able to see the effects of your changes when you edit your filter&#039;s code. (You should also be using the other common developer settings, like developer debug, theme designer mode and so on.)&lt;br /&gt;
&lt;br /&gt;
==Creating a basic filter==&lt;br /&gt;
&lt;br /&gt;
During this tutorial, we will build a simple example filter. We will make one that adds the word &#039;hello&#039; before every occurrence of the word &#039;world&#039;.&lt;br /&gt;
&lt;br /&gt;
1. Since our filter is not part of a module, we should put it inside the &#039;filter&#039; folder. Therefore, we create a directory called &#039;filter/helloworld&#039;.&lt;br /&gt;
&lt;br /&gt;
2. Inside that folder, we create a file called &#039;filter.php&#039;.&lt;br /&gt;
&lt;br /&gt;
3. Inside that PHP file, we define a class called filter_helloworld, that extends the moodle_text_filter class.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class filter_helloworld extends moodle_text_filter {&lt;br /&gt;
    // ...&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
4. Inside that class, we have to define one method, called &#039;filter&#039;. This takes the HTML to be filtered as an argument. The method should then transform that, and return the processed text. Replace the &#039;// ...&#039; above with&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_helloworld extends moodle_text_filter {&lt;br /&gt;
    public function filter($text, array $options = array()) {&lt;br /&gt;
        return str_replace(&#039;world&#039;, &#039;hello world!&#039;, $text);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That is basically all there is to it!&lt;br /&gt;
&lt;br /&gt;
==Giving your filter a name==&lt;br /&gt;
&lt;br /&gt;
To try the new filter, you first have to log in as Administrator and enable it by going to the page Administration ► Plugins ► Filters ► Manage filters.&lt;br /&gt;
&lt;br /&gt;
When you do, you will find that your plugin does not have a name. We missed a step:&lt;br /&gt;
&lt;br /&gt;
5. Inside the &#039;filter/helloworld&#039; folder, create a folder called &#039;lang&#039;, and in there, create a folder called &#039;en&#039;.&lt;br /&gt;
&lt;br /&gt;
6. Inside there, create a file called &#039;filter_helloworld.php&#039;. That is, you have just created the file &#039;filter/helloworld/lang/en/filter_helloworld.php&#039;.&lt;br /&gt;
&lt;br /&gt;
7. In that file, put&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php // $Id$&lt;br /&gt;
// Language string for filter/helloworld.&lt;br /&gt;
&lt;br /&gt;
$string[&#039;filtername&#039;] = &#039;Hello world!&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That may seem a little involved, just to give your filter a name, but it is just [[Places_to_search_for_lang_strings|the standard way Moodle stores language strings for plugins]].&lt;br /&gt;
&lt;br /&gt;
==Trying out your filter==&lt;br /&gt;
&lt;br /&gt;
We had just got to the [[Filters|filters administration screen]]. If you reload that page now, it should now show your filter with its proper name. Turn your filter on now.&lt;br /&gt;
&lt;br /&gt;
Filters are applied to all text that is printed with the [[Output functions|output functions]] format_text(), and, if you have turned on that option, format_string(). So, to see your filter in action, add some content containing the word &#039;world&#039; somewhere, for example, create a test course, and use the word in the course description. When you look at that course in the course listing, you should see that your filter has transformed it.&lt;br /&gt;
&lt;br /&gt;
==Adding a global settings screen==&lt;br /&gt;
&lt;br /&gt;
Some filters can benefit from some settings to let the administrator control how they work. Suppose we want to greet something other than &#039;world&#039;. To add global settings to the filter you need to:&lt;br /&gt;
&lt;br /&gt;
8. Create a file called &#039;filtersettings.php&#039; inside the &#039;filter/helloworld&#039; folder. Use standard &#039;settings.php&#039; file in Moodle 2.6 and later.&lt;br /&gt;
&lt;br /&gt;
9. In the &#039;filtersettings.php&#039; file, put something like:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$settings-&amp;gt;add(new admin_setting_configtext(&#039;filter_helloworld_word&#039;,&lt;br /&gt;
        get_string(&#039;word&#039;, &#039;filter_helloworld&#039;),&lt;br /&gt;
        get_string(&#039;word_desc&#039;, &#039;filter_helloworld&#039;), &#039;world&#039;, PARAM_NOTAGS));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
10. In the language file &#039;filter/helloworld/lang/en/filter_helloworld.php&#039; add the necessary strings:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;word&#039;] = &#039;The thing to greet&#039;;&lt;br /&gt;
$string[&#039;word_desc&#039;] = &#039;The hello world filter will add the word \&#039;hello\&#039; in front of every occurrence of this word in any content.&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
11. Change the filter to use the new setting:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_helloworld extends moodle_text_filter {&lt;br /&gt;
    public function filter($text, array $options = array()) {&lt;br /&gt;
        global $CFG;&lt;br /&gt;
        return str_replace($CFG-&amp;gt;filter_helloworld_word,&lt;br /&gt;
                &amp;quot;hello $CFG-&amp;gt;filter_helloworld_word!&amp;quot;, $text);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In standard Moodle, the censor, mediaplugin and tex filters all provide good examples of of how filters use global configuration like this.&lt;br /&gt;
&lt;br /&gt;
==A note about performance==&lt;br /&gt;
&lt;br /&gt;
One important thing to remember when creating a filter is that the filter will be called to transform every bit of text output using format_text(), and possibly also format_string(). That means that you have to be careful, or you could cause big performance problems. If you have to get data out of the database, try to cache it so that you only do a fixed number of database queries per page load. The Glossary filter is an example of this. (I am not sure how good an example ;-))&lt;br /&gt;
&lt;br /&gt;
==Local configuration==&lt;br /&gt;
&lt;br /&gt;
In addition, in Moodle 2.0, filters can also have different configuration in each context. For example, the glossary could be changes so that in Forum A, you can choose to only link words from a particular glossary, sat Glossary A, while in Forum B you choose to link words from Glossary B.&lt;br /&gt;
&lt;br /&gt;
To do that sort of thing, you need to add a file called filterlocalsettings.php. In it, you must define a [[lib/formslib.php|Moodle form]] that is a subclass of filter_local_settings_form. In addition to the standard formslib methods, you also need to define a save_changes method. There is not a good example of this in the standard Moodle install yet. To continue our example:&lt;br /&gt;
&lt;br /&gt;
12. Create a file called &#039;filterlocalsettings.php&#039; inside the &#039;filter/helloworld&#039; folder.&lt;br /&gt;
&lt;br /&gt;
13. In the &#039;filterlocalsettings.php&#039; file, put:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class helloworld_filter_local_settings_form extends filter_local_settings_form {&lt;br /&gt;
    protected function definition_inner($mform) {&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;word&#039;, get_string(&#039;word&#039;, &#039;filter_helloworld&#039;), array(&#039;size&#039; =&amp;gt; 20));&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;word&#039;, PARAM_NOTAGS);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
14. Extend the filter to use the new setting, if it is present. The filter must be able to work if the setting is not set, for example by falling back to the global or default setting in this case&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class filter_helloworld extends moodle_text_filter {&lt;br /&gt;
    public function filter($text, array $options = array()) {&lt;br /&gt;
        global $CFG;&lt;br /&gt;
        if (isset($this-&amp;gt;localconfig[&#039;word&#039;])) {&lt;br /&gt;
            $word = $this-&amp;gt;localconfig[&#039;word&#039;];&lt;br /&gt;
        } else {&lt;br /&gt;
            $word = $CFG-&amp;gt;filter_helloworld_word;&lt;br /&gt;
        }&lt;br /&gt;
        return str_replace($word, &amp;quot;hello $word!&amp;quot;, $text);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Two types of filter==&lt;br /&gt;
&lt;br /&gt;
In the past, Moodle supported two different types of filter:&lt;br /&gt;
* Stand-alone filters like the one we created above. These live in a folder inside the &#039;filter&#039; folder. For example, in &#039;filter/myfilter&#039;. &#039;filter/tex&#039; is an example of a core filter of this type.&lt;br /&gt;
* Filters that were part of an activity module. In this case, the filter code lives inside the &#039;mod/mymod&#039; folder. &#039;mod/glossary&#039; used to be an example of a core module with a filter.&lt;br /&gt;
The second option no longer exists. All filters live in the filter folder. Of course, a filter may depend on an associated other plugin, like mod_glossary. If so, you should declare that in the [[version.php]] file.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Filters]] how to write filters for Moodle 1.9 and before. Note that a Moodle 1.9 filter will still work in Moodle 2.0, but you should still update your code when you get the chance.&lt;br /&gt;
* [[Filters schema]] - a page containing some ideas and thoughts about modifications to the filters system&lt;br /&gt;
* [[Filters 2.0]] - user documentation about filters.&lt;br /&gt;
* [https://moodle.org/plugins/browse.php?list=category&amp;amp;id=7 - List of filters in the Plugins database].&lt;br /&gt;
&lt;br /&gt;
[[Category:Filters]]&lt;br /&gt;
[[Category:Filter]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Filters&amp;diff=42633</id>
		<title>Filters</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Filters&amp;diff=42633"/>
		<updated>2013-10-16T10:01:32Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Before you start */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;p class=&amp;quot;note&amp;quot;&amp;gt;&#039;&#039;&#039;Please note:&#039;&#039;&#039; This page contains information for developers. You may prefer to read the [[:en:Filters| information about filters for teachers and administrators]].&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Filters&#039;&#039;&#039; are a way to automatically transform content before it is output. For example&lt;br /&gt;
* render embedded equations to images (the TeX filter)&lt;br /&gt;
* Links to media files can be automatically converted to an embedded applet for playing the media.&lt;br /&gt;
* Mentions of glossary terms can be automatically converted to links.&lt;br /&gt;
The possibilities are endless. There are a number of standard filters included with Moodle, or you can create your own. Filters are one of the easiest types of plugin to create. This page explains how.&lt;br /&gt;
&lt;br /&gt;
==Before you start==&lt;br /&gt;
&lt;br /&gt;
Go to  Site administration ▶ Plugins ▶ Filters ▶ Common filter settings and set Text cache lifetime to 0 (&amp;quot;No&amp;quot;) while you do development. Otherwise, you will not be able to see the effects of your changes when you edit your filter&#039;s code. (You should also be using the other common developer settings, like developer debug, theme designer mode and so on.)&lt;br /&gt;
&lt;br /&gt;
==Creating a basic filter==&lt;br /&gt;
&lt;br /&gt;
During this tutorial, we will build a simple example filter. We will make one that adds the word &#039;hello&#039; before every occurrence of the word &#039;world&#039;.&lt;br /&gt;
&lt;br /&gt;
1. Since our filter is not part of a module, we should put it inside the &#039;filter&#039; folder. Therefore, we create a directory called &#039;filter/helloworld&#039;.&lt;br /&gt;
&lt;br /&gt;
2. Inside that folder, we create a file called &#039;filter.php&#039;.&lt;br /&gt;
&lt;br /&gt;
3. Inside that PHP file, we define a class called filter_helloworld, that extends the moodle_text_filter class.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class filter_helloworld extends moodle_text_filter {&lt;br /&gt;
    // ...&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
4. Inside that class, we have to define one method, called &#039;filter&#039;. This takes the HTML to be filtered as an argument. The method should then transform that, and return the processed text. Replace the &#039;// ...&#039; above with&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_helloworld extends moodle_text_filter {&lt;br /&gt;
    public function filter($text, array $options = array()) {&lt;br /&gt;
        return str_replace(&#039;world&#039;, &#039;hello world!&#039;, $text);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That is basically all there is to it!&lt;br /&gt;
&lt;br /&gt;
==Giving your filter a name==&lt;br /&gt;
&lt;br /&gt;
To try the new filter, you first have to log in as Administrator and enable it by going to the page Administration ► Plugins ► Filters ► Manage filters.&lt;br /&gt;
&lt;br /&gt;
When you do, you will find that your plugin does not have a name. We missed a step:&lt;br /&gt;
&lt;br /&gt;
5. Inside the &#039;filter/helloworld&#039; folder, create a folder called &#039;lang&#039;, and in there, create a folder called &#039;en&#039;.&lt;br /&gt;
&lt;br /&gt;
6. Inside there, create a file called &#039;filter_helloworld.php&#039;. That is, you have just created the file &#039;filter/helloworld/lang/en/filter_helloworld.php&#039;.&lt;br /&gt;
&lt;br /&gt;
7. In that file, put&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php // $Id$&lt;br /&gt;
// Language string for filter/helloworld.&lt;br /&gt;
&lt;br /&gt;
$string[&#039;filtername&#039;] = &#039;Hello world!&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That may seem a little involved, just to give your filter a name, but it is just [[Places_to_search_for_lang_strings|the standard way Moodle stores language strings for plugins]].&lt;br /&gt;
&lt;br /&gt;
==Trying out your filter==&lt;br /&gt;
&lt;br /&gt;
We had just got to the [[Filters|filters administration screen]]. If you reload that page now, it should now show your filter with its proper name. Turn your filter on now.&lt;br /&gt;
&lt;br /&gt;
Filters are applied to all text that is printed with the [[Output functions|output functions]] format_text(), and, if you have turned on that option, format_string(). So, to see your filter in action, add some content containing the work &#039;world&#039; somewhere, for example, create a test course, and use the work in the course description. When you look at that course in the course listing, you should see that your filter has transformed it.&lt;br /&gt;
&lt;br /&gt;
==Adding a global settings screen==&lt;br /&gt;
&lt;br /&gt;
Some filters can benefit from some settings to let the administrator control how they work. Suppose we want to greet something other than &#039;world&#039;. To add global settings to the filter you need to:&lt;br /&gt;
&lt;br /&gt;
8. Create a file called &#039;filtersettings.php&#039; inside the &#039;filter/helloworld&#039; folder. Use standard &#039;settings.php&#039; file in Moodle 2.6 and later.&lt;br /&gt;
&lt;br /&gt;
9. In the &#039;filtersettings.php&#039; file, put something like:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$settings-&amp;gt;add(new admin_setting_configtext(&#039;filter_helloworld_word&#039;,&lt;br /&gt;
        get_string(&#039;word&#039;, &#039;filter_helloworld&#039;),&lt;br /&gt;
        get_string(&#039;word_desc&#039;, &#039;filter_helloworld&#039;), &#039;world&#039;, PARAM_NOTAGS));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
10. In the language file &#039;filter/helloworld/lang/en/filter_helloworld.php&#039; add the necessary strings:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;word&#039;] = &#039;The thing to greet&#039;;&lt;br /&gt;
$string[&#039;word_desc&#039;] = &#039;The hello world filter will add the word \&#039;hello\&#039; in front of every occurrence of this word in any content.&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
11. Change the filter to use the new setting:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_helloworld extends moodle_text_filter {&lt;br /&gt;
    public function filter($text, array $options = array()) {&lt;br /&gt;
        global $CFG;&lt;br /&gt;
        return str_replace($CFG-&amp;gt;filter_helloworld_word,&lt;br /&gt;
                &amp;quot;hello $CFG-&amp;gt;filter_helloworld_word!&amp;quot;, $text);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In standard Moodle, the censor, mediaplugin and tex filters all provide good examples of of how filters use global configuration like this.&lt;br /&gt;
&lt;br /&gt;
==A note about performance==&lt;br /&gt;
&lt;br /&gt;
One important thing to remember when creating a filter is that the filter will be called to transform every bit of text output using format_text(), and possibly also format_string(). That means that you have to be careful, or you could cause big performance problems. If you have to get data out of the database, try to cache it so that you only do a fixed number of database queries per page load. The Glossary filter is an example of this. (I am not sure how good an example ;-))&lt;br /&gt;
&lt;br /&gt;
==Local configuration==&lt;br /&gt;
&lt;br /&gt;
In addition, in Moodle 2.0, filters can also have different configuration in each context. For example, the glossary could be changes so that in Forum A, you can choose to only link words from a particular glossary, sat Glossary A, while in Forum B you choose to link words from Glossary B.&lt;br /&gt;
&lt;br /&gt;
To do that sort of thing, you need to add a file called filterlocalsettings.php. In it, you must define a [[lib/formslib.php|Moodle form]] that is a subclass of filter_local_settings_form. In addition to the standard formslib methods, you also need to define a save_changes method. There is not a good example of this in the standard Moodle install yet. To continue our example:&lt;br /&gt;
&lt;br /&gt;
12. Create a file called &#039;filterlocalsettings.php&#039; inside the &#039;filter/helloworld&#039; folder.&lt;br /&gt;
&lt;br /&gt;
13. In the &#039;filterlocalsettings.php&#039; file, put:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class helloworld_filter_local_settings_form extends filter_local_settings_form {&lt;br /&gt;
    protected function definition_inner($mform) {&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;word&#039;, get_string(&#039;word&#039;, &#039;filter_helloworld&#039;), array(&#039;size&#039; =&amp;gt; 20));&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;word&#039;, PARAM_NOTAGS);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
14. Extend the filter to use the new setting, if it is present. The filter must be able to work if the setting is not set, for example by falling back to the global or default setting in this case&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class filter_helloworld extends moodle_text_filter {&lt;br /&gt;
    public function filter($text, array $options = array()) {&lt;br /&gt;
        global $CFG;&lt;br /&gt;
        if (isset($this-&amp;gt;localconfig[&#039;word&#039;])) {&lt;br /&gt;
            $word = $this-&amp;gt;localconfig[&#039;word&#039;];&lt;br /&gt;
        } else {&lt;br /&gt;
            $word = $CFG-&amp;gt;filter_helloworld_word;&lt;br /&gt;
        }&lt;br /&gt;
        return str_replace($word, &amp;quot;hello $word!&amp;quot;, $text);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Two types of filter==&lt;br /&gt;
&lt;br /&gt;
In the past, Moodle supported two different types of filter:&lt;br /&gt;
* Stand-alone filters like the one we created above. These live in a folder inside the &#039;filter&#039; folder. For example, in &#039;filter/myfilter&#039;. &#039;filter/tex&#039; is an example of a core filter of this type.&lt;br /&gt;
* Filters that were part of an activity module. In this case, the filter code lives inside the &#039;mod/mymod&#039; folder. &#039;mod/glossary&#039; used to be an example of a core module with a filter.&lt;br /&gt;
The second option no longer exists. All filters live in the filter folder. Of course, a filter may depend on an associated other plugin, like mod_glossary. If so, you should declare that in the [[version.php]] file.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Filters]] how to write filters for Moodle 1.9 and before. Note that a Moodle 1.9 filter will still work in Moodle 2.0, but you should still update your code when you get the chance.&lt;br /&gt;
* [[Filters schema]] - a page containing some ideas and thoughts about modifications to the filters system&lt;br /&gt;
* [[Filters 2.0]] - user documentation about filters.&lt;br /&gt;
* [https://moodle.org/plugins/browse.php?list=category&amp;amp;id=7 - List of filters in the Plugins database].&lt;br /&gt;
&lt;br /&gt;
[[Category:Filters]]&lt;br /&gt;
[[Category:Filter]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42563</id>
		<title>FirePHP</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42563"/>
		<updated>2013-10-14T16:03:10Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Moodle resources */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
== What is FirePHP? ==&lt;br /&gt;
[http://www.firephp.org/ FirePHP] enables you to log to your Firebug Console using a simple PHP method call. &lt;br /&gt;
&lt;br /&gt;
== FirePHP block for Moodle ==&lt;br /&gt;
* There&#039;s a [http://moodle.org/mod/forum/discuss.php?d=119961 FirePHP plugin for Moodle] in the works. &lt;br /&gt;
* The block will soon be available in the Moodle Plugin Directory: https://moodle.org/plugins/view.php?plugin=block_firephp&lt;br /&gt;
&lt;br /&gt;
== Moodle resources == &lt;br /&gt;
* [https://moodle.org/mod/forum/discuss.php?d=119961 Moodle  General developer forum: FirePHP plugin] - where everything started.&lt;br /&gt;
* The plugin in the [https://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4793  old plugin database]&lt;br /&gt;
* Related tracker issue: MDL-16371&lt;br /&gt;
* Current code on [https://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4793 GitHub]&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Firebug]]&lt;br /&gt;
* [[Moodle Development kit]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer tools|Firebug]]&lt;br /&gt;
[[Category:Firefox extensions|Firebug]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Repository_plugins&amp;diff=42561</id>
		<title>Repository plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Repository_plugins&amp;diff=42561"/>
		<updated>2013-10-14T15:56:14Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Quick Start */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Repository plugins}}&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Repository plugin allow Moodle to bring contents into Moodle from external repositories.&lt;br /&gt;
&lt;br /&gt;
===Prerequisites===&lt;br /&gt;
Before starting coding, it is necessary to know how to use repository administration pages and how to use the file picker.&lt;br /&gt;
&lt;br /&gt;
===Overview===&lt;br /&gt;
&lt;br /&gt;
The 3 different parts to write&lt;br /&gt;
# Administration - You can customise the way administrators and users can configure their repositories. &lt;br /&gt;
# File picker integration - The core of your plugin, it will manage communication between Moodle and the repository service, and also the file picker display.&lt;br /&gt;
# I18n - Internationalization should be done at the same time as you&#039;re writing the other parts.&lt;br /&gt;
&lt;br /&gt;
== History ==&lt;br /&gt;
&lt;br /&gt;
Repository plugins exists from 2.0&lt;br /&gt;
&lt;br /&gt;
== Example ==&lt;br /&gt;
*[[Box.net Repository Plugin|Box.net Repository Plugin]]&lt;br /&gt;
*[[Flickr Repository Plugin|Flickr Repository Plugin]]&lt;br /&gt;
*[[Moodle Repository Plugin|Remote Moodle Repository Plugin]]&lt;br /&gt;
&lt;br /&gt;
==Creating new repository plugin==&lt;br /&gt;
# Create a folder for your plugin in &#039;&#039;/repository/&#039;&#039; e.g. &#039;&#039;/repository/myplugin&#039;&#039;&lt;br /&gt;
# Create the following files in your plugin folder:&lt;br /&gt;
#* &#039;&#039;/repository/myplugin/lib.php&#039;&#039;&lt;br /&gt;
#* &#039;&#039;/repository/myplugin/pix/icon.png&#039;&#039; - the icon displayed in the file picker (16x16)&lt;br /&gt;
#* &#039;&#039;/repository/myplugin/[[version.php]]&#039;&#039;&lt;br /&gt;
#* &#039;&#039;/repository/myplugin/lang/en/repository_myplugin.php&#039;&#039; - language file&lt;br /&gt;
#* &#039;&#039;/repository/myplugin/db/access.php&#039;&#039;&lt;br /&gt;
# Declare class &#039;&#039;&#039;repository_myplugin extends repository&#039;&#039;&#039; in your lib.php&lt;br /&gt;
# In your repository_myplugin class overwrite function get_listing() to &#039;&#039;&#039;return array(&#039;list&#039; =&amp;gt; array());&#039;&#039;&#039;&lt;br /&gt;
# Add at least strings &#039;&#039;&#039;$string[&#039;pluginname&#039;]&#039;&#039;&#039; and &#039;&#039;&#039;$string[&#039;configplugin&#039;]&#039;&#039;&#039; to your language file&lt;br /&gt;
# Add capability &#039;repository/myplugin:view&#039; to your access.php file&lt;br /&gt;
# Create install and upgrade scripts (optional or you can do it later)&lt;br /&gt;
# Login as admin on your website and run upgrade&lt;br /&gt;
# Open Site Administration-&amp;gt;Plugins-&amp;gt;Repositories-&amp;gt;Manage Repositories and make your repository &#039;Enabled and visible&#039;&lt;br /&gt;
&lt;br /&gt;
For a more detailed explanation of each of each of the files that have been created here, along with code examples, see [[Repository plugin files]].&lt;br /&gt;
&lt;br /&gt;
==Administration APIs==&lt;br /&gt;
&lt;br /&gt;
===Fixed settings===&lt;br /&gt;
&lt;br /&gt;
These are settings that are hard-coded into your repository plugin and can only be updated by changing the plugin code.&lt;br /&gt;
&lt;br /&gt;
====supported_returntypes()====&lt;br /&gt;
Return any combination of the following values:&lt;br /&gt;
* FILE_INTERNAL - the file is uploaded/downloaded and stored directly within the Moodle file system&lt;br /&gt;
* FILE_EXTERNAL - the file stays in the external repository and is accessed from there directly&lt;br /&gt;
* FILE_REFERENCE - the file may be cached locally, but is automatically synchronised, as required, with any changes to the external original&lt;br /&gt;
The type used by Moodle depends on the choices made by the end user (e.g. inserting a link, will result in &#039;FILE_EXTERNAL&#039;-related functions being used, using a &#039;shortcut/alias&#039; will result in the &#039;FILE_REFERENCE&#039;-related functions being used).&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function supported_returntypes() {&lt;br /&gt;
    return FILE_REFERENCE|FILE_INTERNAL|FILE_EXTERNAL;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====supported_filetypes()====&lt;br /&gt;
Optional. Returns &#039;*&#039; for all file types (default implementation), or an array of types or groups (e.g. array(&#039;text/plain&#039;, &#039;image/gif&#039;, &#039;web_image&#039;) )&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function supported_filetypes() {&lt;br /&gt;
    //return &#039;*&#039;;&lt;br /&gt;
    //return array(&#039;image/gif&#039;, &#039;image/jpeg&#039;, &#039;image/png&#039;);&lt;br /&gt;
    return array(&#039;web_image&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
For a full list of possible types and groups, look in lib/filelib.php, function get_mimetypes_array().&lt;br /&gt;
&lt;br /&gt;
===Global settings===&lt;br /&gt;
&lt;br /&gt;
These are settings that are configured for the whole Moodle site and not per instance of your plugin. All of these are optional, without them there will be no configuration options in the Site administration &amp;gt; Plugins &amp;gt; Repositories &amp;gt; Myplugin page.&lt;br /&gt;
&lt;br /&gt;
====get_type_option_names()====&lt;br /&gt;
&#039;&#039;This function must be declared static&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Optional. Return an array of string. These strings are setting names. These settings are shared by all instances.&lt;br /&gt;
Parent function returns an empty array.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_type_option_names() {&lt;br /&gt;
   return array_merge(parent::get_type_option_names(), array(&#039;rootpath&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====type_config_form($mform, $classname=&#039;repository&#039;)====&lt;br /&gt;
Optional. This is for modifying the Moodle form displaying the plugin settings. [[lib/formslib.php Form Definition]] has details of all the types of elements you can add to the settings form.&lt;br /&gt;
&lt;br /&gt;
For example, to display the standard repository plugin settings along with the custom ones use:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function type_config_form($mform) {&lt;br /&gt;
    parent::type_config_form($mform);&lt;br /&gt;
&lt;br /&gt;
    $rootpath = get_config(&#039;repository_someplugin&#039;, &#039;rootpath&#039;);&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;rootpath&#039;, get_string(&#039;rootpath&#039;, &#039;repository_someplugin&#039;), array(&#039;size&#039; =&amp;gt; &#039;40&#039;));&lt;br /&gt;
    $mform-&amp;gt;setDefault(&#039;rootpath&#039;, $rootpath);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====type_form_validation($mform, $data, $errors)====&lt;br /&gt;
Optional. Use this function if you need to validate some variables submitted by plugin settings form. To use it, check through the associative array of data provided (&#039;settingname&#039; =&amp;gt; value) for any errors. Then push the items to $error array in the format (&amp;quot;fieldname&amp;quot; =&amp;gt; &amp;quot;human readable error message&amp;quot;) to have them highlighted in the form.&lt;br /&gt;
&lt;br /&gt;
With the example above, this function may look like:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function type_form_validation($mform, $data, $errors) {&lt;br /&gt;
    if (!is_dir($data[&#039;rootpath&#039;])) {&lt;br /&gt;
        $errors[&#039;rootpath&#039;] = get_string(&#039;invalidrootpath&#039;, &#039;repository_someplugin&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    return $errors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Instance settings===&lt;br /&gt;
These functions relate to a specific instance of your plugin (e.g. the URL and login details to access a specific webdav repository). All of these are optional, without them, the instance settings form will only contain a single &#039;name&#039; field.&lt;br /&gt;
&lt;br /&gt;
==== get_instance_option_names()====&lt;br /&gt;
&#039;&#039;This function must be declared static&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Optional. Return an array of strings. These strings are setting names. These settings are specific to an instance.&lt;br /&gt;
If the function returns an empty array, the API will consider that the plugin displays only one repository in the file picker.&lt;br /&gt;
Parent function returns an empty array. This is equivalent to &#039;&#039;get_type_option_names()&#039;&#039;, but for a specific instance.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_instance_option_names() {&lt;br /&gt;
    return array(&#039;fs_path&#039;); // From repository_filesystem&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====instance_config_form($mform)====&lt;br /&gt;
Optional. This is for modifying the Moodle form displaying the settings specific to an instance. This is equivalent to &#039;&#039;type_config_form($mform, $classname)&#039;&#039; but for instances. [[lib/formslib.php Form Definition]] has details of all the types of elements you can add to the settings form.&lt;br /&gt;
&lt;br /&gt;
For example, to add a required text box called email_address:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;text&#039;, &#039;email_address&#039;, get_string(&#039;emailaddress&#039;, &#039;repository_flickr_public&#039;));&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;email_address&#039;, $strrequired, &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
   &lt;br /&gt;
&#039;&#039;Note: &#039;&#039;mform&#039;&#039; has by default a name text box (cannot be removed).&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Parent function does nothing.&lt;br /&gt;
&lt;br /&gt;
====instance_form_validation($mform, $data, $errors)====&lt;br /&gt;
Optional. This allows us to validate what has been submitted in the instance configuration form. This is equivalent to &#039;&#039;type_form_validation($mform, $data, $errors), but for instances. For example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function instance_form_validation($mform, $data, $errors) {&lt;br /&gt;
    if (empty($data[&#039;email_address&#039;])) {&lt;br /&gt;
        $errors[&#039;email_address&#039;] = get_string(&#039;invalidemailsettingname&#039;, &#039;repository_flickr_public&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    return $errors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Getting / updating settings====&lt;br /&gt;
&lt;br /&gt;
Both global and instance settings can be retrieved, from within the plugin, via $this-&amp;gt;get_option(&#039;settingname&#039;) and updated via $this-&amp;gt;set_option(array(&#039;settingname&#039; =&amp;gt; &#039;value&#039;)).&lt;br /&gt;
&lt;br /&gt;
====plugin_init()====&lt;br /&gt;
&#039;&#039;This function must be declared static&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Optional. This function is called when the administrator adds the plugin. So unless the administrator deletes the plugin and re-adds it, it should be called only once.&lt;br /&gt;
Parent function does nothing.&lt;br /&gt;
&lt;br /&gt;
===Example of using the settings===&lt;br /&gt;
&lt;br /&gt;
As an example, let&#039;s create a Flickr plugin for accessing a public flickr account. The plugin will be called &amp;quot;Flickr Public&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Firstly the skeleton:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * repository_flickr_public class&lt;br /&gt;
 * Moodle user can access public flickr account&lt;br /&gt;
 *&lt;br /&gt;
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License&lt;br /&gt;
*/&lt;br /&gt;
class repository_flickr_public extends repository {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then consider the question &amp;quot;What does my plugin do?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In the Moodle file picker, we want to display some flickr public repositories directly linked to a flickr public account. For example &#039;&#039;My Public Flickr Pictures&#039;&#039;, and also &#039;&#039;My Friend&#039;s Flickr Pictures&#039;&#039;. When the user clicks on one of these repositories, the public pictures are displayed in the file picker.&lt;br /&gt;
&lt;br /&gt;
In order to access to a flickr public account, the plugin needs to know the email address of the Flickr public account owner. So the administrator will need to set an email address for every repository. Let&#039;s add an &amp;quot;email address&amp;quot; setting to every repository.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
//We tell the API that the repositories have specific settings: &amp;quot;email address&amp;quot;&lt;br /&gt;
    public static function get_instance_option_names() {&lt;br /&gt;
        return array(&#039;email_address&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
//We add an &amp;quot;email address&amp;quot; text box to the create/edit repository instance Moodle form&lt;br /&gt;
    public function instance_config_form($mform) {&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;email_address&#039;, get_string(&#039;emailaddress&#039;, &#039;repository_flickr_public&#039;));&lt;br /&gt;
        $mform-&amp;gt;addRule(&#039;email_address&#039;, get_string(&#039;required&#039;), &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So at this moment all our Flickr Public Repositories will have a specific email address. However this is not enough. In order to communicate with Flickr, Moodle needs to know a Flickr API key (http://www.flickr.com/services/api/). This API key is the same for any repository. We could add it with the email address setting but the administrator would have to enter the same API key for every repository. Hopefully the administrator can add settings to the plugin level, impacting all repositories. The code is similar the repository instance settings:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
//We tell the API that the repositories have general settings: &amp;quot;api_key&amp;quot;&lt;br /&gt;
    public static function get_type_option_names() {&lt;br /&gt;
        return array(&#039;api_key&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
//We add an &amp;quot;api key&amp;quot; text box to the create/edit repository plugin Moodle form (also called a Repository type Moodle form)&lt;br /&gt;
    public function type_config_form($mform) {&lt;br /&gt;
        //the following line is needed in order to retrieve the API key value from the database when Moodle displays the edit form&lt;br /&gt;
        $api_key = get_config(&#039;flickr_public&#039;, &#039;api_key&#039;);&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;api_key&#039;, get_string(&#039;apikey&#039;, &#039;repository_flickr_public&#039;), &lt;br /&gt;
                           array(&#039;value&#039;=&amp;gt;$api_key,&#039;size&#039; =&amp;gt; &#039;40&#039;));&lt;br /&gt;
        $mform-&amp;gt;addRule(&#039;api_key&#039;, get_string(&#039;required&#039;), &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Have we finished yet?&lt;br /&gt;
&lt;br /&gt;
Yes! We have created everything necessary for the administration pages. But let&#039;s go further. It would be good if the user can enter any &amp;quot;Flickr public account email address&amp;quot; in the file picker. In fact we want to display in the file picker a Flickr Public repository that the Moodle administrator can never delete. Let&#039;s add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
     //this function is only called one time, when the Moodle administrator add the Flickr Public Plugin into the Moodle site.&lt;br /&gt;
     public static function plugin_init() {&lt;br /&gt;
        //here we create a default repository instance. The last parameter is 1 in order to set the instance as readonly.&lt;br /&gt;
        repository::static_function(&#039;flickr_public&#039;,&#039;create&#039;, &#039;flickr_public&#039;, 0, get_system_context(), &lt;br /&gt;
                                    array(&#039;name&#039; =&amp;gt; &#039;default instance&#039;,&#039;email_address&#039; =&amp;gt; null),1);&lt;br /&gt;
     }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all - the administration part of our Flickr Public plugin is done. For your information, Box.net, Flickr, and Flickr Public all have similar administration APIs.&lt;br /&gt;
&lt;br /&gt;
==Repository APIs==&lt;br /&gt;
=== Quick Start ===&lt;br /&gt;
First of all, the File Picker using intensively Ajax you will need a easy way to debug. Install [[FirePHP]] (MDL-16371) and make it works. It will save you a lot of time. (You might give the [http://moodle.org/mod/forum/discuss.php?d=119961 FirePHP plugin for Moodle] a try, it&#039;s still work in progress, though.)&lt;br /&gt;
&lt;br /&gt;
* Your first question when you write your plugin specification is &#039;Does the user need to log-in&#039;? If they do, in your plugin you have to detect user session in constructor() function, and use print_login() if required, see more details below.&lt;br /&gt;
* For most of plugins, you need to establish a connection with the remote repository. This connection can be done into the get_listing(), constructor() function, see more details below.&lt;br /&gt;
* You wanna retrieve the file that the user selected, rewrite get_file() if required, see more details below.&lt;br /&gt;
* Optional question that you should ask yourself is &#039;Does the user can execute a search&#039;, if they do, you will have to rewrite search() method, see more details below.&lt;br /&gt;
&lt;br /&gt;
===Functions you *MUST* override===&lt;br /&gt;
&lt;br /&gt;
These functions cover the basics of initialising your plugin each time the repository is accessed and listing the files available to the user from within the plugin.&lt;br /&gt;
&lt;br /&gt;
====__construct($respoitoryid, $context=SYSCONTEXTID, $options=array(), $readonly=0)====&lt;br /&gt;
Should be overridden to do any initialisation required by the repository, including:&lt;br /&gt;
* logging in via optional_param, if required - see &#039;print_login&#039;, below&lt;br /&gt;
* getting any options from the database&lt;br /&gt;
&lt;br /&gt;
The possible items in the $options array are:&lt;br /&gt;
* &#039;ajax&#039; - bool, true if the user is using the AJAX filepicker&lt;br /&gt;
* &#039;mimetypes&#039; - array of accepted mime types, or &#039;*&#039; for all types&lt;br /&gt;
&lt;br /&gt;
Calling parent::__construct($repositoryid, $context, $options, $readonly); is essential and will set up various required member variables:&lt;br /&gt;
* $this-&amp;gt;id - the repository instance id (the ID of the entry in mdl_repository_instances)&lt;br /&gt;
* $this-&amp;gt;context - the context in which the repository instance can be found&lt;br /&gt;
* $this-&amp;gt;instance - the repository instance record (from mdl_repository_instances)&lt;br /&gt;
* $this-&amp;gt;readonly - whether or not the settings can be changed&lt;br /&gt;
* $this-&amp;gt;options - the above options, combined with the settings saved in the database&lt;br /&gt;
* $this-&amp;gt;name - as specified by $this-&amp;gt;get_name()&lt;br /&gt;
* $this-&amp;gt;returntypes - as specified by $this-&amp;gt;supported_returntypes()&lt;br /&gt;
&lt;br /&gt;
====get_listing($path=&amp;quot;&amp;quot;, $page=&amp;quot;&amp;quot;)====&lt;br /&gt;
This function will return a list of files to be displayed to the user, the list must be a array like this:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$list = array(&lt;br /&gt;
 //this will be used to build navigation bar&lt;br /&gt;
&#039;path&#039;=&amp;gt;array(array(&#039;name&#039;=&amp;gt;&#039;root&#039;,&#039;path&#039;=&amp;gt;&#039;/&#039;), array(&#039;name&#039;=&amp;gt;&#039;subfolder&#039;, &#039;path&#039;=&amp;gt;&#039;/subfolder&#039;)),&lt;br /&gt;
&#039;manage&#039;=&amp;gt;&#039;http://webmgr.moodle.com&#039;,&lt;br /&gt;
&#039;list&#039;=&amp;gt; array(&lt;br /&gt;
    array(&#039;title&#039;=&amp;gt;&#039;filename1&#039;, &#039;date&#039;=&amp;gt;&#039;1340002147&#039;, &#039;size&#039;=&amp;gt;&#039;10451213&#039;, &#039;source&#039;=&amp;gt;&#039;http://www.moodle.com/dl.rar&#039;),&lt;br /&gt;
    array(&#039;title&#039;=&amp;gt;&#039;folder&#039;, &#039;date&#039;=&amp;gt;&#039;1340002147&#039;, &#039;size&#039;=&amp;gt;&#039;0&#039;, &#039;children&#039;=&amp;gt;array())&lt;br /&gt;
)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Amongst other details, this returns a &#039;&#039;&#039;title&#039;&#039;&#039; for each file (to be displayed in the filepicker) and the &#039;&#039;&#039;source&#039;&#039;&#039; for the file (which will be included in the request to &#039;download&#039; the file into Moodle or to generate a link to the file). Directories return a &#039;&#039;&#039;children&#039;&#039;&#039; value, which is either an empty array (if &#039;dynload&#039; is specified) or an array of the files and directories contained within it.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;The full specification of list element:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
 array(&lt;br /&gt;
   // &#039;path&#039; is used to build navegation bar to show the current folder, so you need to include all parents folders&lt;br /&gt;
   // array(array(&#039;name&#039;=&amp;gt;&#039;root&#039;,&#039;path&#039;=&amp;gt;&#039;/&#039;), array(&#039;name&#039;=&amp;gt;&#039;subfolder&#039;, &#039;path&#039;=&amp;gt;&#039;/subfolder&#039;))&lt;br /&gt;
   // This will result in: /root/subfolder as current directory&lt;br /&gt;
   &#039;path&#039; =&amp;gt; (array) this will be used to build navigation bar&lt;br /&gt;
   // &#039;dynload&#039; tells file picker to fetch list dynamically.&lt;br /&gt;
   // When user clicks the folder, it will send a ajax request to server side.&lt;br /&gt;
   // Default value is false but note that non-Javascript file picker always acts as if dynload was set to true&lt;br /&gt;
   &#039;dynload&#039; =&amp;gt; (bool) use dynamic loading,&lt;br /&gt;
   // if you are using pagination, &#039;page&#039; and &#039;pages&#039; parameters should be set.&lt;br /&gt;
   // It is not recommended to use pagination and subfolders at the same time, the tree view mode can not handle it correctly&lt;br /&gt;
   &#039;page&#039; =&amp;gt; (int) which page is this list&lt;br /&gt;
   &#039;pages&#039; =&amp;gt; (int) how many pages. If number of pages is unknown but we know that the next page exists repository may return -1&lt;br /&gt;
   &#039;manage&#039; =&amp;gt; (string) url to file manager for the external repository, if specified will display link in file picker&lt;br /&gt;
   &#039;help&#039; =&amp;gt; (string) url to the help window, if specified will display link in file picker&lt;br /&gt;
   &#039;nologin&#039; =&amp;gt; (bool) requires login, default false, if set to true the login link will be removed from file picker&lt;br /&gt;
   &#039;norefresh&#039; =&amp;gt; (bool) no refresh button, default false&lt;br /&gt;
   &#039;logouttext&#039; =&amp;gt; (string) in case of nologin=false can substitute the text &#039;Logout&#039; for logout link in file picker&lt;br /&gt;
   &#039;nosearch&#039; =&amp;gt; (bool) no search link, default false, if set to true the search link will be removed from file picker&lt;br /&gt;
   &#039;issearchresult&#039; =&amp;gt; (bool) tells that this listing is the result of search&lt;br /&gt;
   // for repositories that actually upload a file: set &#039;upload&#039; option to display an upload form in file picker&lt;br /&gt;
   &#039;upload&#039; =&amp;gt; array( // upload manager&lt;br /&gt;
     &#039;label&#039; =&amp;gt; (string) label of the form element,&lt;br /&gt;
     &#039;id&#039; =&amp;gt; (string) id of the form element&lt;br /&gt;
   ),&lt;br /&gt;
   // &#039;list&#039; is used by file picker to build a file/folder tree&lt;br /&gt;
   &#039;list&#039; =&amp;gt; array(&lt;br /&gt;
     array( // file&lt;br /&gt;
       &#039;title&#039; =&amp;gt; (string) file name,&lt;br /&gt;
       &#039;shorttitle&#039; =&amp;gt; (string) optional, if you prefer to display a short title&lt;br /&gt;
       &#039;date&#039; =&amp;gt; (int) UNIX timestamp, default value for datemodified and datecreated,&lt;br /&gt;
       &#039;datemodified&#039; =&amp;gt; (int) UNIX timestamp when the file was last modified [2.3+],&lt;br /&gt;
       &#039;datecreated&#039; =&amp;gt; (int) UNIX timestamp when the file was last created [2.3+],&lt;br /&gt;
       &#039;size&#039; =&amp;gt; (int) file size in bytes,&lt;br /&gt;
       &#039;thumbnail&#039; =&amp;gt; (string) url to thumbnail for the file,&lt;br /&gt;
       &#039;thumbnail_width&#039; =&amp;gt; (int) the width of the thumbnail image,&lt;br /&gt;
       &#039;thumbnail_height&#039; =&amp;gt; (int) the height of the thumbnail image,&lt;br /&gt;
       &#039;source&#039; =&amp;gt; plugin-dependent unique path to the file (id, url, path, etc.),&lt;br /&gt;
       &#039;url&#039; =&amp;gt; the accessible url of file,&lt;br /&gt;
       &#039;icon&#039; =&amp;gt; (string) url to icon of the image (24x24px), if omitted the moodle filetype icon will be used [2.3+],&lt;br /&gt;
       &#039;realthumbnail&#039; =&amp;gt; (string) url to image preview to be lazy-loaded when scrolled to it (if it requires to be generated and can not be returned as &#039;thumbnail&#039;) [2.3+],&lt;br /&gt;
       &#039;realicon&#039; =&amp;gt; (string) url to image preview in icon size (24x24) [2.3+],&lt;br /&gt;
       &#039;author&#039; =&amp;gt; (string) default value for file author,&lt;br /&gt;
       &#039;license&#039; =&amp;gt; (string) default value for license (short name, see class license_manager),&lt;br /&gt;
       &#039;image_height&#039; =&amp;gt; (int) if the file is an image, image height in pixels, null otherwise [2.3+],&lt;br /&gt;
       &#039;image_width&#039; =&amp;gt;  (int) if the file is an image, image width in pixels, null otherwise [2.3+]&lt;br /&gt;
     ),&lt;br /&gt;
     array( // folder - similar to file, has also &#039;path&#039; and &#039;children&#039; but no &#039;source&#039; or &#039;url&#039;&lt;br /&gt;
       &#039;title&#039; =&amp;gt; (string) folder name,&lt;br /&gt;
       &#039;shorttitle&#039; =&amp;gt; (string) optional, if you prefer to display a short title&lt;br /&gt;
       &#039;path&#039; =&amp;gt; (string) path to this folder. In case of dynload=true (and for non-JS filepicker) the value will be passed to repository_xxx::get_listing() in order to retrieve children&lt;br /&gt;
       &#039;date&#039;, &#039;datemodified&#039;, &#039;datecreated&#039;, &#039;thumbnail&#039;, &#039;icon&#039; =&amp;gt; see above,&lt;br /&gt;
       &#039;children&#039; =&amp;gt; array( &lt;br /&gt;
         // presence of this attribute actually tells file picker that this is a folder. In case of dynload=true, it should be empty array&lt;br /&gt;
         // otherwise it is a nested list of contained files and folders&lt;br /&gt;
       )&lt;br /&gt;
     ),&lt;br /&gt;
   )&lt;br /&gt;
// The &#039;object&#039; tag can be used to embed an external web page or application within the filepicker&lt;br /&gt;
   &#039;object&#039; =&amp;gt; array(&lt;br /&gt;
      &#039;type&#039; =&amp;gt; (string) e.g. &#039;text/html&#039;, &#039;application/x-shockwave-flash&#039;&lt;br /&gt;
      &#039;src&#039; =&amp;gt; (string) the website address to embed in the object&lt;br /&gt;
   )&lt;br /&gt;
 )&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Dynamically loading&lt;br /&gt;
Some repositories contain many files which cannot load in one time, in this case, we need dynamically loading to fetch them step by step, files in subfolder won&#039;t be listed until user click the folder in file picker treeview.&lt;br /&gt;
&lt;br /&gt;
As a plug-in developer, if you set dynload flag as &#039;&#039;&#039;true&#039;&#039;&#039;, you should return files and folders (set children as a null array) in current path only instead of building the whole file tree.&lt;br /&gt;
&lt;br /&gt;
Example of dynamically loading&lt;br /&gt;
See [http://cvs.moodle.org/moodle/repository/alfresco/lib.php?view=log Alfresco] plug-in&lt;br /&gt;
&lt;br /&gt;
The use of the &#039;&#039;&#039;object&#039;&#039;&#039; tag, instead of returning a &#039;&#039;list&#039;&#039; of files, allows you to embed an external file chooser within the repository panel. See [[Repository plugins embedding external file chooser]] for details about how to do this.&lt;br /&gt;
&lt;br /&gt;
===User login (optional)===&lt;br /&gt;
If the plugin requires login from the user at the time when they use it, then these functions can be used.&lt;br /&gt;
&lt;br /&gt;
====print_login====&lt;br /&gt;
Returns an array of the elements required in the login form. If no login form is required, then the default implementation of this will redirect to the files list. If $this-&amp;gt;options[&#039;ajax&#039;] is not set, then an HTML-snippet with the login fields (but not the form tags) should be output, instead of returning the form details.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function print_login() { // From repository_alfresco&lt;br /&gt;
    if ($this-&amp;gt;options[&#039;ajax&#039;]) {&lt;br /&gt;
        $user_field = new stdClass();&lt;br /&gt;
        $user_field-&amp;gt;label = get_string(&#039;username&#039;, &#039;repository_alfresco&#039;).&#039;: &#039;;&lt;br /&gt;
        $user_field-&amp;gt;id    = &#039;alfresco_username&#039;;&lt;br /&gt;
        $user_field-&amp;gt;type  = &#039;text&#039;;&lt;br /&gt;
        $user_field-&amp;gt;name  = &#039;al_username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $passwd_field = new stdClass();&lt;br /&gt;
        $passwd_field-&amp;gt;label = get_string(&#039;password&#039;, &#039;repository_alfresco&#039;).&#039;: &#039;;&lt;br /&gt;
        $passwd_field-&amp;gt;id    = &#039;alfresco_password&#039;;&lt;br /&gt;
        $passwd_field-&amp;gt;type  = &#039;password&#039;;&lt;br /&gt;
        $passwd_field-&amp;gt;name  = &#039;al_password&#039;;&lt;br /&gt;
&lt;br /&gt;
        $ret = array();&lt;br /&gt;
        $ret[&#039;login&#039;] = array($user_field, $passwd_field);&lt;br /&gt;
        return $ret;&lt;br /&gt;
    } else { // Non-AJAX login form - directly output the form elements&lt;br /&gt;
        echo &#039;&amp;lt;table&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;label&amp;gt;&#039;.get_string(&#039;username&#039;, &#039;repository_alfresco&#039;).&#039;&amp;lt;/label&amp;gt;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;al_username&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;label&amp;gt;&#039;.get_string(&#039;password&#039;, &#039;repository_alfresco&#039;).&#039;&amp;lt;/label&amp;gt;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;password&amp;quot; name=&amp;quot;al_password&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;/table&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Enter&amp;quot; /&amp;gt;&#039;;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
This will help to generate a form by file picker which contains user name and password input elements.&lt;br /&gt;
&lt;br /&gt;
If your login form is static and never changes, you can add &#039;&#039;$ret[&#039;allowcaching&#039;] = true;&#039;&#039; and filepicker will not send the request to the server every time user opens the login/search form.&lt;br /&gt;
&lt;br /&gt;
For plugins that do not fully process the login via a popup window, the submitted details can be retrieved, from within the &#039;__construct&#039; function, via $submitted = optional_param(&#039;fieldname&#039;, [defaultvalue], PARAM_INT/PARAM_TEXT).&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array()) {&lt;br /&gt;
// Taken from repository_alfresco&lt;br /&gt;
&lt;br /&gt;
/* Skipping code that is not relevant to user login */&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;alfresco = new Alfresco_Repository($this-&amp;gt;options[&#039;alfresco_url&#039;]);        &lt;br /&gt;
        $this-&amp;gt;username = optional_param(&#039;al_username&#039;, &#039;&#039;, PARAM_RAW);&lt;br /&gt;
        $this-&amp;gt;password = optional_param(&#039;al_password&#039;, &#039;&#039;, PARAM_RAW);&lt;br /&gt;
        try{&lt;br /&gt;
            // deal with user logging in&lt;br /&gt;
            if (empty($SESSION-&amp;gt;{$this-&amp;gt;sessname}) &amp;amp;&amp;amp; !empty($this-&amp;gt;username) &amp;amp;&amp;amp; !empty($this-&amp;gt;password)) {&lt;br /&gt;
                $this-&amp;gt;ticket = $this-&amp;gt;alfresco-&amp;gt;authenticate($this-&amp;gt;username, $this-&amp;gt;password);&lt;br /&gt;
                $SESSION-&amp;gt;{$this-&amp;gt;sessname} = $this-&amp;gt;ticket;&lt;br /&gt;
            } else {&lt;br /&gt;
                if (!empty($SESSION-&amp;gt;{$this-&amp;gt;sessname})) {&lt;br /&gt;
                    $this-&amp;gt;ticket = $SESSION-&amp;gt;{$this-&amp;gt;sessname};&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            $this-&amp;gt;user_session = $this-&amp;gt;alfresco-&amp;gt;createSession($this-&amp;gt;ticket);&lt;br /&gt;
            $this-&amp;gt;store = new SpacesStore($this-&amp;gt;user_session);&lt;br /&gt;
        } catch (Exception $e) {&lt;br /&gt;
            $this-&amp;gt;logout();&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;current_node = null;&lt;br /&gt;
&lt;br /&gt;
/* Skipping code that is not relevant to user login */&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Many types include a single element of type &#039;popup&#039; with the param &#039;url&#039; pointing at the URL used to authenticate the repo instance.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function print_login(){ // Code taken from repository_boxnet&lt;br /&gt;
    $t = $this-&amp;gt;boxclient-&amp;gt;getTicket();&lt;br /&gt;
    if ($this-&amp;gt;options[&#039;ajax&#039;]) {&lt;br /&gt;
        $popup_btn = new stdClass();&lt;br /&gt;
        $popup_btn-&amp;gt;type = &#039;popup&#039;;&lt;br /&gt;
        $popup_btn-&amp;gt;url = &#039; https://www.box.com/api/1.0/auth/&#039; . $t[&#039;ticket&#039;];&lt;br /&gt;
&lt;br /&gt;
        $ret = array();&lt;br /&gt;
        $ret[&#039;login&#039;] = array($popup_btn);&lt;br /&gt;
        return $ret;&lt;br /&gt;
    } else {&lt;br /&gt;
        echo &#039;&amp;lt;table&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;label&amp;gt;&#039;.get_string(&#039;username&#039;, &#039;repository_boxnet&#039;).&#039;&amp;lt;/label&amp;gt;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;boxusername&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;label&amp;gt;&#039;.get_string(&#039;password&#039;, &#039;repository_boxnet&#039;).&#039;&amp;lt;/label&amp;gt;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;password&amp;quot; name=&amp;quot;boxpassword&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;ticket&amp;quot; value=&amp;quot;&#039;.$t[&#039;ticket&#039;].&#039;&amp;quot; /&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;/table&amp;gt;&#039;;&lt;br /&gt;
        echo &#039;&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;&#039;.get_string(&#039;enter&#039;, &#039;repository&#039;).&#039;&amp;quot; /&amp;gt;&#039;;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====check_login====&lt;br /&gt;
This function will return a boolean value to tell Moodle whether the user has logged in.&lt;br /&gt;
By default, this function will return true.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function check_login() { // Taken from repository_alfresco&lt;br /&gt;
    global $SESSION;&lt;br /&gt;
    return !empty($SESSION-&amp;gt;{$this-&amp;gt;sessname});&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
====logout====&lt;br /&gt;
When a user clicks the logout button in file picker, this function will be called. You may clean up the session or disconnect the connection with remote server here. After this the code should return something suitable to display to the user (usually the results of calling $this-&amp;gt;print_login() ):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function logout() { // Taken from repository_alfresco&lt;br /&gt;
    global $SESSION;&lt;br /&gt;
    unset($SESSION-&amp;gt;{$this-&amp;gt;sessname});&lt;br /&gt;
    return $this-&amp;gt;print_login();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Transferring files to Moodle (optional)===&lt;br /&gt;
These functions all relate to transferring the files into Moodle, once they have been chosen in the filepicker. All of them are optional and have default implementations which are often suitable to use as they are.&lt;br /&gt;
&lt;br /&gt;
====get_file_reference($source)====&lt;br /&gt;
Rarely does anything other than return the $source param (the default action if not overridden), but takes the &#039;source&#039; reference from the browser (originally specified in the list returned by &#039;get_listing&#039;) and makes sure it is ready to be passed on to the &#039;get_file&#039; or &#039;get_file_by_reference&#039; functions (below).&lt;br /&gt;
&lt;br /&gt;
====get_file($url, $filename = &amp;quot;&amp;quot;)====&lt;br /&gt;
For FILE_INTERNAL or FILE_REFERENCE this function is called at the point when the user has clicked on the file and then on &#039;select this file&#039; to add it to the filemanager / editor element. It does the actual transfer of the file from the repository and onto the Moodle server. The default implementation is to download the $url via CURL. The $url parameter is the $reference returned by get_file_reference (above, but usually the same as the &#039;source&#039; returned by &#039;get_listing&#039;). The $filename should usually be processed by $path = $this-&amp;gt;prepare_file($filename), giving the full &#039;path&#039; where the file should be saved locally. This function then returns an array, containing:&lt;br /&gt;
* path - the local path where the file was saved&lt;br /&gt;
* url - the $url param passed into the function&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function get_file($url, $filename = &#039;&#039;) {&lt;br /&gt;
// Default implementation from the base &#039;repository&#039; class&lt;br /&gt;
    $path = $this-&amp;gt;prepare_file($filename); // Generate a unique temporary filename&lt;br /&gt;
    $c = new curl;&lt;br /&gt;
    $result = $c-&amp;gt;download_one($url, null, array(&#039;filepath&#039; =&amp;gt; $path, &#039;timeout&#039; =&amp;gt; self::GETFILE_TIMEOUT));&lt;br /&gt;
    if ($result !== true) {&lt;br /&gt;
        throw new moodle_exception(&#039;errorwhiledownload&#039;, &#039;repository&#039;, &#039;&#039;, $result);&lt;br /&gt;
    }&lt;br /&gt;
    return array(&#039;path&#039;=&amp;gt;$path, &#039;url&#039;=&amp;gt;$url);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function get_file($reference, $filename = &#039;&#039;) {&lt;br /&gt;
// Slightly extended version taken from repository_equella&lt;br /&gt;
    global $USER;&lt;br /&gt;
// Extract the details saved in the &#039;source&#039; param by &lt;br /&gt;
// repository/equella/callback.php (now in the $reference paramater)&lt;br /&gt;
    $ref = @unserialize(base64_decode($reference));&lt;br /&gt;
    if (!isset($ref-&amp;gt;url) || !($url = $this-&amp;gt;appendtoken($ref-&amp;gt;url))) {&lt;br /&gt;
        // Occurs when the user isn&#039;t known..&lt;br /&gt;
        return null;&lt;br /&gt;
    }&lt;br /&gt;
    $path = $this-&amp;gt;prepare_file($filename);&lt;br /&gt;
    $cookiepathname = $this-&amp;gt;prepare_file($USER-&amp;gt;id. &#039;_&#039;. uniqid(&#039;&#039;, true). &#039;.cookie&#039;);&lt;br /&gt;
    $c = new curl(array(&#039;cookie&#039;=&amp;gt;$cookiepathname));&lt;br /&gt;
    $result = $c-&amp;gt;download_one($url, null, array(&#039;filepath&#039; =&amp;gt; $path, &#039;followlocation&#039; =&amp;gt; true, &#039;timeout&#039; =&amp;gt; self::GETFILE_TIMEOUT));&lt;br /&gt;
    // Delete cookie jar.&lt;br /&gt;
    if (file_exists($cookiepathname)) {&lt;br /&gt;
        unlink($cookiepathname);&lt;br /&gt;
    }&lt;br /&gt;
    if ($result !== true) {&lt;br /&gt;
        throw new moodle_exception(&#039;errorwhiledownload&#039;, &#039;repository&#039;, &#039;&#039;, $result);&lt;br /&gt;
    }&lt;br /&gt;
    return array(&#039;path&#039;=&amp;gt;$path, &#039;url&#039;=&amp;gt;$url);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====get_link($url)====&lt;br /&gt;
Used with FILE_EXTERNAL to convert a reference (from &#039;get_file_reference&#039;, but ultimately from the output of &#039;get_listing&#039;) into a URL that can be used directly by the end-user&#039;s browser. Usually just returns the original $url, but may need further transformation based on the internal implementation of the repository plugin.&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.3}}====get_file_source_info($source)====&lt;br /&gt;
Takes the &#039;source&#039; field from &#039;get_listing&#039; (as returned by the user&#039;s browser) and returns the value to be stored in files.source field in DB (regardless whether file is picked as a copy or by reference). It indicates where the file came from. It is advised to include either full URL here or indication of the repository.&lt;br /&gt;
Examples: &#039;Dropbox: /filename.jpg&#039;, &#039;http://fullurl.com/path/file&#039;, etc.&lt;br /&gt;
This value will be used to display warning message if reference can not be restored from backup.  Also it can (although not has to) be used in get_reference_details() to produce the human-readable reference source in the fileinfo dialogue in the file manager.&lt;br /&gt;
&lt;br /&gt;
===Search functions (optional)===&lt;br /&gt;
&lt;br /&gt;
These functions allow you to implement search functionality within your repository.&lt;br /&gt;
&lt;br /&gt;
====print_search====&lt;br /&gt;
When a user clicks the search button on file picker, this function will be called to return a search form. By default, it will create a form with single search bar - you can override it to create a advanced search form.&lt;br /&gt;
&lt;br /&gt;
A custom search form must include the following:&lt;br /&gt;
* A text field element named &#039;&#039;&#039;s&#039;&#039;&#039;, this is where users will type in their search criteria&lt;br /&gt;
&lt;br /&gt;
The following fields are automatically inserted in Moodle 2.3+ (but may need to be manually included in earlier versions):&lt;br /&gt;
* A hidden element named &#039;&#039;&#039;repo_id&#039;&#039;&#039; and the value must be the id of the repository instance&lt;br /&gt;
* A hidden element named &#039;&#039;&#039;ctx_id&#039;&#039;&#039; and the value must be the context id of the repository instance&lt;br /&gt;
* A hidden element named &#039;&#039;&#039;sesskey&#039;&#039;&#039; and the value must be the session key&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function print_search() {&lt;br /&gt;
    // The default implementation in class &#039;repository&#039;&lt;br /&gt;
    global $PAGE;&lt;br /&gt;
    $renderer = $PAGE-&amp;gt;get_renderer(&#039;core&#039;, &#039;files&#039;);&lt;br /&gt;
    return $renderer-&amp;gt;repository_default_searchform();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// From core_files_renderer (repository/renderer.php)&lt;br /&gt;
public function repository_default_searchform() {&lt;br /&gt;
    $str = &#039;&amp;lt;div class=&amp;quot;fp-def-search&amp;quot;&amp;gt;&amp;lt;input name=&amp;quot;s&amp;quot; value=&#039;.get_string(&#039;search&#039;, &#039;repository&#039;).&#039; /&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    return $str;&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;
public function print_search() {&lt;br /&gt;
&lt;br /&gt;
    // label search name&lt;br /&gt;
    $param = array(&#039;for&#039; =&amp;gt; &#039;label_search_name&#039;);&lt;br /&gt;
    $title = get_string(&#039;search_name&#039;, &#039;myrepo_search_name&#039;);&lt;br /&gt;
    $html .= html_writer::tag(&#039;label&#039;, $title, $param);&lt;br /&gt;
    $html .= html_writer::empty_tag(&#039;br&#039;);&lt;br /&gt;
&lt;br /&gt;
    // text field search name&lt;br /&gt;
    $attributes[&#039;type&#039;] = &#039;text&#039;;&lt;br /&gt;
    $attributes[&#039;name&#039;] = &#039;s&#039;;&lt;br /&gt;
    $attributes[&#039;value&#039;] = &#039;&#039;;&lt;br /&gt;
    $attributes[&#039;title&#039;] = $title;&lt;br /&gt;
    $html .= html_writer::empty_tag(&#039;input&#039;, $attributes);&lt;br /&gt;
    $html .= html_writer::empty_tag(&#039;br&#039;);&lt;br /&gt;
      &lt;br /&gt;
    return $html;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====search($search_text, $page = 0)====&lt;br /&gt;
Return the results of doing the search. Any additional parameters from the search form can be retrieved by $param = optional_param(&#039;paramname&#039;, [defaultvalue], PARAM_INT / PARAM_TEXT);. The return should return an array containing:&lt;br /&gt;
* list - with the same layout as the &#039;list&#039; element in &#039;get_listing&#039;&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function search($search_text, $page = 0) { &lt;br /&gt;
// Example from repoistory_googledocs&lt;br /&gt;
    $gdocs = new google_docs($this-&amp;gt;googleoauth);&lt;br /&gt;
&lt;br /&gt;
    $ret = array();&lt;br /&gt;
    $ret[&#039;dynload&#039;] = true;&lt;br /&gt;
    $ret[&#039;list&#039;] = $gdocs-&amp;gt;get_file_list($search_text);&lt;br /&gt;
    return $ret;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====global_search()====&lt;br /&gt;
Return true if should be included in a search throughout all repositories (currently not available via the UI)&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.3}}===Repository support for returning file as alias/shortcut=== &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
From Moodle 2.3 it became possible to link to the file from external (or internal) repository by reference. In UI it is called “create alias/shortcut”. This creates a row in {files} table but the contents of the file is not stored. Although it may be cached by repository if developer wants to.&lt;br /&gt;
&lt;br /&gt;
Make sure that function supported_returntypes() returns FILE_REFERENCE among other types.&lt;br /&gt;
&lt;br /&gt;
Note that external file is synchronised by moodle when UI wants to show the file size.&lt;br /&gt;
&lt;br /&gt;
====get_reference_file_lifetime()====&lt;br /&gt;
Return minimum number of seconds before checking for changes to the file (default implementation = 1 day)&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function get_reference_file_lifetime($ref) {&lt;br /&gt;
    return 60 * 60 * 24; // One day&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====sync_individual_file(stored_file $storedfile)====&lt;br /&gt;
Called after the file has reached the &#039;lifetime&#039; specified above to see if it should now be synchronised (default implementation is to return true)&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function sync_individual_file(stored_file $storedfile) {&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====get_reference_details($reference, $filestatus = 0)====&lt;br /&gt;
Returns human-readable information about where the original file is stored (to be displayed in the filepicker properties box). It is usually prefixed with repository name and semicolon (e.g. &#039;Myrepository: http://url.to.file&#039;). $reference is the &#039;source&#039; output by &#039;get_listing&#039;. $filestatus can be either 0 (OK - default) or 666 (source file missing).&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function get_reference_details($reference, $filestatus = 0) {&lt;br /&gt;
// Example taken from repository_equella&lt;br /&gt;
    if (!$filestatus) {&lt;br /&gt;
        $ref = unserialize(base64_decode($reference));&lt;br /&gt;
        return $this-&amp;gt;get_name(). &#039;: &#039;. $ref-&amp;gt;filename;&lt;br /&gt;
    } else {&lt;br /&gt;
        return get_string(&#039;lostsource&#039;, &#039;repository&#039;, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====get_file_by_reference($reference)====&lt;br /&gt;
Returns up-to-date information about the original file, only called when the &#039;lifetime&#039; is reached and &#039;sync_individual_file&#039; returns true.&lt;br /&gt;
* for image files - download the file and return either $ret-&amp;gt;filepath (full path on the server), $ret-&amp;gt;handle (open handle to the file) or $ret-&amp;gt;content (raw data from the file) to allow the file to be saved into the Moodle filesystem and the thumbnail to be updated&lt;br /&gt;
* for non-image files - avoid downloading the file (if possible) and just return $ret-&amp;gt;filesize to update that information&lt;br /&gt;
* for missing / inaccessible files - return null&lt;br /&gt;
Remember this function may be called quite a lot, as the filemanager often wants to know the filesize.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function get_file_by_reference($reference) {&lt;br /&gt;
// Example taken from repository_equella&lt;br /&gt;
    global $USER;&lt;br /&gt;
    // Extract the remote file identifier&lt;br /&gt;
    $ref = @unserialize(base64_decode($reference-&amp;gt;reference));&lt;br /&gt;
    if (!isset($ref-&amp;gt;url) || !($url = $this-&amp;gt;appendtoken($ref-&amp;gt;url))) {&lt;br /&gt;
        // Occurs when the user isn&#039;t known..&lt;br /&gt;
        return null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Download the file details&lt;br /&gt;
    $return = null;&lt;br /&gt;
    $cookiepathname = $this-&amp;gt;prepare_file($USER-&amp;gt;id. &#039;_&#039;. uniqid(&#039;&#039;, true). &#039;.cookie&#039;);&lt;br /&gt;
    $c = new curl(array(&#039;cookie&#039; =&amp;gt; $cookiepathname));&lt;br /&gt;
    if (file_extension_in_typegroup($ref-&amp;gt;filename, &#039;web_image&#039;)) {&lt;br /&gt;
        // The file is an image - download and return the file path&lt;br /&gt;
        $path = $this-&amp;gt;prepare_file(&#039;&#039;);&lt;br /&gt;
        $result = $c-&amp;gt;download_one($url, null, array(&#039;filepath&#039; =&amp;gt; $path, &#039;followlocation&#039; =&amp;gt; true, &#039;timeout&#039; =&amp;gt; self::SYNCIMAGE_TIMEOUT));&lt;br /&gt;
        if ($result === true) {&lt;br /&gt;
            $return = (object)array(&#039;filepath&#039; =&amp;gt; $path);&lt;br /&gt;
        }&lt;br /&gt;
    } else {&lt;br /&gt;
        // The file is not an image - just get the file details&lt;br /&gt;
        $result = $c-&amp;gt;head($url, array(&#039;followlocation&#039; =&amp;gt; true, &#039;timeout&#039; =&amp;gt; self::SYNCFILE_TIMEOUT));&lt;br /&gt;
    }&lt;br /&gt;
    // Delete cookie jar.&lt;br /&gt;
    if (file_exists($cookiepathname)) {&lt;br /&gt;
        unlink($cookiepathname);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;connection_result($c-&amp;gt;get_errno());&lt;br /&gt;
    $curlinfo = $c-&amp;gt;get_info();&lt;br /&gt;
    if ($return === null &amp;amp;&amp;amp; isset($curlinfo[&#039;http_code&#039;]) &amp;amp;&amp;amp; $curlinfo[&#039;http_code&#039;] == 200&lt;br /&gt;
            &amp;amp;&amp;amp; array_key_exists(&#039;download_content_length&#039;, $curlinfo)&lt;br /&gt;
            &amp;amp;&amp;amp; $curlinfo[&#039;download_content_length&#039;] &amp;gt;= 0) {&lt;br /&gt;
        // we received a correct header and at least can tell the file size&lt;br /&gt;
        $return = (object)array(&#039;filesize&#039; =&amp;gt; $curlinfo[&#039;download_content_length&#039;]);&lt;br /&gt;
    }&lt;br /&gt;
    return $return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====send_file($storedfile, $lifetime=86400, $filter=0, $forcedownload=false, array $options = null)====&lt;br /&gt;
Send the requested file back to the user&#039;s browser. The &#039;reference&#039; for the file can be found via $storedfile-&amp;gt;get_reference(). If the file is not found / no longer exists, the function &#039;send_file_not_found()&#039; should be used. Otherwise the file should be output directly, via the most appropriate method - e.g. use a &#039;Location: &#039; header to redirect to the external URL; or download the file and cache within the Moodle filesystem (possibly using &#039;$this-&amp;gt;import_external_file_contents()&#039;), then call &#039;send_stored_file&#039;. Note, it is up to the repository developer to decide whether to actually download the file or to return a locally cached copy instead.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function send_file($stored_file, $lifetime=86400 , $filter=0, $forcedownload=false, array $options = null) {&lt;br /&gt;
// Example taken from repository_equella&lt;br /&gt;
    $reference  = unserialize(base64_decode($stored_file-&amp;gt;get_reference()));&lt;br /&gt;
    $url = $this-&amp;gt;appendtoken($reference-&amp;gt;url);&lt;br /&gt;
    if ($url) {&lt;br /&gt;
        header(&#039;Location: &#039; . $url);&lt;br /&gt;
    } else {&lt;br /&gt;
        send_file_not_found();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
An example of caching files within the Moodle filesystem can be found in repository_dropbox.&lt;br /&gt;
&lt;br /&gt;
===Misc functions===&lt;br /&gt;
&lt;br /&gt;
A couple of other useful functions to be aware of.&lt;br /&gt;
&lt;br /&gt;
====get_name()====&lt;br /&gt;
Returns the human-readable name for this instance of the plugin (the default implementation should usually be fine and this function can be useful when doing any output to the user).&lt;br /&gt;
&lt;br /&gt;
====cron()====&lt;br /&gt;
For any background tasks that need to be scheduled (rarely needed). The minimum time between calls is specified in the version.php file (but the maximum time depends on the server settings for the Moodle install).&lt;br /&gt;
&lt;br /&gt;
== I18n - Internationalization ==&lt;br /&gt;
These following strings are required in &#039;&#039;moodle/repository/myplugin/lang/en/repository_myplugin.php&#039;&#039; or &#039;&#039;moodle/lang/en/repository_myplugin.php&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;pluginname&#039;] = &#039;Flickr Public&#039;;&lt;br /&gt;
$string[&#039;configplugin&#039;] = &#039;Flickr Public configuration&#039;;&lt;br /&gt;
$string[&#039;pluginname_help&#039;] = &#039;A Flickr public repository&#039;;&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;
*[[Repository_Interface_for_Moodle/Course/User| Repository Interface for Moodle/Course/User]]&lt;br /&gt;
*[[QA:Use Case Number Attribution| Use Case Number Attribution]]&lt;br /&gt;
* MDL-16543 - A list of officially supported repository plugins&lt;br /&gt;
* MDL-16543 - Template plugin for developers&lt;br /&gt;
&lt;br /&gt;
[[Category:Repositories]]&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42560</id>
		<title>FirePHP</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42560"/>
		<updated>2013-10-14T15:46:57Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
== What is FirePHP? ==&lt;br /&gt;
[http://www.firephp.org/ FirePHP] enables you to log to your Firebug Console using a simple PHP method call. &lt;br /&gt;
&lt;br /&gt;
== FirePHP block for Moodle ==&lt;br /&gt;
* There&#039;s a [http://moodle.org/mod/forum/discuss.php?d=119961 FirePHP plugin for Moodle] in the works. &lt;br /&gt;
* The block will soon be available in the Moodle Plugin Directory: https://moodle.org/plugins/view.php?plugin=block_firephp&lt;br /&gt;
&lt;br /&gt;
== Moodle resources == &lt;br /&gt;
* [https://moodle.org/mod/forum/discuss.php?d=119961 Moodle  General developer forum: FirePHP plugin] - where everything started.&lt;br /&gt;
* The plugin in the [https://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4793  old plugin database]&lt;br /&gt;
* Related [https://tracker.moodle.org/browse/MDL-16371 tracker issue]&lt;br /&gt;
* Current code on [https://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4793 GitHub]&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Firebug]]&lt;br /&gt;
* [[Moodle Development kit]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer tools|Firebug]]&lt;br /&gt;
[[Category:Firefox extensions|Firebug]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42559</id>
		<title>FirePHP</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42559"/>
		<updated>2013-10-14T15:46:17Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* See also */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
{{stub}}&lt;br /&gt;
&lt;br /&gt;
== What is FirePHP? ==&lt;br /&gt;
[http://www.firephp.org/ FirePHP] enables you to log to your Firebug Console using a simple PHP method call. &lt;br /&gt;
&lt;br /&gt;
== FirePHP block for Moodle ==&lt;br /&gt;
* There&#039;s a [http://moodle.org/mod/forum/discuss.php?d=119961 FirePHP plugin for Moodle] in the works. &lt;br /&gt;
* The block will soon be available in the Moodle Plugin Directory: https://moodle.org/plugins/view.php?plugin=block_firephp&lt;br /&gt;
&lt;br /&gt;
== Moodle resources == &lt;br /&gt;
* [https://moodle.org/mod/forum/discuss.php?d=119961 Moodle  General developer forum: FirePHP plugin] - where everything started.&lt;br /&gt;
* The plugin in the [https://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4793  old plugin database]&lt;br /&gt;
* Related [https://tracker.moodle.org/browse/MDL-16371 tracker issue]&lt;br /&gt;
* Current code on [https://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4793 GitHub]&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Firebug]]&lt;br /&gt;
* [[Moodle Development kit]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer tools|Firebug]]&lt;br /&gt;
[[Category:Firefox extensions|Firebug]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42558</id>
		<title>FirePHP</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42558"/>
		<updated>2013-10-14T15:41:18Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Moodle resources */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
{{stub}}&lt;br /&gt;
&lt;br /&gt;
== What is FirePHP? ==&lt;br /&gt;
[http://www.firephp.org/ FirePHP] enables you to log to your Firebug Console using a simple PHP method call. &lt;br /&gt;
&lt;br /&gt;
== FirePHP block for Moodle ==&lt;br /&gt;
* There&#039;s a [http://moodle.org/mod/forum/discuss.php?d=119961 FirePHP plugin for Moodle] in the works. &lt;br /&gt;
* The block will soon be available in the Moodle Plugin Directory: https://moodle.org/plugins/view.php?plugin=block_firephp&lt;br /&gt;
&lt;br /&gt;
== Moodle resources == &lt;br /&gt;
* [https://moodle.org/mod/forum/discuss.php?d=119961 Moodle  General developer forum: FirePHP plugin] - where everything started.&lt;br /&gt;
* The plugin in the [https://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4793  old plugin database]&lt;br /&gt;
* Related [https://tracker.moodle.org/browse/MDL-16371 tracker issue]&lt;br /&gt;
* Current code on [https://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4793 GitHub]&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Firebug]]&lt;br /&gt;
* [[Moodle Development kit]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42557</id>
		<title>FirePHP</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42557"/>
		<updated>2013-10-14T15:40:42Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
{{stub}}&lt;br /&gt;
&lt;br /&gt;
== What is FirePHP? ==&lt;br /&gt;
[http://www.firephp.org/ FirePHP] enables you to log to your Firebug Console using a simple PHP method call. &lt;br /&gt;
&lt;br /&gt;
== FirePHP block for Moodle ==&lt;br /&gt;
* There&#039;s a [http://moodle.org/mod/forum/discuss.php?d=119961 FirePHP plugin for Moodle] in the works. &lt;br /&gt;
* The block will soon be available in the Moodle Plugin Directory: https://moodle.org/plugins/view.php?plugin=block_firephp&lt;br /&gt;
&lt;br /&gt;
== Moodle resources == &lt;br /&gt;
* [https://moodle.org/mod/forum/discuss.php?d=119961 Moodle  General developer forum: FirePHP plugin] - where everything started.&lt;br /&gt;
* The plugin in the [https://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4793  old plugin database]&lt;br /&gt;
* Relatede [https://tracker.moodle.org/browse/MDL-16371 tracker issue]&lt;br /&gt;
* Current code on [https://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4793 GitHub]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Firebug]]&lt;br /&gt;
* [[Moodle Development kit]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42556</id>
		<title>FirePHP</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42556"/>
		<updated>2013-10-14T15:34:35Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
{{stub}}&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Firebug]]&lt;br /&gt;
* [[Moodle Development kit]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42555</id>
		<title>FirePHP</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42555"/>
		<updated>2013-10-14T15:33:56Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Firebug]]&lt;br /&gt;
* [[Moodle Development kit]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42554</id>
		<title>FirePHP</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=FirePHP&amp;diff=42554"/>
		<updated>2013-10-14T15:31:07Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: Created page with &amp;quot;{{Stub}}  == See also == * Firebug * Moodle Development kit&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Firebug]]&lt;br /&gt;
* [[Moodle Development kit]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=42540</id>
		<title>Acceptance testing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=42540"/>
		<updated>2013-10-13T18:17:01Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Writing features */  Changed the syntax highlighting language for scenarios to YAML.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This page describes how we describe Moodle&#039;s functionalities and how we automatically test all of them.&lt;br /&gt;
&lt;br /&gt;
Behat is a behavioural driven development (BDD) tool written in PHP, it can parse a human-readable list of sentences (called steps) and execute actions in a browser using Selenium or other tools to simulate users interactions.&lt;br /&gt;
&lt;br /&gt;
For technical info: [[Behat integration]]&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
Behat parses and executes features files which describes Moodle&#039;s features (for example &#039;&#039;Post in a forum&#039;&#039;), each feature file is composed by many scenarios (for example &#039;&#039;Add a post to a discussion&#039;&#039; or &#039;&#039;Create a new discussion&#039;&#039;), and finally each scenario is composed by steps (for example  &#039;&#039;I press &amp;quot;Post to Forum&amp;quot;&#039;&#039; or &#039;&#039;I should see &amp;quot;My post title&amp;quot;&#039;&#039;). When the feature file is executed, every step internally is translated into an PHP method and is executed.&lt;br /&gt;
&lt;br /&gt;
This features are executed nightly in the HQ servers with all the supported databases (MySQL, PostgreSQL, MSSQL and Oracle) and with different browsers (Firefox, Internet Explorer, Safari and Chrome) to avoid regressions and test new functionalities.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
* There is a closed list of steps to use in the features, a feature written with the basic (or low-level) steps looks like this:&lt;br /&gt;
  @auth&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Login&lt;br /&gt;
    In order to login&lt;br /&gt;
    As a moodle user&lt;br /&gt;
    I need to be able to validate the username and password against moodle&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an existing user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an unexisting user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that The 3 sentences below &#039;&#039;Feature: Login&#039;&#039; are only information about what we want to test.&lt;br /&gt;
&lt;br /&gt;
These are simple scenarios, but most of Moodle&#039;s functionalities would require a huge list of this steps to test a scenario, imagine a &#039;&#039;Add a post to a discussion&#039;&#039; scenario; you need to login, create a course, create a user and enrol it in the course... Most of this steps is not what we intend to test in a &#039;&#039;Post in a forum&#039;&#039; feature, Moodle provides extra steps to quickly set up the context required to test a Moodle feature, for example:&lt;br /&gt;
&lt;br /&gt;
  @mod @mod_forum&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Add forum activities and discussions&lt;br /&gt;
    In order to discuss topics with other users&lt;br /&gt;
    As a moodle teacher&lt;br /&gt;
    I need to add forum activities to moodle courses&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Add a forum and a discussion&lt;br /&gt;
      &#039;&#039;&#039;Given&#039;&#039;&#039; the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
        | username | firstname | lastname | email |&lt;br /&gt;
        | teacher1 | Teacher | 1 | teacher1@asd.com |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
        | fullname | shortname | category |&lt;br /&gt;
        | Course 1 | C1 | 0 |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
        | user | course | role |&lt;br /&gt;
        | teacher1 | C1 | editingteacher |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I log in as &amp;quot;teacher1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I follow &amp;quot;Course 1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I turn editing mode on&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I add a &amp;quot;Forum&amp;quot; to section &amp;quot;1&amp;quot; and I fill the form with:&lt;br /&gt;
        | Forum name | Test forum name |&lt;br /&gt;
        | Forum type | Standard forum for general use |&lt;br /&gt;
        | Description | Test forum description |&lt;br /&gt;
      &#039;&#039;&#039;When&#039;&#039;&#039; I add a new discussion to &amp;quot;Test forum name&amp;quot; forum with:&lt;br /&gt;
        | Subject | Forum post subject |&lt;br /&gt;
        | Message | This is the body |&lt;br /&gt;
      &#039;&#039;&#039;Then&#039;&#039;&#039; I should see &amp;quot;Test forum name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that:&lt;br /&gt;
&lt;br /&gt;
* Each scenario is executed in an isolated testing environment, so the first step begins with an empty moodle site and what you set up in an scenario (like the &#039;&#039;Test forum name&#039;&#039; forum in the example above) is cleaned up after the scenario execution&lt;br /&gt;
* The prefixes &amp;quot;Given&amp;quot;, &amp;quot;When&amp;quot; and &amp;quot;Then&amp;quot; are only informative and they are used to define the context (Given), specify the action (When) and check the results (Then), using them properly helps to understand what the scenario is testing.&lt;br /&gt;
&lt;br /&gt;
== Quick start ==&lt;br /&gt;
&lt;br /&gt;
This is a quick introduction to write a functional test (acceptance tests) using steps in a development/testing site, please DON&#039;T USE THIS IN A PRODUCTION SITE.&lt;br /&gt;
&lt;br /&gt;
To let you experience the pleasure of watching a feature file doing &amp;quot;your work&amp;quot; automatically in a real browser, this guide includes 2 optional steps to download Selenium and run it in another CLI.&lt;br /&gt;
&lt;br /&gt;
# Open a command line interface&lt;br /&gt;
# &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
# Edit config.php adding the following lines before the lib/setup.php include&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;$CFG-&amp;gt;behat_prefix = &#039;b_&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_dataroot = &#039;/path/to/your/behat/dataroot/directory&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_switchcompletely = true;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039; (In case you have problems read https://docs.moodle.org/dev/Acceptance_testing#Installation)&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
# Download selenium-server-standalone-2.NN.N.jar from http://seleniumhq.org/download/, under &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
# Open another command line interface and run &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
# You just ran the current Moodle tests, now let&#039;s add your own test, add a blog entry for example&lt;br /&gt;
# Browse to your $CFG-&amp;gt;wwwroot, this is an empty test site and it is reset before each test (called scenario)&lt;br /&gt;
# From this point follow the steps you would follow to add manually a blog entry (login credentials are admin/admin)&lt;br /&gt;
# When you are done go to &#039;Site administration&#039; -&amp;gt; &#039;Development&#039; -&amp;gt; &#039;Acceptance testing&#039;, you will find the list of &amp;quot;actions&amp;quot; that can be run automatically, you can filter them to find what do you need to do (more steps can be added if you need, more info in https://docs.moodle.org/dev/Acceptance_testing#Adding_steps_definitions)&lt;br /&gt;
# To &#039;add a blog entry&#039; we need to:&lt;br /&gt;
## Log in the system as a valid user&lt;br /&gt;
## Expand &#039;My profile&#039; node of the navigation block&lt;br /&gt;
## Expand the &#039;Blogs&#039; node of the navigation block&lt;br /&gt;
## Follow he &#039;Add a new entry&#039; link&lt;br /&gt;
## Fill the moodle form with values for &#039;Entry title&#039; and &#039;Blog entry body&#039;&lt;br /&gt;
## Press the &#039;Save changes&#039; button&lt;br /&gt;
## Verify you see the values you entered in the form and verify you are not in the form page&lt;br /&gt;
# This translated to steps is:&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
And I fill the moodle form with:&lt;br /&gt;
  | Entry title | I&#039;m the name |&lt;br /&gt;
  | Blog entry body | I&#039;m the description |&lt;br /&gt;
When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
Then I should see &amp;quot;Blog entries&amp;quot;&lt;br /&gt;
And I should see &amp;quot;I&#039;m the description&amp;quot;&lt;br /&gt;
And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# We need to wrap this steps following the behaviour driven development guidelines (more info in https://docs.moodle.org/dev/Acceptance_testing#Writing_features)&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
@core @core_blog&lt;br /&gt;
Feature: Add a blog entry&lt;br /&gt;
  In order to let the world know about me&lt;br /&gt;
  As a user&lt;br /&gt;
  I need to write blog entries&lt;br /&gt;
&lt;br /&gt;
  @javascript&lt;br /&gt;
  Scenario: Add a blog entry with valid data&lt;br /&gt;
    Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
    And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
    And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
    And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
    And I fill the moodle form with:&lt;br /&gt;
      | Entry title | I&#039;m the name |&lt;br /&gt;
      | Blog entry body | I&#039;m the description |&lt;br /&gt;
    When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
    Then I should see &amp;quot;View all of my entries&amp;quot;&lt;br /&gt;
    And I should see &amp;quot;I&#039;m a description&amp;quot;&lt;br /&gt;
    And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# And save it into a file, in this case &#039;&#039;&#039;blog/tests/behat/add_entry.feature&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;  (This will update the available tests and steps definitions)&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml --tags @core_blog&#039;&#039;&#039;&lt;br /&gt;
# Selenium will open a browser (firefox by default) and you will see how the steps you have been writting are executed&lt;br /&gt;
&lt;br /&gt;
You can also try to expand non existing nodes or change the &#039;Then&#039; assertions to get a beautiful failure.&lt;br /&gt;
&lt;br /&gt;
For detailed steps and/or troubleshooting:&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Running_tests&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* PHP 5.4 (see https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage for PHP 5.3, only for non-production sites)&lt;br /&gt;
* Other dependencies are managed by the composer installer&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Edit config.php&lt;br /&gt;
** Use $CFG-&amp;gt;behat_dataroot to set the directory where behat test environment dataroot will be stored, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_dataroot = &#039;/your/directory/path&#039;;&#039;&#039;&#039;. Ensure the directory can be created or have write permissions&lt;br /&gt;
** Use $CFG-&amp;gt;behat_prefix to set the database prefix of the behat test environment database tables, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_prefix = &#039;behat_&#039;;&#039;&#039;&#039;&lt;br /&gt;
* Download composer&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;&lt;br /&gt;
*** If you don&#039;t have curl installed or you have problems running &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;:&lt;br /&gt;
**** Download &#039;&#039;&#039;http://getcomposer.org/installer&#039;&#039;&#039;&lt;br /&gt;
**** Store it in /your/moodle/dirroot/composerinstaller.php for example&lt;br /&gt;
**** Run it from /your/moodle/dirroot with &#039;&#039;&#039;php composerinstaller.php&#039;&#039;&#039;, you can delete this file after running the next step (&#039;&#039;&#039;php composer.phar update --dev&#039;&#039;&#039;)&lt;br /&gt;
* Install behat dependencies and enable the test environment&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
* (Optional) If you want to run tests that involves Javascript (most of them) you will also need Selenium&lt;br /&gt;
** Download it from http://seleniumhq.org/download/, named &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Running tests ==&lt;br /&gt;
# Start the PHP built-in web server&lt;br /&gt;
#* Open a command line interface and &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
#* &#039;&#039;&#039;php -S localhost:8000&#039;&#039;&#039; (This is the default address, if you want to use another one you can override it in config.php with $CFG-&amp;gt;behat_wwwroot attribute; more info in https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage)&lt;br /&gt;
#** &#039;&#039;There is something crazy going on here. Thit advice seems to assume that you have $CFG-&amp;gt;wwwroot set to &#039;http://localhost:8000&#039;, but it does not acutally tell you to set that anywhere, and if you do that, it will break your normal install. Please could someone sort out these instructions.&#039;&#039;&lt;br /&gt;
# (Optional) Start the Selenium server (in case you want to run tests that involves Javascript)&lt;br /&gt;
#* Open another command line interface and &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# Run Behat&lt;br /&gt;
#* &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039; (For more options &#039;&#039;&#039;vendor/bin/behat --help&#039;&#039;&#039; or http://docs.behat.org/guides/6.cli.html)&lt;br /&gt;
#* In case you don&#039;t want to run Javascript tests use the Behat tags option to skip them, &#039;&#039;&#039;vendor/bin/behat --tags ~@javascript --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
#* If you followed all the steps and you receive an unknown weird error probably your system&#039;s Firefox version is not compatible with the Selenium version you are running, try downloading the latest Selenium release from it&#039;s website as explained above&lt;br /&gt;
# (Optional) If you are adding new tests or steps definitions update the tests list:&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;&lt;br /&gt;
# (Optional) Disable test environment (in case you want to use the PHP built-in web server for regular moodle environment)&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --disable&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note that if you have the HTTP_PROXY environemnt variable set, which you may have had to do to run composer, then you also need to set NO_PROXY=localhost.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Tests filters ===&lt;br /&gt;
With the &#039;&#039;&#039;--tags&#039;&#039;&#039; or the &#039;&#039;&#039;-name&#039;&#039;&#039; Behat options you can filter which tests are going to run or which ones are going to be skipped. There are few tags that you might be interested in:&lt;br /&gt;
* &#039;&#039;&#039;@javascript&#039;&#039;&#039;: All the tests that runs in a browser using Javascript; they require Selenium to be running, otherwise an exception will be thrown.&lt;br /&gt;
* &#039;&#039;&#039;@_only_local&#039;&#039;&#039;: All the tests that involves file uploading or any OS feature that is not 100% part of the browser. They should only be executed when Selenium is running in the same machine where the tests are running.&lt;br /&gt;
* &#039;&#039;&#039;@_cross_browser&#039;&#039;&#039;: All the tests that should run against multiple combinations of browsers + OS in a regular basis. The features that are sensitive to different combinations of OS and browsers should be tagges as @_cross_browser.&lt;br /&gt;
* &#039;&#039;&#039;@componentname&#039;&#039;&#039;: Moodle features uses the [https://docs.moodle.org/dev/Frankenstyle Frankenstyle] component name to tag the features according to the Moodle subsystem they belong to.&lt;br /&gt;
&lt;br /&gt;
=== Output formats ===&lt;br /&gt;
&lt;br /&gt;
If you want to see the failures immediately (rather than waiting ~3 hours for all the tests to finish) then either use the -v option to output a bit more information, or change the output format using --format.&lt;br /&gt;
&lt;br /&gt;
== Advanced usage ==&lt;br /&gt;
There are a few settings for advanced use of Behat and execution in continuous integration systems, by default all this options are disabled, use this settings only if you know what you are doing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Different test server URL&#039;&#039;&#039;, by default the test web server only can be accessed in localhost. If for example your are interested in allowing accesses from your local network because your Jenkins server is there you can set $CFG-&amp;gt;behat_wwwroot to &#039;&#039;&#039;http://my.computer.local.ip:8000&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Behat configuration&#039;&#039;&#039;, Moodle writes a behat.yml config file with info about the available tests and steps definitions along with other Behat parameters, you can override the Behat parameters we set and add your new parameters, your parameters will be merged with the Moodle ones giving priority to your values in case of conflict. This is useful for an advanced use of Behat, with multiple profiles, output formats, integration with continuous servers... &lt;br /&gt;
* &#039;&#039;&#039;Running with a browser other than Firefox&#039;&#039;&#039;, by adding the following code to your config.php you can change the selected browser that is run when behat is invoked. In this case Chrome is selected, but internet explorer, firefox, iphone, android, chrome, htmlunit should be valid options. You will need to run &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039; for changes to take effect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code language=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;behat_config = array(&lt;br /&gt;
    &#039;default&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;extensions&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;Behat\MinkExtension\Extension&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;selenium2&#039; =&amp;gt; array(&lt;br /&gt;
                    &#039;browser&#039; =&amp;gt; &#039;chrome&#039;&lt;br /&gt;
                )&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;
:Note that for Chrome, you will need the Selenium Chrome Driver (https://code.google.com/p/selenium/wiki/ChromeDriver), and it will need to be installed in the command search path.&lt;br /&gt;
* &#039;&#039;&#039;Switch completely to test environment&#039;&#039;&#039;, DON&#039;T USE THIS SETTING IN PRODUCTION SITES! all the site users would be using the test environment instead of the regular one with your courses and all your data and they wouldn&#039;t be able to login in your site; this setting should only be used in development/testing installations. It&#039;s purpose is to ease the integration with cloud-based continuous integration systems, another possible use is to allow acceptance testing in a development environment without PHP 5.4.&lt;br /&gt;
** Note that when using cloud-based systems that can make use of non-standard capabilities like Saucelabs, you might want to provide configuration attributes containing the &#039;&#039;&#039;&#039;-&#039;&#039;&#039;&#039; character, which is automatically converted to &#039;&#039;&#039;&#039;_&#039;&#039;&#039;&#039; by the Symfony configuration manager that Behat is making use of (@see Symfony\Component\Config\Definition\Processor::normalizeKeys()) a way to avoid this restriction is to, adding to the vars you set like &#039;&#039;&#039;&#039;max-duration&#039;&#039;&#039;&#039; add the same var replacing dashes for underscores, this way the configuration manager will maintain the attribute containing dashes.&lt;br /&gt;
You can find more info and examples of how to use this settings in the config-dist.php file included in the Moodle codebase.&lt;br /&gt;
&lt;br /&gt;
== Writing features ==&lt;br /&gt;
&lt;br /&gt;
All Moodle components and plugins (including 3rd party plugins) can specify their tests in .feature files using all the available steps.&lt;br /&gt;
&lt;br /&gt;
Once you decided which functionality you want to specify as a feature you should:&lt;br /&gt;
# Select the most appropriate Moodle component to include your test and create a COMPONENTNAME/tests/behat/FEATURENAME.feature file&lt;br /&gt;
# Add a tag with the component name in Frankenstyle format (https://docs.moodle.org/dev/Frankenstyle) on the first line along with the plugin type or @core if it&#039;s a core subsystem&lt;br /&gt;
# Begin writing the user story of the feature, including in the &#039;As a ...&#039; statement the main beneficiary of the feature:&lt;br /&gt;
#: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@plugintype @plugintype_pluginname&lt;br /&gt;
Feature: FEATURENAME&lt;br /&gt;
  In order to ...    // Why this feature is useful&lt;br /&gt;
  As ...    // It can be &#039;an admin&#039;, &#039;a teacher&#039;, &#039;a student&#039;, &#039;a guest&#039;, &#039;a user&#039;, &#039;a tests writer&#039; and &#039;a developer&#039;&lt;br /&gt;
  I need to ...      // The feature we want&amp;lt;/code&amp;gt;&lt;br /&gt;
# From the beneficiary point of view, think of different scenarios to ensure the feature works as expected&lt;br /&gt;
# For each scenario you thought:&lt;br /&gt;
## Think of the initial context you need, for example &#039;&#039;1 course with 2 students on it and an assignment&#039;&#039;, and which steps do you need to follow (interacting with the browser) to verify the scenario works as expected&lt;br /&gt;
## What you are testing requires Javascript? Think only on the feature you are testing (for example if you want to test that you can view your profile you don&#039;t need Javascript to click on a link and assert against plain HTML, but if you want to test something related with the course&#039;s gradebook you might want to test it with Javascript)&lt;br /&gt;
## Check the steps list (more info in https://docs.moodle.org/dev/Acceptance_testing#Available_steps) and set the initial context data (see https://docs.moodle.org/dev/Acceptance_testing#Fixtures for more info) and the steps to follow to verify all works as it should work. Remember to use &#039;&#039;Given&#039;&#039;, &#039;&#039;When&#039;&#039; and &#039;&#039;Then&#039;&#039; in a way that reflects what the scenario is testing&lt;br /&gt;
## Copy the list of steps to the .feature file with the Scenario header:&lt;br /&gt;
##: &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;Scenario: Short description of the scenario&lt;br /&gt;
  Given step 1&lt;br /&gt;
  And step 2&lt;br /&gt;
  And step 3&lt;br /&gt;
  When step 4&lt;br /&gt;
  And step 5&lt;br /&gt;
  Then step 6&amp;lt;/code&amp;gt;&lt;br /&gt;
## If the steps you are using requires Javascript add the @javascript tag above the &amp;quot;Scenario:&amp;quot; headline&lt;br /&gt;
##:    &amp;lt;code lang=&amp;quot;yaml&amp;quot;&amp;gt;@javascript&lt;br /&gt;
Scenario: Short description of the scenario&lt;br /&gt;
  ...&lt;br /&gt;
  ...&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run the tests, when creating your new features/scenarios you can specify a &#039;@wip&#039; (work in progress) tag in both the line above the Scenario description and the tests runner (vendor/bin/behat) to execute only the new scenario instead of running the whole set of tests.&lt;br /&gt;
# Add extra tags to the scenario or the feature if required&lt;br /&gt;
#* If there are scenarios that includes files uploads they should be tagged as @_only_local&lt;br /&gt;
#* If there are scenarios that are likely to fail in some browser-OS combinations they can be tagged as @_cross_browser, they will be tested in different OS / browser combinations by Moodle HQ continuous integration servers&lt;br /&gt;
&lt;br /&gt;
=== Available steps ===&lt;br /&gt;
&lt;br /&gt;
Moodle provides a interface to list and filter the steps you can use when writing features. You can access it through the Administration block, following &#039;&#039;&#039;Site Administration&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Development&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Acceptance testing&#039;&#039;&#039;. It allows filtering by keyword, by the Moodle component or by the type of step:&lt;br /&gt;
* Processes to set up the environment&lt;br /&gt;
* Actions that provokes an event&lt;br /&gt;
* Checkings to ensure the outcomes are the expected ones&lt;br /&gt;
&lt;br /&gt;
[[File:Acceptance_testing_UI_2.5.png]]&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
* You can use a &#039;&#039;&#039;Background&#039;&#039;&#039; section before the &#039;&#039;&#039;Scenario&#039;&#039;&#039; sections, this steps will be executed before the steps of each scenario (http://docs.behat.org/guides/1.gherkin.html#backgrounds)&lt;br /&gt;
* You can use &#039;&#039;&#039;Scenario outlines&#039;&#039;&#039; if your scenarios are nearly the same and depends on a few vars; check out the link for an explicative example (http://docs.behat.org/guides/1.gherkin.html#scenario-outlines)&lt;br /&gt;
* Is better to test the outcomes against the given data than against language strings, which are depending on the selected language.&lt;br /&gt;
* In case you need to interact with popup windows you need to switch to the window you want to interact with after opening it using the &#039;&#039;&#039;I switch to &amp;quot;popupwindowname&amp;quot; window&#039;&#039;&#039;, close it when you finish interacting with it and return to the main window using &#039;&#039;&#039;I switch to main window&#039;&#039;&#039;&lt;br /&gt;
* The format of the .feature files is YAML which finds out the data hierarchy from the indentation of it&#039;s elements, so be sure that the elements are correctly nested and the indentation is correct using spaces when necessary&lt;br /&gt;
&lt;br /&gt;
=== Providing values to steps ===&lt;br /&gt;
Most of the steps requires values, there are four methods to provide values to steps, the method depends on the step specification, you can know when a steps requires a value because you will see a drop down menu with a closed list of options that the step accepts as argument or an upper case string between double quotes, something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; or it ends with a &#039;&#039;&#039;:&#039;&#039;&#039; . The three methods are:&lt;br /&gt;
* &#039;&#039;&#039;A string/text&#039;&#039;&#039;; is the most common case, the texts are wrapped between double quotes (&amp;quot; character) you have to replace the info about the expected value for your value; for example something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; should become &#039;&#039;&#039;I press &amp;quot;Save and return to course&amp;quot;&#039;&#039;&#039;. If you want to add a string which contains a &amp;quot; character, you can escape it with \&amp;quot;, for example &#039;&#039;&#039;I fill the &amp;quot;Name&amp;quot; field with &amp;quot;Alan alias \&amp;quot;the legend\&amp;quot;&amp;quot;&#039;&#039;&#039;. You can identify this steps because they ends with &#039;&#039;&#039;_STRING&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A number&#039;&#039;&#039;; some steps requires numbers as values, to be more specific an undetermined number of digits from 0 to 9 (Natural numbers + 0) you can identify them because the expected value info string ends with &#039;&#039;&#039;_NUMBER&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A table&#039;&#039;&#039;; is a relation between values, the most common use of it is to fill forms. The steps which requires tables are easily identifiable because they finish with &#039;&#039;&#039;:&#039;&#039;&#039; The steps description gives info about what the table columns must contain, for example &#039;&#039;&#039;Fills a moodle form with field/value data&#039;&#039;&#039;. Here you don&#039;t need to escape the double quotes if you want to include them as part of the value.&lt;br /&gt;
* &#039;&#039;&#039;A selector&#039;&#039;&#039;; there are steps that can be used with different kinds of elements, for example &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;link&amp;quot;&#039;&#039;&#039; or &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;button&amp;quot;&#039;&#039;&#039; this is a closed list of elements, in the &#039;Acceptance testing&#039; interface you can see a dropdown menu to select one of these options:&lt;br /&gt;
** field - for searching a field by its id, name, value or label&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** link - for searching a link by its href, id, title, img alt or value&lt;br /&gt;
** button - for searching a button by its name, id, value, img alt or title&lt;br /&gt;
** link_or_button - for searching for both, links and buttons&lt;br /&gt;
** select - for searching a select field by its id, name or label&lt;br /&gt;
** checkbox - for searching a checkbox by its id, name, or label&lt;br /&gt;
** radio - for searching a radio button by its id, name, or label&lt;br /&gt;
** file - for searching a file input by its id, name, or label&lt;br /&gt;
** optgroup - for searching optgroup by its label&lt;br /&gt;
** option - for searching an option by its content&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
&lt;br /&gt;
==== Uploading files ====&lt;br /&gt;
Note than some tests requires files to be uploaded, in this case&lt;br /&gt;
* The &#039;&#039;&#039;I upload &amp;quot;FILEPATH_STRING&amp;quot; file to &amp;quot;FILEPICKER_FIELD_STRING&amp;quot; filepicker&#039;&#039;&#039; step can be used when located in the form page&lt;br /&gt;
* The file to upload should be included along with the Moodle codebase in COMPONENTNAME/tests/fixtures/*&lt;br /&gt;
* The file to upload is specified by it&#039;s path, which should be relative to the codebase root (&#039;&#039;&#039;lib/tests/fixtures/users.csv&#039;&#039;&#039; for example) &lt;br /&gt;
* &#039;&#039;&#039;/&#039;&#039;&#039; should be used as directory separator and the file names can not include this &#039;&#039;&#039;/&#039;&#039;&#039; character as all of them would be converted to the OS-dependant directory separator to maintain the compatibility with Windows systems.&lt;br /&gt;
* The scenarios that includes files uploading should be tagged using the &#039;&#039;&#039;@_only_local&#039;&#039;&#039; tag&lt;br /&gt;
&lt;br /&gt;
=== Fixtures ===&lt;br /&gt;
&lt;br /&gt;
As seen in [[https://docs.moodle.org/dev/Acceptance_testing#Examples examples]] Moodle provides a way to quickly set up the contextual data (courses, users, enrolments...) that you need to properly test scenarios, this can be done using one of the site templates (TODO) or creating entities in the background section (common for all the steps) or in the &amp;quot;Given&amp;quot; part of your scenario. Note that this steps can only be used to set up the contextual data required to test the feature but they don&#039;t test what they are doing; for example, the &amp;quot;Given the following &amp;quot;users&amp;quot; exists&amp;quot; is not testing that Moodle is able to create a user, but to test that a user can add a blog entry you might want to use this step. For further info, acceptance tests are supposed to be black-boxed tests (the tester don&#039;t know about the internals of the application) and this steps are using internal Moodle data generators instead of running all the steps required to create a user or to create a course, which speeds up the test execution. There are other features to test that all this elements can be properly created.&lt;br /&gt;
&lt;br /&gt;
==== Available elements ====&lt;br /&gt;
Most of the available elements can only be created in relation to other elements, to hide the complexity of the Moodle internals (references by contexts, ids...) the references can be done using more human-friendly mappings. &lt;br /&gt;
&lt;br /&gt;
The examples below shows how to add elements referencing other elements, there are required fields to reference the elements, other attributes will be filled with random data if they are not specified.&lt;br /&gt;
&lt;br /&gt;
* Course categories&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
** References between parent/children by their idnumber, using the &amp;quot;category&amp;quot; field&lt;br /&gt;
  Given the following &amp;quot;categories&amp;quot; exists:&lt;br /&gt;
    | name       | category | idnumber |&lt;br /&gt;
    | Category 1 | 0        | CAT1     |&lt;br /&gt;
    | Category 2 | CAT1     | CAT2     |&lt;br /&gt;
&lt;br /&gt;
* Courses&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses the category idnumber as category reference&lt;br /&gt;
  Given the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
    | fullname | shortname | category | format | &lt;br /&gt;
    | Course 1 | COURSE1   | CAT1     | topics |&lt;br /&gt;
    | Course 2 | COURSE2   | CAT2     |        |&lt;br /&gt;
&lt;br /&gt;
* Groups&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groups&amp;quot; exists:&lt;br /&gt;
    | name    | description | course  | idnumber |&lt;br /&gt;
    | Group 1 | Anything    | COURSE1 | GROUP1   |&lt;br /&gt;
&lt;br /&gt;
* Groupings&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groupings&amp;quot; exists:&lt;br /&gt;
    | name       | course  | idnumber  |&lt;br /&gt;
    | Grouping 1 | COURSE1 | GROUPING1 |&lt;br /&gt;
    | Grouping 2 | COURSE1 | GROUPING2 |&lt;br /&gt;
&lt;br /&gt;
* Users&lt;br /&gt;
** The required field is username (if password is not set username value will be used as password too)&lt;br /&gt;
  Given the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
    | username | email       | firstname | lastname |&lt;br /&gt;
    | testuser | asd@asd.com | Test      | User     |&lt;br /&gt;
&lt;br /&gt;
* Course enrolments&lt;br /&gt;
** The required fields are user, course and role&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses the enrolment name as enrol reference&lt;br /&gt;
  Given the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
    | user     | course  | role           | enrol  |&lt;br /&gt;
    | testuser | COURSE1 | editingteacher | manual |&lt;br /&gt;
&lt;br /&gt;
* System role assigns&lt;br /&gt;
** The required fields are user and role&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
  Given the following &amp;quot;system role assigns&amp;quot; exists:&lt;br /&gt;
    | user     | role    |&lt;br /&gt;
    | testuser | manager |&lt;br /&gt;
&lt;br /&gt;
* Group members&lt;br /&gt;
** The required fields are user and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
  Given the following &amp;quot;group members&amp;quot; exists:&lt;br /&gt;
    | user     | group  |&lt;br /&gt;
    | testuser | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Grouping groups&lt;br /&gt;
** The required fields are grouping and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the grouping idnumber as grouping reference&lt;br /&gt;
  Given the following &amp;quot;grouping groups&amp;quot; exists:&lt;br /&gt;
    | grouping  | group  |&lt;br /&gt;
    | GROUPING1 | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Cohorts&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
  Given the following &amp;quot;cohorts&amp;quot; exists:&lt;br /&gt;
    | name     | idnumber |&lt;br /&gt;
    | Cohort 1 | COHORT1  |&lt;br /&gt;
&lt;br /&gt;
=== Features check list ===&lt;br /&gt;
* It&#039;s a new feature or a new scenario of an existing feature&lt;br /&gt;
* Is using the &#039;&#039;&#039;Background&#039;&#039;&#039; section or &#039;&#039;&#039;Scenario Outlines&#039;&#039;&#039; instead of duplicating steps (only when applicable)&lt;br /&gt;
* Are using the appropriate Moodle component tag and includes @_only_local or @_cross_browser when required&lt;br /&gt;
* The user story of the feature includes a valid stakeholder and makes sense according to https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
* Covers both JS and non-JS environments&lt;br /&gt;
&lt;br /&gt;
== Adding steps definitions ==&lt;br /&gt;
&lt;br /&gt;
Each Moodle component and plugin (including 3rd party plugins) can add new steps definitions. If you are writing tests and you notice that you are repeating the same group of steps you might want to create a new step definition that allows you to substitute the group of steps for one single step, something like &#039;&#039;I add a forum post with &amp;quot;blablabla&amp;quot; as description&#039;&#039; for example; also you can create whole new steps using the APIs provided by Behat and Mink if what you need to do is not covered by any of the available steps.&lt;br /&gt;
&lt;br /&gt;
As commented in https://docs.moodle.org/dev/Acceptance_testing#Fixtures, this are black box tests, so we are not supposed to know about Moodle internals; translated to developer language it means don&#039;t use Moodle internals API calls, for example you should not try to cheat using a set_config() call, you should follow Moodle&#039;s user interface to reach the setting page and change it&#039;s value.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
You can use this example below or any of the existing steps definitions as a template.&lt;br /&gt;
&lt;br /&gt;
* auth/tests/behat/behat_auth.php&lt;br /&gt;
  class behat_auth extends behat_base {&lt;br /&gt;
      /**&lt;br /&gt;
       * Logs in the user. There should exist a user with the same value as username and password&lt;br /&gt;
       *&lt;br /&gt;
       * @Given /^I log in as &amp;quot;(?P&amp;lt;username_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/&lt;br /&gt;
       */&lt;br /&gt;
      public function i_log_in_as($username) {&lt;br /&gt;
          return array(new Given(&#039;I am on homepage&#039;),&lt;br /&gt;
              new Given(&#039;I follow &amp;quot;Login&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;Username&amp;quot; with &amp;quot;&#039;.$username.&#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;Password&amp;quot; with &amp;quot;&#039;.$username.&#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I press &amp;quot;Login&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I should see &amp;quot;You are logged in as&amp;quot;&#039;));&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
&lt;br /&gt;
If you are creating a completely new step definition there are also a few things to consider:&lt;br /&gt;
* Steps definitions should be compatible with both Javascript and non-Javascript tests, you can use $this-&amp;gt;running_javascript() to deal with both&lt;br /&gt;
* The definition code will be executed by Behat, not by Moodle, you have to keep this in mind for example when throwing exceptions, Behat exceptions will give more info to the user about where is the problem&lt;br /&gt;
** You can find these exceptions in &#039;&#039;&#039;vendor/behat/mink/src/Behat/Mink/Exception/*&#039;&#039;&#039;&lt;br /&gt;
* Selenium is fast, sometimes it tries to interact with DOM elements or tries to execute actions that requires JS that are not loaded or ready to used; this is why, sometimes and randomly, you can see an &amp;quot;element not found&amp;quot; failure&lt;br /&gt;
** The quickest way to solve this problem is using behat_base::find*() methods (where the * corresponds to &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;, &#039;&#039;&#039;_all&#039;&#039;&#039;, or to a named selector preceded by &#039;&#039;&#039;_&#039;&#039;&#039;, http://mink.behat.org/#named-selectors) which only requires the locator as argument. This methods will wait for the requested element to be ready or return an exception if the element is not found after the timeout value expires, you can also force the timeout value, which defaults to 6 seconds. An example of a named selector use is &#039;&#039;&#039;$button = $this-&amp;gt;find_button(&amp;quot;Save changes&amp;quot;);&#039;&#039;&#039; if you are not sure about the element being available you always can wrap the find*() call in a try &amp;amp; catch.&lt;br /&gt;
** For advanced usages, the spin method is defined in &#039;&#039;&#039;lib/behat/behat_base::spin&#039;&#039;&#039;, consider that all the contents of the closures passed to spin() can be executed more than once, so don&#039;t use irreversible actions that can invalidate the tests results (for example use find() methods but don&#039;t use click() methods)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you create new steps definitions or tests you must run &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039; to update the Behat config file before running &#039;&#039;&#039;vendor/bin/behat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Check list ===&lt;br /&gt;
&lt;br /&gt;
New steps should be/have:&lt;br /&gt;
* Implemented as public methods of a PHP class whose name must begin with &#039;behat_&#039; prefix and with &#039;.php extension&lt;br /&gt;
* Using the class name as filename (adding the &#039;.php&#039; extension) and extending MOODLEDIRROOT/lib/behat/behat_base.php (or MOODLEDIRROOT/lib/behat/behat_files.php if it&#039;s a repository or is files-related)&lt;br /&gt;
* With a descriptive class name, for example the component name (it will be used when filtering steps definitions)&lt;br /&gt;
* Stored in COMPONENTNAME/tests/behat/ directory or lib/tests/behat/ if is not part of any other component&lt;br /&gt;
* Describe it&#039;s purpose in a single line inside the method doc comment, the size of the comment is not a problem&lt;br /&gt;
* Describe the regular expression with the most appropriate tag inside the method doc comment:&lt;br /&gt;
** &#039;&#039;&#039;@Given&#039;&#039;&#039; - A step to set up the initial context (for example &#039;&#039;the following &amp;quot;courses&amp;quot; exists&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@When&#039;&#039;&#039; - An action that provokes an event (for example &#039;&#039;I press the button &amp;quot;buttonname&amp;quot;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@Then&#039;&#039;&#039; - Checkings to ensure the outcomes are the expected (for example &#039;&#039;I should see &amp;quot;whatever&amp;quot;&#039;&#039;)&lt;br /&gt;
* Depending on the inputs your definition expects you must use a different regular expression:&lt;br /&gt;
** &#039;&#039;&#039;If you expect a number:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_number&amp;gt;\d+)&amp;quot; (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a string or a text:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; Don&#039;t use &#039;&#039;&#039;text_selector_string&#039;&#039;&#039; and &#039;&#039;&#039;selector_string&#039;&#039;&#039; as info strings, they are reserved to selector types (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a table with key/value pairs (for example to fill a form):&#039;&#039;&#039; Finish your regular expression with &#039;&#039;&#039;:&#039;&#039;&#039; and provide info in the description about the contents of the table&lt;br /&gt;
** &#039;&#039;&#039;If you expect a selector type:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; or &amp;quot;(?P&amp;lt;text_selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; depending on whether you want to use any selector or you want a text-based selector (more info about selectors in https://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps)&lt;br /&gt;
* To make test writer&#039;s life better is good to include explicative info in the subexpressions of the regular expression about what the test writer is supposed to put in there (for example &#039;&#039;I expand &amp;quot;(?P&amp;lt;nodetext&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; node&#039;&#039;)&lt;br /&gt;
* Is recommended to use the static part of the regular expression as the name of the method, using underscores instead of spaces (see current steps definitions)&lt;br /&gt;
&lt;br /&gt;
== Links == &lt;br /&gt;
* Guidelines for contributors: [[Acceptance_testing/Contributing_automated_tests|Contributing automated tests]]&lt;br /&gt;
* Technical info: [[Behat integration]]&lt;br /&gt;
* Behat CLI command options: http://docs.behat.org/guides/6.cli.html&lt;br /&gt;
* How to use selectors to interact with the site elements: http://mink.behat.org/#traverse-the-page-selectors&lt;br /&gt;
* See Also [https://tracker.moodle.org/browse/MDL-37046 MDL37046] for clear instruction&lt;br /&gt;
[[Category:Behat]][[Category:Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[es:Prueba de aceptación]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=42539</id>
		<title>Acceptance testing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Acceptance_testing&amp;diff=42539"/>
		<updated>2013-10-13T18:14:07Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Advanced usage */  Changed syntax highlighting language to PHP.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
This page describes how we describe Moodle&#039;s functionalities and how we automatically test all of them.&lt;br /&gt;
&lt;br /&gt;
Behat is a behavioural driven development (BDD) tool written in PHP, it can parse a human-readable list of sentences (called steps) and execute actions in a browser using Selenium or other tools to simulate users interactions.&lt;br /&gt;
&lt;br /&gt;
For technical info: [[Behat integration]]&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
Behat parses and executes features files which describes Moodle&#039;s features (for example &#039;&#039;Post in a forum&#039;&#039;), each feature file is composed by many scenarios (for example &#039;&#039;Add a post to a discussion&#039;&#039; or &#039;&#039;Create a new discussion&#039;&#039;), and finally each scenario is composed by steps (for example  &#039;&#039;I press &amp;quot;Post to Forum&amp;quot;&#039;&#039; or &#039;&#039;I should see &amp;quot;My post title&amp;quot;&#039;&#039;). When the feature file is executed, every step internally is translated into an PHP method and is executed.&lt;br /&gt;
&lt;br /&gt;
This features are executed nightly in the HQ servers with all the supported databases (MySQL, PostgreSQL, MSSQL and Oracle) and with different browsers (Firefox, Internet Explorer, Safari and Chrome) to avoid regressions and test new functionalities.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
&lt;br /&gt;
* There is a closed list of steps to use in the features, a feature written with the basic (or low-level) steps looks like this:&lt;br /&gt;
  @auth&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Login&lt;br /&gt;
    In order to login&lt;br /&gt;
    As a moodle user&lt;br /&gt;
    I need to be able to validate the username and password against moodle&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an existing user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Login as an unexisting user&lt;br /&gt;
      Given I am on &amp;quot;login/index.php&amp;quot;&lt;br /&gt;
      When I fill in &amp;quot;username&amp;quot; with &amp;quot;admin&amp;quot;&lt;br /&gt;
      And I fill in &amp;quot;password&amp;quot; with &amp;quot;moodle&amp;quot;&lt;br /&gt;
      And I press &amp;quot;loginbtn&amp;quot;&lt;br /&gt;
      Then I should see &amp;quot;Moodle 101: Course Name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that The 3 sentences below &#039;&#039;Feature: Login&#039;&#039; are only information about what we want to test.&lt;br /&gt;
&lt;br /&gt;
These are simple scenarios, but most of Moodle&#039;s functionalities would require a huge list of this steps to test a scenario, imagine a &#039;&#039;Add a post to a discussion&#039;&#039; scenario; you need to login, create a course, create a user and enrol it in the course... Most of this steps is not what we intend to test in a &#039;&#039;Post in a forum&#039;&#039; feature, Moodle provides extra steps to quickly set up the context required to test a Moodle feature, for example:&lt;br /&gt;
&lt;br /&gt;
  @mod @mod_forum&lt;br /&gt;
  &#039;&#039;&#039;Feature&#039;&#039;&#039;: Add forum activities and discussions&lt;br /&gt;
    In order to discuss topics with other users&lt;br /&gt;
    As a moodle teacher&lt;br /&gt;
    I need to add forum activities to moodle courses&lt;br /&gt;
    &lt;br /&gt;
    &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Add a forum and a discussion&lt;br /&gt;
      &#039;&#039;&#039;Given&#039;&#039;&#039; the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
        | username | firstname | lastname | email |&lt;br /&gt;
        | teacher1 | Teacher | 1 | teacher1@asd.com |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
        | fullname | shortname | category |&lt;br /&gt;
        | Course 1 | C1 | 0 |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
        | user | course | role |&lt;br /&gt;
        | teacher1 | C1 | editingteacher |&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I log in as &amp;quot;teacher1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I follow &amp;quot;Course 1&amp;quot;&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I turn editing mode on&lt;br /&gt;
      &#039;&#039;&#039;And&#039;&#039;&#039; I add a &amp;quot;Forum&amp;quot; to section &amp;quot;1&amp;quot; and I fill the form with:&lt;br /&gt;
        | Forum name | Test forum name |&lt;br /&gt;
        | Forum type | Standard forum for general use |&lt;br /&gt;
        | Description | Test forum description |&lt;br /&gt;
      &#039;&#039;&#039;When&#039;&#039;&#039; I add a new discussion to &amp;quot;Test forum name&amp;quot; forum with:&lt;br /&gt;
        | Subject | Forum post subject |&lt;br /&gt;
        | Message | This is the body |&lt;br /&gt;
      &#039;&#039;&#039;Then&#039;&#039;&#039; I should see &amp;quot;Test forum name&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Note that:&lt;br /&gt;
&lt;br /&gt;
* Each scenario is executed in an isolated testing environment, so the first step begins with an empty moodle site and what you set up in an scenario (like the &#039;&#039;Test forum name&#039;&#039; forum in the example above) is cleaned up after the scenario execution&lt;br /&gt;
* The prefixes &amp;quot;Given&amp;quot;, &amp;quot;When&amp;quot; and &amp;quot;Then&amp;quot; are only informative and they are used to define the context (Given), specify the action (When) and check the results (Then), using them properly helps to understand what the scenario is testing.&lt;br /&gt;
&lt;br /&gt;
== Quick start ==&lt;br /&gt;
&lt;br /&gt;
This is a quick introduction to write a functional test (acceptance tests) using steps in a development/testing site, please DON&#039;T USE THIS IN A PRODUCTION SITE.&lt;br /&gt;
&lt;br /&gt;
To let you experience the pleasure of watching a feature file doing &amp;quot;your work&amp;quot; automatically in a real browser, this guide includes 2 optional steps to download Selenium and run it in another CLI.&lt;br /&gt;
&lt;br /&gt;
# Open a command line interface&lt;br /&gt;
# &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
# Edit config.php adding the following lines before the lib/setup.php include&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;$CFG-&amp;gt;behat_prefix = &#039;b_&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_dataroot = &#039;/path/to/your/behat/dataroot/directory&#039;;&lt;br /&gt;
$CFG-&amp;gt;behat_switchcompletely = true;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039; (In case you have problems read https://docs.moodle.org/dev/Acceptance_testing#Installation)&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
# Download selenium-server-standalone-2.NN.N.jar from http://seleniumhq.org/download/, under &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
# Open another command line interface and run &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
# You just ran the current Moodle tests, now let&#039;s add your own test, add a blog entry for example&lt;br /&gt;
# Browse to your $CFG-&amp;gt;wwwroot, this is an empty test site and it is reset before each test (called scenario)&lt;br /&gt;
# From this point follow the steps you would follow to add manually a blog entry (login credentials are admin/admin)&lt;br /&gt;
# When you are done go to &#039;Site administration&#039; -&amp;gt; &#039;Development&#039; -&amp;gt; &#039;Acceptance testing&#039;, you will find the list of &amp;quot;actions&amp;quot; that can be run automatically, you can filter them to find what do you need to do (more steps can be added if you need, more info in https://docs.moodle.org/dev/Acceptance_testing#Adding_steps_definitions)&lt;br /&gt;
# To &#039;add a blog entry&#039; we need to:&lt;br /&gt;
## Log in the system as a valid user&lt;br /&gt;
## Expand &#039;My profile&#039; node of the navigation block&lt;br /&gt;
## Expand the &#039;Blogs&#039; node of the navigation block&lt;br /&gt;
## Follow he &#039;Add a new entry&#039; link&lt;br /&gt;
## Fill the moodle form with values for &#039;Entry title&#039; and &#039;Blog entry body&#039;&lt;br /&gt;
## Press the &#039;Save changes&#039; button&lt;br /&gt;
## Verify you see the values you entered in the form and verify you are not in the form page&lt;br /&gt;
# This translated to steps is:&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
And I fill the moodle form with:&lt;br /&gt;
  | Entry title | I&#039;m the name |&lt;br /&gt;
  | Blog entry body | I&#039;m the description |&lt;br /&gt;
When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
Then I should see &amp;quot;Blog entries&amp;quot;&lt;br /&gt;
And I should see &amp;quot;I&#039;m the description&amp;quot;&lt;br /&gt;
And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# We need to wrap this steps following the behaviour driven development guidelines (more info in https://docs.moodle.org/dev/Acceptance_testing#Writing_features)&lt;br /&gt;
#: &amp;lt;code language=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
@core @core_blog&lt;br /&gt;
Feature: Add a blog entry&lt;br /&gt;
  In order to let the world know about me&lt;br /&gt;
  As a user&lt;br /&gt;
  I need to write blog entries&lt;br /&gt;
&lt;br /&gt;
  @javascript&lt;br /&gt;
  Scenario: Add a blog entry with valid data&lt;br /&gt;
    Given I log in as &amp;quot;admin&amp;quot;&lt;br /&gt;
    And I expand &amp;quot;My profile&amp;quot; node&lt;br /&gt;
    And I expand &amp;quot;Blogs&amp;quot; node&lt;br /&gt;
    And I follow &amp;quot;Add a new entry&amp;quot;&lt;br /&gt;
    And I fill the moodle form with:&lt;br /&gt;
      | Entry title | I&#039;m the name |&lt;br /&gt;
      | Blog entry body | I&#039;m the description |&lt;br /&gt;
    When I press &amp;quot;Save changes&amp;quot;&lt;br /&gt;
    Then I should see &amp;quot;View all of my entries&amp;quot;&lt;br /&gt;
    And I should see &amp;quot;I&#039;m a description&amp;quot;&lt;br /&gt;
    And I should not see &amp;quot;Required&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
# And save it into a file, in this case &#039;&#039;&#039;blog/tests/behat/add_entry.feature&#039;&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;  (This will update the available tests and steps definitions)&lt;br /&gt;
# &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/behat/dataroot/directory/behat/behat.yml --tags @core_blog&#039;&#039;&#039;&lt;br /&gt;
# Selenium will open a browser (firefox by default) and you will see how the steps you have been writting are executed&lt;br /&gt;
&lt;br /&gt;
You can also try to expand non existing nodes or change the &#039;Then&#039; assertions to get a beautiful failure.&lt;br /&gt;
&lt;br /&gt;
For detailed steps and/or troubleshooting:&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Running_tests&lt;br /&gt;
* https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* PHP 5.4 (see https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage for PHP 5.3, only for non-production sites)&lt;br /&gt;
* Other dependencies are managed by the composer installer&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
* Edit config.php&lt;br /&gt;
** Use $CFG-&amp;gt;behat_dataroot to set the directory where behat test environment dataroot will be stored, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_dataroot = &#039;/your/directory/path&#039;;&#039;&#039;&#039;. Ensure the directory can be created or have write permissions&lt;br /&gt;
** Use $CFG-&amp;gt;behat_prefix to set the database prefix of the behat test environment database tables, something like &#039;&#039;&#039;$CFG-&amp;gt;behat_prefix = &#039;behat_&#039;;&#039;&#039;&#039;&lt;br /&gt;
* Download composer&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;&lt;br /&gt;
*** If you don&#039;t have curl installed or you have problems running &#039;&#039;&#039;curl http://getcomposer.org/installer | php&#039;&#039;&#039;:&lt;br /&gt;
**** Download &#039;&#039;&#039;http://getcomposer.org/installer&#039;&#039;&#039;&lt;br /&gt;
**** Store it in /your/moodle/dirroot/composerinstaller.php for example&lt;br /&gt;
**** Run it from /your/moodle/dirroot with &#039;&#039;&#039;php composerinstaller.php&#039;&#039;&#039;, you can delete this file after running the next step (&#039;&#039;&#039;php composer.phar update --dev&#039;&#039;&#039;)&lt;br /&gt;
* Install behat dependencies and enable the test environment&lt;br /&gt;
** &#039;&#039;&#039;cd /your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039;&lt;br /&gt;
* (Optional) If you want to run tests that involves Javascript (most of them) you will also need Selenium&lt;br /&gt;
** Download it from http://seleniumhq.org/download/, named &amp;quot;Selenium server (formerly the Selenium RC Server)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Running tests ==&lt;br /&gt;
# Start the PHP built-in web server&lt;br /&gt;
#* Open a command line interface and &#039;&#039;&#039;cd /to/your/moodle/dirroot&#039;&#039;&#039;&lt;br /&gt;
#* &#039;&#039;&#039;php -S localhost:8000&#039;&#039;&#039; (This is the default address, if you want to use another one you can override it in config.php with $CFG-&amp;gt;behat_wwwroot attribute; more info in https://docs.moodle.org/dev/Acceptance_testing#Advanced_usage)&lt;br /&gt;
#** &#039;&#039;There is something crazy going on here. Thit advice seems to assume that you have $CFG-&amp;gt;wwwroot set to &#039;http://localhost:8000&#039;, but it does not acutally tell you to set that anywhere, and if you do that, it will break your normal install. Please could someone sort out these instructions.&#039;&#039;&lt;br /&gt;
# (Optional) Start the Selenium server (in case you want to run tests that involves Javascript)&lt;br /&gt;
#* Open another command line interface and &#039;&#039;&#039;java -jar /path/to/your/selenium/server/selenium-server-standalone-2.NN.N.jar&#039;&#039;&#039;&lt;br /&gt;
# Run Behat&lt;br /&gt;
#* &#039;&#039;&#039;vendor/bin/behat --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039; (For more options &#039;&#039;&#039;vendor/bin/behat --help&#039;&#039;&#039; or http://docs.behat.org/guides/6.cli.html)&lt;br /&gt;
#* In case you don&#039;t want to run Javascript tests use the Behat tags option to skip them, &#039;&#039;&#039;vendor/bin/behat --tags ~@javascript --config /path/to/your/CFG_behat_dataroot/behat/behat.yml&#039;&#039;&#039;&lt;br /&gt;
#* If you followed all the steps and you receive an unknown weird error probably your system&#039;s Firefox version is not compatible with the Selenium version you are running, try downloading the latest Selenium release from it&#039;s website as explained above&lt;br /&gt;
# (Optional) If you are adding new tests or steps definitions update the tests list:&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039;&lt;br /&gt;
# (Optional) Disable test environment (in case you want to use the PHP built-in web server for regular moodle environment)&lt;br /&gt;
#* &#039;&#039;&#039;php admin/tool/behat/cli/util.php --disable&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note that if you have the HTTP_PROXY environemnt variable set, which you may have had to do to run composer, then you also need to set NO_PROXY=localhost.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Tests filters ===&lt;br /&gt;
With the &#039;&#039;&#039;--tags&#039;&#039;&#039; or the &#039;&#039;&#039;-name&#039;&#039;&#039; Behat options you can filter which tests are going to run or which ones are going to be skipped. There are few tags that you might be interested in:&lt;br /&gt;
* &#039;&#039;&#039;@javascript&#039;&#039;&#039;: All the tests that runs in a browser using Javascript; they require Selenium to be running, otherwise an exception will be thrown.&lt;br /&gt;
* &#039;&#039;&#039;@_only_local&#039;&#039;&#039;: All the tests that involves file uploading or any OS feature that is not 100% part of the browser. They should only be executed when Selenium is running in the same machine where the tests are running.&lt;br /&gt;
* &#039;&#039;&#039;@_cross_browser&#039;&#039;&#039;: All the tests that should run against multiple combinations of browsers + OS in a regular basis. The features that are sensitive to different combinations of OS and browsers should be tagges as @_cross_browser.&lt;br /&gt;
* &#039;&#039;&#039;@componentname&#039;&#039;&#039;: Moodle features uses the [https://docs.moodle.org/dev/Frankenstyle Frankenstyle] component name to tag the features according to the Moodle subsystem they belong to.&lt;br /&gt;
&lt;br /&gt;
=== Output formats ===&lt;br /&gt;
&lt;br /&gt;
If you want to see the failures immediately (rather than waiting ~3 hours for all the tests to finish) then either use the -v option to output a bit more information, or change the output format using --format.&lt;br /&gt;
&lt;br /&gt;
== Advanced usage ==&lt;br /&gt;
There are a few settings for advanced use of Behat and execution in continuous integration systems, by default all this options are disabled, use this settings only if you know what you are doing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Different test server URL&#039;&#039;&#039;, by default the test web server only can be accessed in localhost. If for example your are interested in allowing accesses from your local network because your Jenkins server is there you can set $CFG-&amp;gt;behat_wwwroot to &#039;&#039;&#039;http://my.computer.local.ip:8000&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Behat configuration&#039;&#039;&#039;, Moodle writes a behat.yml config file with info about the available tests and steps definitions along with other Behat parameters, you can override the Behat parameters we set and add your new parameters, your parameters will be merged with the Moodle ones giving priority to your values in case of conflict. This is useful for an advanced use of Behat, with multiple profiles, output formats, integration with continuous servers... &lt;br /&gt;
* &#039;&#039;&#039;Running with a browser other than Firefox&#039;&#039;&#039;, by adding the following code to your config.php you can change the selected browser that is run when behat is invoked. In this case Chrome is selected, but internet explorer, firefox, iphone, android, chrome, htmlunit should be valid options. You will need to run &#039;&#039;&#039;php admin/tool/behat/cli/init.php&#039;&#039;&#039; for changes to take effect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code language=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;behat_config = array(&lt;br /&gt;
    &#039;default&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;extensions&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;Behat\MinkExtension\Extension&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;selenium2&#039; =&amp;gt; array(&lt;br /&gt;
                    &#039;browser&#039; =&amp;gt; &#039;chrome&#039;&lt;br /&gt;
                )&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;
:Note that for Chrome, you will need the Selenium Chrome Driver (https://code.google.com/p/selenium/wiki/ChromeDriver), and it will need to be installed in the command search path.&lt;br /&gt;
* &#039;&#039;&#039;Switch completely to test environment&#039;&#039;&#039;, DON&#039;T USE THIS SETTING IN PRODUCTION SITES! all the site users would be using the test environment instead of the regular one with your courses and all your data and they wouldn&#039;t be able to login in your site; this setting should only be used in development/testing installations. It&#039;s purpose is to ease the integration with cloud-based continuous integration systems, another possible use is to allow acceptance testing in a development environment without PHP 5.4.&lt;br /&gt;
** Note that when using cloud-based systems that can make use of non-standard capabilities like Saucelabs, you might want to provide configuration attributes containing the &#039;&#039;&#039;&#039;-&#039;&#039;&#039;&#039; character, which is automatically converted to &#039;&#039;&#039;&#039;_&#039;&#039;&#039;&#039; by the Symfony configuration manager that Behat is making use of (@see Symfony\Component\Config\Definition\Processor::normalizeKeys()) a way to avoid this restriction is to, adding to the vars you set like &#039;&#039;&#039;&#039;max-duration&#039;&#039;&#039;&#039; add the same var replacing dashes for underscores, this way the configuration manager will maintain the attribute containing dashes.&lt;br /&gt;
You can find more info and examples of how to use this settings in the config-dist.php file included in the Moodle codebase.&lt;br /&gt;
&lt;br /&gt;
== Writing features ==&lt;br /&gt;
&lt;br /&gt;
All Moodle components and plugins (including 3rd party plugins) can specify their tests in .feature files using all the available steps.&lt;br /&gt;
&lt;br /&gt;
Once you decided which functionality you want to specify as a feature you should:&lt;br /&gt;
# Select the most appropriate Moodle component to include your test and create a COMPONENTNAME/tests/behat/FEATURENAME.feature file&lt;br /&gt;
# Add a tag with the component name in Frankenstyle format (https://docs.moodle.org/dev/Frankenstyle) on the first line along with the plugin type or @core if it&#039;s a core subsystem&lt;br /&gt;
# Begin writing the user story of the feature, including in the &#039;As a ...&#039; statement the main beneficiary of the feature:&lt;br /&gt;
#: &amp;lt;code lang=&amp;quot;text&amp;quot;&amp;gt;@plugintype @plugintype_pluginname&lt;br /&gt;
Feature: FEATURENAME&lt;br /&gt;
  In order to ...    // Why this feature is useful&lt;br /&gt;
  As ...    // It can be &#039;an admin&#039;, &#039;a teacher&#039;, &#039;a student&#039;, &#039;a guest&#039;, &#039;a user&#039;, &#039;a tests writer&#039; and &#039;a developer&#039;&lt;br /&gt;
  I need to ...      // The feature we want&amp;lt;/code&amp;gt;&lt;br /&gt;
# From the beneficiary point of view, think of different scenarios to ensure the feature works as expected&lt;br /&gt;
# For each scenario you thought:&lt;br /&gt;
## Think of the initial context you need, for example &#039;&#039;1 course with 2 students on it and an assignment&#039;&#039;, and which steps do you need to follow (interacting with the browser) to verify the scenario works as expected&lt;br /&gt;
## What you are testing requires Javascript? Think only on the feature you are testing (for example if you want to test that you can view your profile you don&#039;t need Javascript to click on a link and assert against plain HTML, but if you want to test something related with the course&#039;s gradebook you might want to test it with Javascript)&lt;br /&gt;
## Check the steps list (more info in https://docs.moodle.org/dev/Acceptance_testing#Available_steps) and set the initial context data (see https://docs.moodle.org/dev/Acceptance_testing#Fixtures for more info) and the steps to follow to verify all works as it should work. Remember to use &#039;&#039;Given&#039;&#039;, &#039;&#039;When&#039;&#039; and &#039;&#039;Then&#039;&#039; in a way that reflects what the scenario is testing&lt;br /&gt;
## Copy the list of steps to the .feature file with the Scenario header:&lt;br /&gt;
##: &amp;lt;code lang=&amp;quot;text&amp;quot;&amp;gt;Scenario: Short description of the scenario&lt;br /&gt;
  Given step 1&lt;br /&gt;
  And step 2&lt;br /&gt;
  And step 3&lt;br /&gt;
  When step 4&lt;br /&gt;
  And step 5&lt;br /&gt;
  Then step 6&amp;lt;/code&amp;gt;&lt;br /&gt;
## If the steps you are using requires Javascript add the @javascript tag above the &amp;quot;Scenario:&amp;quot; headline&lt;br /&gt;
##:    &amp;lt;code lang=&amp;quot;text&amp;quot;&amp;gt;@javascript&lt;br /&gt;
Scenario: Short description of the scenario&lt;br /&gt;
  ...&lt;br /&gt;
  ...&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run the tests, when creating your new features/scenarios you can specify a &#039;@wip&#039; (work in progress) tag in both the line above the Scenario description and the tests runner (vendor/bin/behat) to execute only the new scenario instead of running the whole set of tests.&lt;br /&gt;
# Add extra tags to the scenario or the feature if required&lt;br /&gt;
#* If there are scenarios that includes files uploads they should be tagged as @_only_local&lt;br /&gt;
#* If there are scenarios that are likely to fail in some browser-OS combinations they can be tagged as @_cross_browser, they will be tested in different OS / browser combinations by Moodle HQ continuous integration servers&lt;br /&gt;
&lt;br /&gt;
=== Available steps ===&lt;br /&gt;
&lt;br /&gt;
Moodle provides a interface to list and filter the steps you can use when writing features. You can access it through the Administration block, following &#039;&#039;&#039;Site Administration&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Development&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;Acceptance testing&#039;&#039;&#039;. It allows filtering by keyword, by the Moodle component or by the type of step:&lt;br /&gt;
* Processes to set up the environment&lt;br /&gt;
* Actions that provokes an event&lt;br /&gt;
* Checkings to ensure the outcomes are the expected ones&lt;br /&gt;
&lt;br /&gt;
[[File:Acceptance_testing_UI_2.5.png]]&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
* You can use a &#039;&#039;&#039;Background&#039;&#039;&#039; section before the &#039;&#039;&#039;Scenario&#039;&#039;&#039; sections, this steps will be executed before the steps of each scenario (http://docs.behat.org/guides/1.gherkin.html#backgrounds)&lt;br /&gt;
* You can use &#039;&#039;&#039;Scenario outlines&#039;&#039;&#039; if your scenarios are nearly the same and depends on a few vars; check out the link for an explicative example (http://docs.behat.org/guides/1.gherkin.html#scenario-outlines)&lt;br /&gt;
* Is better to test the outcomes against the given data than against language strings, which are depending on the selected language.&lt;br /&gt;
* In case you need to interact with popup windows you need to switch to the window you want to interact with after opening it using the &#039;&#039;&#039;I switch to &amp;quot;popupwindowname&amp;quot; window&#039;&#039;&#039;, close it when you finish interacting with it and return to the main window using &#039;&#039;&#039;I switch to main window&#039;&#039;&#039;&lt;br /&gt;
* The format of the .feature files is YAML which finds out the data hierarchy from the indentation of it&#039;s elements, so be sure that the elements are correctly nested and the indentation is correct using spaces when necessary&lt;br /&gt;
&lt;br /&gt;
=== Providing values to steps ===&lt;br /&gt;
Most of the steps requires values, there are four methods to provide values to steps, the method depends on the step specification, you can know when a steps requires a value because you will see a drop down menu with a closed list of options that the step accepts as argument or an upper case string between double quotes, something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; or it ends with a &#039;&#039;&#039;:&#039;&#039;&#039; . The three methods are:&lt;br /&gt;
* &#039;&#039;&#039;A string/text&#039;&#039;&#039;; is the most common case, the texts are wrapped between double quotes (&amp;quot; character) you have to replace the info about the expected value for your value; for example something like &#039;&#039;&#039;I press &amp;quot;BUTTON_STRING&amp;quot;&#039;&#039;&#039; should become &#039;&#039;&#039;I press &amp;quot;Save and return to course&amp;quot;&#039;&#039;&#039;. If you want to add a string which contains a &amp;quot; character, you can escape it with \&amp;quot;, for example &#039;&#039;&#039;I fill the &amp;quot;Name&amp;quot; field with &amp;quot;Alan alias \&amp;quot;the legend\&amp;quot;&amp;quot;&#039;&#039;&#039;. You can identify this steps because they ends with &#039;&#039;&#039;_STRING&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A number&#039;&#039;&#039;; some steps requires numbers as values, to be more specific an undetermined number of digits from 0 to 9 (Natural numbers + 0) you can identify them because the expected value info string ends with &#039;&#039;&#039;_NUMBER&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;A table&#039;&#039;&#039;; is a relation between values, the most common use of it is to fill forms. The steps which requires tables are easily identifiable because they finish with &#039;&#039;&#039;:&#039;&#039;&#039; The steps description gives info about what the table columns must contain, for example &#039;&#039;&#039;Fills a moodle form with field/value data&#039;&#039;&#039;. Here you don&#039;t need to escape the double quotes if you want to include them as part of the value.&lt;br /&gt;
* &#039;&#039;&#039;A selector&#039;&#039;&#039;; there are steps that can be used with different kinds of elements, for example &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;link&amp;quot;&#039;&#039;&#039; or &#039;&#039;&#039;I click on &amp;quot;User Name&amp;quot; &amp;quot;button&amp;quot;&#039;&#039;&#039; this is a closed list of elements, in the &#039;Acceptance testing&#039; interface you can see a dropdown menu to select one of these options:&lt;br /&gt;
** field - for searching a field by its id, name, value or label&lt;br /&gt;
** fieldset - for searching a fieldset by it&#039;s id or legend&lt;br /&gt;
** link - for searching a link by its href, id, title, img alt or value&lt;br /&gt;
** button - for searching a button by its name, id, value, img alt or title&lt;br /&gt;
** link_or_button - for searching for both, links and buttons&lt;br /&gt;
** select - for searching a select field by its id, name or label&lt;br /&gt;
** checkbox - for searching a checkbox by its id, name, or label&lt;br /&gt;
** radio - for searching a radio button by its id, name, or label&lt;br /&gt;
** file - for searching a file input by its id, name, or label&lt;br /&gt;
** optgroup - for searching optgroup by its label&lt;br /&gt;
** option - for searching an option by its content&lt;br /&gt;
** table - for searching a table by its id or caption&lt;br /&gt;
** css_element - for searching an element by its CSS selector&lt;br /&gt;
** xpath_element - for searching an element by its XPath&lt;br /&gt;
&lt;br /&gt;
==== Uploading files ====&lt;br /&gt;
Note than some tests requires files to be uploaded, in this case&lt;br /&gt;
* The &#039;&#039;&#039;I upload &amp;quot;FILEPATH_STRING&amp;quot; file to &amp;quot;FILEPICKER_FIELD_STRING&amp;quot; filepicker&#039;&#039;&#039; step can be used when located in the form page&lt;br /&gt;
* The file to upload should be included along with the Moodle codebase in COMPONENTNAME/tests/fixtures/*&lt;br /&gt;
* The file to upload is specified by it&#039;s path, which should be relative to the codebase root (&#039;&#039;&#039;lib/tests/fixtures/users.csv&#039;&#039;&#039; for example) &lt;br /&gt;
* &#039;&#039;&#039;/&#039;&#039;&#039; should be used as directory separator and the file names can not include this &#039;&#039;&#039;/&#039;&#039;&#039; character as all of them would be converted to the OS-dependant directory separator to maintain the compatibility with Windows systems.&lt;br /&gt;
* The scenarios that includes files uploading should be tagged using the &#039;&#039;&#039;@_only_local&#039;&#039;&#039; tag&lt;br /&gt;
&lt;br /&gt;
=== Fixtures ===&lt;br /&gt;
&lt;br /&gt;
As seen in [[https://docs.moodle.org/dev/Acceptance_testing#Examples examples]] Moodle provides a way to quickly set up the contextual data (courses, users, enrolments...) that you need to properly test scenarios, this can be done using one of the site templates (TODO) or creating entities in the background section (common for all the steps) or in the &amp;quot;Given&amp;quot; part of your scenario. Note that this steps can only be used to set up the contextual data required to test the feature but they don&#039;t test what they are doing; for example, the &amp;quot;Given the following &amp;quot;users&amp;quot; exists&amp;quot; is not testing that Moodle is able to create a user, but to test that a user can add a blog entry you might want to use this step. For further info, acceptance tests are supposed to be black-boxed tests (the tester don&#039;t know about the internals of the application) and this steps are using internal Moodle data generators instead of running all the steps required to create a user or to create a course, which speeds up the test execution. There are other features to test that all this elements can be properly created.&lt;br /&gt;
&lt;br /&gt;
==== Available elements ====&lt;br /&gt;
Most of the available elements can only be created in relation to other elements, to hide the complexity of the Moodle internals (references by contexts, ids...) the references can be done using more human-friendly mappings. &lt;br /&gt;
&lt;br /&gt;
The examples below shows how to add elements referencing other elements, there are required fields to reference the elements, other attributes will be filled with random data if they are not specified.&lt;br /&gt;
&lt;br /&gt;
* Course categories&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
** References between parent/children by their idnumber, using the &amp;quot;category&amp;quot; field&lt;br /&gt;
  Given the following &amp;quot;categories&amp;quot; exists:&lt;br /&gt;
    | name       | category | idnumber |&lt;br /&gt;
    | Category 1 | 0        | CAT1     |&lt;br /&gt;
    | Category 2 | CAT1     | CAT2     |&lt;br /&gt;
&lt;br /&gt;
* Courses&lt;br /&gt;
** The required field is shortname&lt;br /&gt;
** Uses the category idnumber as category reference&lt;br /&gt;
  Given the following &amp;quot;courses&amp;quot; exists:&lt;br /&gt;
    | fullname | shortname | category | format | &lt;br /&gt;
    | Course 1 | COURSE1   | CAT1     | topics |&lt;br /&gt;
    | Course 2 | COURSE2   | CAT2     |        |&lt;br /&gt;
&lt;br /&gt;
* Groups&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groups&amp;quot; exists:&lt;br /&gt;
    | name    | description | course  | idnumber |&lt;br /&gt;
    | Group 1 | Anything    | COURSE1 | GROUP1   |&lt;br /&gt;
&lt;br /&gt;
* Groupings&lt;br /&gt;
** The required fields are course and idnumber&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
  Given the following &amp;quot;groupings&amp;quot; exists:&lt;br /&gt;
    | name       | course  | idnumber  |&lt;br /&gt;
    | Grouping 1 | COURSE1 | GROUPING1 |&lt;br /&gt;
    | Grouping 2 | COURSE1 | GROUPING2 |&lt;br /&gt;
&lt;br /&gt;
* Users&lt;br /&gt;
** The required field is username (if password is not set username value will be used as password too)&lt;br /&gt;
  Given the following &amp;quot;users&amp;quot; exists:&lt;br /&gt;
    | username | email       | firstname | lastname |&lt;br /&gt;
    | testuser | asd@asd.com | Test      | User     |&lt;br /&gt;
&lt;br /&gt;
* Course enrolments&lt;br /&gt;
** The required fields are user, course and role&lt;br /&gt;
** Uses the course shortname as course reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
** Uses the enrolment name as enrol reference&lt;br /&gt;
  Given the following &amp;quot;course enrolments&amp;quot; exists:&lt;br /&gt;
    | user     | course  | role           | enrol  |&lt;br /&gt;
    | testuser | COURSE1 | editingteacher | manual |&lt;br /&gt;
&lt;br /&gt;
* System role assigns&lt;br /&gt;
** The required fields are user and role&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
** Uses the role shortname as role reference&lt;br /&gt;
  Given the following &amp;quot;system role assigns&amp;quot; exists:&lt;br /&gt;
    | user     | role    |&lt;br /&gt;
    | testuser | manager |&lt;br /&gt;
&lt;br /&gt;
* Group members&lt;br /&gt;
** The required fields are user and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the user username as user reference&lt;br /&gt;
  Given the following &amp;quot;group members&amp;quot; exists:&lt;br /&gt;
    | user     | group  |&lt;br /&gt;
    | testuser | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Grouping groups&lt;br /&gt;
** The required fields are grouping and group&lt;br /&gt;
** Uses the group idnumber as group reference&lt;br /&gt;
** Uses the grouping idnumber as grouping reference&lt;br /&gt;
  Given the following &amp;quot;grouping groups&amp;quot; exists:&lt;br /&gt;
    | grouping  | group  |&lt;br /&gt;
    | GROUPING1 | GROUP1 |&lt;br /&gt;
&lt;br /&gt;
* Cohorts&lt;br /&gt;
** The required field is idnumber&lt;br /&gt;
  Given the following &amp;quot;cohorts&amp;quot; exists:&lt;br /&gt;
    | name     | idnumber |&lt;br /&gt;
    | Cohort 1 | COHORT1  |&lt;br /&gt;
&lt;br /&gt;
=== Features check list ===&lt;br /&gt;
* It&#039;s a new feature or a new scenario of an existing feature&lt;br /&gt;
* Is using the &#039;&#039;&#039;Background&#039;&#039;&#039; section or &#039;&#039;&#039;Scenario Outlines&#039;&#039;&#039; instead of duplicating steps (only when applicable)&lt;br /&gt;
* Are using the appropriate Moodle component tag and includes @_only_local or @_cross_browser when required&lt;br /&gt;
* The user story of the feature includes a valid stakeholder and makes sense according to https://docs.moodle.org/dev/Acceptance_testing#Writing_features&lt;br /&gt;
* Covers both JS and non-JS environments&lt;br /&gt;
&lt;br /&gt;
== Adding steps definitions ==&lt;br /&gt;
&lt;br /&gt;
Each Moodle component and plugin (including 3rd party plugins) can add new steps definitions. If you are writing tests and you notice that you are repeating the same group of steps you might want to create a new step definition that allows you to substitute the group of steps for one single step, something like &#039;&#039;I add a forum post with &amp;quot;blablabla&amp;quot; as description&#039;&#039; for example; also you can create whole new steps using the APIs provided by Behat and Mink if what you need to do is not covered by any of the available steps.&lt;br /&gt;
&lt;br /&gt;
As commented in https://docs.moodle.org/dev/Acceptance_testing#Fixtures, this are black box tests, so we are not supposed to know about Moodle internals; translated to developer language it means don&#039;t use Moodle internals API calls, for example you should not try to cheat using a set_config() call, you should follow Moodle&#039;s user interface to reach the setting page and change it&#039;s value.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
You can use this example below or any of the existing steps definitions as a template.&lt;br /&gt;
&lt;br /&gt;
* auth/tests/behat/behat_auth.php&lt;br /&gt;
  class behat_auth extends behat_base {&lt;br /&gt;
      /**&lt;br /&gt;
       * Logs in the user. There should exist a user with the same value as username and password&lt;br /&gt;
       *&lt;br /&gt;
       * @Given /^I log in as &amp;quot;(?P&amp;lt;username_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/&lt;br /&gt;
       */&lt;br /&gt;
      public function i_log_in_as($username) {&lt;br /&gt;
          return array(new Given(&#039;I am on homepage&#039;),&lt;br /&gt;
              new Given(&#039;I follow &amp;quot;Login&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;Username&amp;quot; with &amp;quot;&#039;.$username.&#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I fill in &amp;quot;Password&amp;quot; with &amp;quot;&#039;.$username.&#039;&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I press &amp;quot;Login&amp;quot;&#039;),&lt;br /&gt;
              new Given(&#039;I should see &amp;quot;You are logged in as&amp;quot;&#039;));&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Tips ===&lt;br /&gt;
&lt;br /&gt;
If you are creating a completely new step definition there are also a few things to consider:&lt;br /&gt;
* Steps definitions should be compatible with both Javascript and non-Javascript tests, you can use $this-&amp;gt;running_javascript() to deal with both&lt;br /&gt;
* The definition code will be executed by Behat, not by Moodle, you have to keep this in mind for example when throwing exceptions, Behat exceptions will give more info to the user about where is the problem&lt;br /&gt;
** You can find these exceptions in &#039;&#039;&#039;vendor/behat/mink/src/Behat/Mink/Exception/*&#039;&#039;&#039;&lt;br /&gt;
* Selenium is fast, sometimes it tries to interact with DOM elements or tries to execute actions that requires JS that are not loaded or ready to used; this is why, sometimes and randomly, you can see an &amp;quot;element not found&amp;quot; failure&lt;br /&gt;
** The quickest way to solve this problem is using behat_base::find*() methods (where the * corresponds to &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;, &#039;&#039;&#039;_all&#039;&#039;&#039;, or to a named selector preceded by &#039;&#039;&#039;_&#039;&#039;&#039;, http://mink.behat.org/#named-selectors) which only requires the locator as argument. This methods will wait for the requested element to be ready or return an exception if the element is not found after the timeout value expires, you can also force the timeout value, which defaults to 6 seconds. An example of a named selector use is &#039;&#039;&#039;$button = $this-&amp;gt;find_button(&amp;quot;Save changes&amp;quot;);&#039;&#039;&#039; if you are not sure about the element being available you always can wrap the find*() call in a try &amp;amp; catch.&lt;br /&gt;
** For advanced usages, the spin method is defined in &#039;&#039;&#039;lib/behat/behat_base::spin&#039;&#039;&#039;, consider that all the contents of the closures passed to spin() can be executed more than once, so don&#039;t use irreversible actions that can invalidate the tests results (for example use find() methods but don&#039;t use click() methods)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you create new steps definitions or tests you must run &#039;&#039;&#039;php admin/tool/behat/cli/util.php --enable&#039;&#039;&#039; to update the Behat config file before running &#039;&#039;&#039;vendor/bin/behat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Check list ===&lt;br /&gt;
&lt;br /&gt;
New steps should be/have:&lt;br /&gt;
* Implemented as public methods of a PHP class whose name must begin with &#039;behat_&#039; prefix and with &#039;.php extension&lt;br /&gt;
* Using the class name as filename (adding the &#039;.php&#039; extension) and extending MOODLEDIRROOT/lib/behat/behat_base.php (or MOODLEDIRROOT/lib/behat/behat_files.php if it&#039;s a repository or is files-related)&lt;br /&gt;
* With a descriptive class name, for example the component name (it will be used when filtering steps definitions)&lt;br /&gt;
* Stored in COMPONENTNAME/tests/behat/ directory or lib/tests/behat/ if is not part of any other component&lt;br /&gt;
* Describe it&#039;s purpose in a single line inside the method doc comment, the size of the comment is not a problem&lt;br /&gt;
* Describe the regular expression with the most appropriate tag inside the method doc comment:&lt;br /&gt;
** &#039;&#039;&#039;@Given&#039;&#039;&#039; - A step to set up the initial context (for example &#039;&#039;the following &amp;quot;courses&amp;quot; exists&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@When&#039;&#039;&#039; - An action that provokes an event (for example &#039;&#039;I press the button &amp;quot;buttonname&amp;quot;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;@Then&#039;&#039;&#039; - Checkings to ensure the outcomes are the expected (for example &#039;&#039;I should see &amp;quot;whatever&amp;quot;&#039;&#039;)&lt;br /&gt;
* Depending on the inputs your definition expects you must use a different regular expression:&lt;br /&gt;
** &#039;&#039;&#039;If you expect a number:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_number&amp;gt;\d+)&amp;quot; (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a string or a text:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;info_about_what_you_expect_string&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; Don&#039;t use &#039;&#039;&#039;text_selector_string&#039;&#039;&#039; and &#039;&#039;&#039;selector_string&#039;&#039;&#039; as info strings, they are reserved to selector types (note that the regular expression is quoted between &#039;&#039;&#039;&amp;quot;&#039;&#039;&#039;)&lt;br /&gt;
** &#039;&#039;&#039;If you expect a table with key/value pairs (for example to fill a form):&#039;&#039;&#039; Finish your regular expression with &#039;&#039;&#039;:&#039;&#039;&#039; and provide info in the description about the contents of the table&lt;br /&gt;
** &#039;&#039;&#039;If you expect a selector type:&#039;&#039;&#039; &amp;quot;(?P&amp;lt;selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; or &amp;quot;(?P&amp;lt;text_selector_string&amp;gt;[^&amp;quot;]*)&amp;quot; depending on whether you want to use any selector or you want a text-based selector (more info about selectors in https://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps)&lt;br /&gt;
* To make test writer&#039;s life better is good to include explicative info in the subexpressions of the regular expression about what the test writer is supposed to put in there (for example &#039;&#039;I expand &amp;quot;(?P&amp;lt;nodetext&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; node&#039;&#039;)&lt;br /&gt;
* Is recommended to use the static part of the regular expression as the name of the method, using underscores instead of spaces (see current steps definitions)&lt;br /&gt;
&lt;br /&gt;
== Links == &lt;br /&gt;
* Guidelines for contributors: [[Acceptance_testing/Contributing_automated_tests|Contributing automated tests]]&lt;br /&gt;
* Technical info: [[Behat integration]]&lt;br /&gt;
* Behat CLI command options: http://docs.behat.org/guides/6.cli.html&lt;br /&gt;
* How to use selectors to interact with the site elements: http://mink.behat.org/#traverse-the-page-selectors&lt;br /&gt;
* See Also [https://tracker.moodle.org/browse/MDL-37046 MDL37046] for clear instruction&lt;br /&gt;
[[Category:Behat]][[Category:Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[es:Prueba de aceptación]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=32585</id>
		<title>NanoGong/Converting to Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=32585"/>
		<updated>2012-02-27T15:07:19Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
This page is for collecting relevant information for converting NanoGong to Moodle 2.0. Any help is welcome!&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 10:35, 7 April 2011 (UTC)&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
== Java settings ==&lt;br /&gt;
(Not sure if this is relevant, but better to keep in mind ...)&lt;br /&gt;
* see [[Question type plugin how to#Configuration settings for your question type]]&lt;br /&gt;
&lt;br /&gt;
== Filter ==&lt;br /&gt;
General information on filters in Moodle 2.0 can be found at [[Filters 2.0]] and for developers at [[Filters 2.0]].&lt;br /&gt;
&lt;br /&gt;
=== Language folder ===&lt;br /&gt;
Add language folder: &#039;&#039;&#039;\lang\en\filter_nanogong.php&#039;&#039;&#039; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;filtername&#039;] = &#039;NanoGong audio&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Filter function ===&lt;br /&gt;
&lt;br /&gt;
The filter function is wrapped inside a class:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_nanogong extends moodle_text_filter {&lt;br /&gt;
    function filter($text, array $options = array()){&lt;br /&gt;
    ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The callback function has to be &#039;&#039;&#039;outside&#039;&#039;&#039; this class definition!&lt;br /&gt;
&lt;br /&gt;
=== Preventing caching ===&lt;br /&gt;
&lt;br /&gt;
 $CFG-&amp;gt;currenttextiscacheable = false;&lt;br /&gt;
&lt;br /&gt;
is deprecated, outcommented&lt;br /&gt;
&lt;br /&gt;
=== File API ===&lt;br /&gt;
&lt;br /&gt;
==== Proof of concept ====&lt;br /&gt;
* Instead of the old &#039;&#039;&#039;file.php&#039;&#039;&#039; the new File API uses &#039;&#039;&#039;pluginfile.php&#039;&#039;&#039;.&lt;br /&gt;
* I uploaded a test file (sentence.wav) as a resource to find out the internal URL Moodle uses for serving this file.&lt;br /&gt;
* I then added this full URL as attribute to the NanoGong tag:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;http://localhost/moodle-MOODLE_20_WEEKLY/pluginfile.php/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* As the URL is already in its full format I just commented out the following lines in &#039;&#039;&#039;filter.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* This works as a proof of concept.&lt;br /&gt;
&lt;br /&gt;
==== Getting closer ... ====&lt;br /&gt;
&lt;br /&gt;
This modification does also work:&lt;br /&gt;
&lt;br /&gt;
; NanoGong tag&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; filter.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url = &amp;quot;{$CFG-&amp;gt;wwwroot}/pluginfile.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
* see [[File API]] and [[Using the File API]]&lt;br /&gt;
&lt;br /&gt;
* [[File_API#File_serving]]&lt;br /&gt;
* [[Using_the_file_API#Serving_files_to_users]]&lt;br /&gt;
* [[File_storage_conversion_Quiz_and_Questions#Serving_files]]&lt;br /&gt;
* [[Using the File API in Moodle forms]]&lt;br /&gt;
&lt;br /&gt;
== Itamar&#039; first shot ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: Just copied over from http://moodle.org/mod/forum/post.php?reply=807695 --[[User:Frank Ralf|Frank Ralf]] 17:49, 13 September 2011 (WST)&lt;br /&gt;
&lt;br /&gt;
In response to Frank&#039;s request to join forces I thought I&#039;d give it a shot and happy to let ya all know that I&#039;ve managed to make it work as a dataform field:&lt;br /&gt;
&lt;br /&gt;
It is still just a stub and at any rate the implementation assumes all kinds of things the dataform does, so the code itself may not be very useful. But the approach and some relevant bits of code may help so here&#039;s a summary and if you have any questions just let me know.&lt;br /&gt;
&lt;br /&gt;
I basically create a draft area and pass the draft item id to the uploading php script which resides in a designated file. In that script I create an instance of repository_upload and call its upload method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$repo = new repository_upload($repo_id, null, array(&#039;ajax&#039;=&amp;gt;true, &#039;name&#039;=&amp;gt;&#039;&#039;, &#039;type&#039;=&amp;gt;&#039;upload&#039;)); &lt;br /&gt;
try {&lt;br /&gt;
    $ret = $repo-&amp;gt;upload($saveas_filename, $maxbytes);&lt;br /&gt;
} catch (moodle_exception $e) {&lt;br /&gt;
    print $e-&amp;gt;errorcode;&lt;br /&gt;
    die;&lt;br /&gt;
}&lt;br /&gt;
print $saveas_filename. &#039; uploaded&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The repository_upload looks for the uploaded file in a specific place and this place should be indicated in the javascript call in the upload button (btw, just a standard button, not a submit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
var ret = recorder.sendGongRequest(&#039;PostToForm&#039;,&lt;br /&gt;
                                  inputpage,&lt;br /&gt;
                                  &#039;repo_upload_file&#039;,&lt;br /&gt;
                                  &#039;&#039;,&lt;br /&gt;
                                  filename);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then all that remains is to fetch the file from the draft area and store it in a proper area.&lt;br /&gt;
&lt;br /&gt;
I use the repository_upload because I like to reuse existing code as much as possible. This may or may not be the best way. I&#039;ll look into that more thoroughly when I return to finalize the field.&lt;br /&gt;
&lt;br /&gt;
=== Further references ===&lt;br /&gt;
* [[Repository API]]&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
; Necessary updating for Moodle 1.9&lt;br /&gt;
* see [[User:Frank Ralf/NanoGong/1.9|subpage on Moodle 1.9]]&lt;br /&gt;
&lt;br /&gt;
; Migrating to Moodle 2.0&lt;br /&gt;
* [[Migrating contrib code to 2.0]]&lt;br /&gt;
* [[Migrating to 2.0 checklist]]&lt;br /&gt;
* [[Migrating contrib code to 2.0/Experience of converting a module to Moodle 2]]&lt;br /&gt;
&lt;br /&gt;
; Forums&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170422 Release of NanoGong 4.1, an important update for Moodle users]&lt;br /&gt;
&lt;br /&gt;
; Moodle plugin database &lt;br /&gt;
* [http://moodle.org/mod/data/view.php?d=13&amp;amp;mode=list&amp;amp;perpage=50&amp;amp;search=&amp;amp;sort=44&amp;amp;order=ASC&amp;amp;advanced=0&amp;amp;filter=1&amp;amp;advanced=1&amp;amp;f_44=&amp;amp;f_45=nanogong all NanoGong plugins]&lt;br /&gt;
&lt;br /&gt;
; NanoGong documentation&lt;br /&gt;
* http://gong.ust.hk/nanogong/moodle.html&lt;br /&gt;
* http://gong.ust.hk/nanogong/info_php.html&lt;br /&gt;
* [http://gong.ust.hk/moodle/course/view.php?id=2 Gong and NanoGong Demonstration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Moodle 2.0|NanoGong]]&lt;br /&gt;
[[Category:Project]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=32584</id>
		<title>NanoGong/Converting to Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=32584"/>
		<updated>2012-02-27T15:07:03Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NUMBEREDHEADINGS__&lt;br /&gt;
&lt;br /&gt;
{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
This page is for collecting relevant information for converting NanoGong to Moodle 2.0. Any help is welcome!&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 10:35, 7 April 2011 (UTC)&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
== Java settings ==&lt;br /&gt;
(Not sure if this is relevant, but better to keep in mind ...)&lt;br /&gt;
* see [[Question type plugin how to#Configuration settings for your question type]]&lt;br /&gt;
&lt;br /&gt;
== Filter ==&lt;br /&gt;
General information on filters in Moodle 2.0 can be found at [[Filters 2.0]] and for developers at [[Filters 2.0]].&lt;br /&gt;
&lt;br /&gt;
=== Language folder ===&lt;br /&gt;
Add language folder: &#039;&#039;&#039;\lang\en\filter_nanogong.php&#039;&#039;&#039; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;filtername&#039;] = &#039;NanoGong audio&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Filter function ===&lt;br /&gt;
&lt;br /&gt;
The filter function is wrapped inside a class:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_nanogong extends moodle_text_filter {&lt;br /&gt;
    function filter($text, array $options = array()){&lt;br /&gt;
    ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The callback function has to be &#039;&#039;&#039;outside&#039;&#039;&#039; this class definition!&lt;br /&gt;
&lt;br /&gt;
=== Preventing caching ===&lt;br /&gt;
&lt;br /&gt;
 $CFG-&amp;gt;currenttextiscacheable = false;&lt;br /&gt;
&lt;br /&gt;
is deprecated, outcommented&lt;br /&gt;
&lt;br /&gt;
=== File API ===&lt;br /&gt;
&lt;br /&gt;
==== Proof of concept ====&lt;br /&gt;
* Instead of the old &#039;&#039;&#039;file.php&#039;&#039;&#039; the new File API uses &#039;&#039;&#039;pluginfile.php&#039;&#039;&#039;.&lt;br /&gt;
* I uploaded a test file (sentence.wav) as a resource to find out the internal URL Moodle uses for serving this file.&lt;br /&gt;
* I then added this full URL as attribute to the NanoGong tag:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;http://localhost/moodle-MOODLE_20_WEEKLY/pluginfile.php/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* As the URL is already in its full format I just commented out the following lines in &#039;&#039;&#039;filter.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* This works as a proof of concept.&lt;br /&gt;
&lt;br /&gt;
==== Getting closer ... ====&lt;br /&gt;
&lt;br /&gt;
This modification does also work:&lt;br /&gt;
&lt;br /&gt;
; NanoGong tag&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; filter.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url = &amp;quot;{$CFG-&amp;gt;wwwroot}/pluginfile.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
* see [[File API]] and [[Using the File API]]&lt;br /&gt;
&lt;br /&gt;
* [[File_API#File_serving]]&lt;br /&gt;
* [[Using_the_file_API#Serving_files_to_users]]&lt;br /&gt;
* [[File_storage_conversion_Quiz_and_Questions#Serving_files]]&lt;br /&gt;
* [[Using the File API in Moodle forms]]&lt;br /&gt;
&lt;br /&gt;
== Itamar&#039; first shot ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: Just copied over from http://moodle.org/mod/forum/post.php?reply=807695 --[[User:Frank Ralf|Frank Ralf]] 17:49, 13 September 2011 (WST)&lt;br /&gt;
&lt;br /&gt;
In response to Frank&#039;s request to join forces I thought I&#039;d give it a shot and happy to let ya all know that I&#039;ve managed to make it work as a dataform field:&lt;br /&gt;
&lt;br /&gt;
It is still just a stub and at any rate the implementation assumes all kinds of things the dataform does, so the code itself may not be very useful. But the approach and some relevant bits of code may help so here&#039;s a summary and if you have any questions just let me know.&lt;br /&gt;
&lt;br /&gt;
I basically create a draft area and pass the draft item id to the uploading php script which resides in a designated file. In that script I create an instance of repository_upload and call its upload method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$repo = new repository_upload($repo_id, null, array(&#039;ajax&#039;=&amp;gt;true, &#039;name&#039;=&amp;gt;&#039;&#039;, &#039;type&#039;=&amp;gt;&#039;upload&#039;)); &lt;br /&gt;
try {&lt;br /&gt;
    $ret = $repo-&amp;gt;upload($saveas_filename, $maxbytes);&lt;br /&gt;
} catch (moodle_exception $e) {&lt;br /&gt;
    print $e-&amp;gt;errorcode;&lt;br /&gt;
    die;&lt;br /&gt;
}&lt;br /&gt;
print $saveas_filename. &#039; uploaded&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The repository_upload looks for the uploaded file in a specific place and this place should be indicated in the javascript call in the upload button (btw, just a standard button, not a submit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
var ret = recorder.sendGongRequest(&#039;PostToForm&#039;,&lt;br /&gt;
                                  inputpage,&lt;br /&gt;
                                  &#039;repo_upload_file&#039;,&lt;br /&gt;
                                  &#039;&#039;,&lt;br /&gt;
                                  filename);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then all that remains is to fetch the file from the draft area and store it in a proper area.&lt;br /&gt;
&lt;br /&gt;
I use the repository_upload because I like to reuse existing code as much as possible. This may or may not be the best way. I&#039;ll look into that more thoroughly when I return to finalize the field.&lt;br /&gt;
&lt;br /&gt;
=== Further references ===&lt;br /&gt;
* [[Repository API]]&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
; Necessary updating for Moodle 1.9&lt;br /&gt;
* see [[User:Frank Ralf/NanoGong/1.9|subpage on Moodle 1.9]]&lt;br /&gt;
&lt;br /&gt;
; Migrating to Moodle 2.0&lt;br /&gt;
* [[Migrating contrib code to 2.0]]&lt;br /&gt;
* [[Migrating to 2.0 checklist]]&lt;br /&gt;
* [[Migrating contrib code to 2.0/Experience of converting a module to Moodle 2]]&lt;br /&gt;
&lt;br /&gt;
; Forums&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170422 Release of NanoGong 4.1, an important update for Moodle users]&lt;br /&gt;
&lt;br /&gt;
; Moodle plugin database &lt;br /&gt;
* [http://moodle.org/mod/data/view.php?d=13&amp;amp;mode=list&amp;amp;perpage=50&amp;amp;search=&amp;amp;sort=44&amp;amp;order=ASC&amp;amp;advanced=0&amp;amp;filter=1&amp;amp;advanced=1&amp;amp;f_44=&amp;amp;f_45=nanogong all NanoGong plugins]&lt;br /&gt;
&lt;br /&gt;
; NanoGong documentation&lt;br /&gt;
* http://gong.ust.hk/nanogong/moodle.html&lt;br /&gt;
* http://gong.ust.hk/nanogong/info_php.html&lt;br /&gt;
* [http://gong.ust.hk/moodle/course/view.php?id=2 Gong and NanoGong Demonstration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Moodle 2.0|NanoGong]]&lt;br /&gt;
[[Category:Project]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Git_for_Administrators&amp;diff=30404</id>
		<title>Git for Administrators</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Git_for_Administrators&amp;diff=30404"/>
		<updated>2011-11-06T15:27:51Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See [https://docs.moodle.org/20/en/Installing_Moodle_from_Git_repository Installing Moodle from Git repository]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Git_for_Administrators&amp;diff=30403</id>
		<title>Git for Administrators</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Git_for_Administrators&amp;diff=30403"/>
		<updated>2011-11-06T15:27:05Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: manual redirect as automatic #REDIRECT doesn&amp;#039;t work across wikis&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See [https://docs.moodle.org/20/en/Installing_Moodle_from_Git_repository]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Git_for_Administrators&amp;diff=30402</id>
		<title>Git for Administrators</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Git_for_Administrators&amp;diff=30402"/>
		<updated>2011-11-06T15:24:49Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: Redirected page to Installing Moodle from Git repository&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Installing_Moodle_from_Git_repository]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Git_for_Administrators&amp;diff=30401</id>
		<title>Git for Administrators</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Git_for_Administrators&amp;diff=30401"/>
		<updated>2011-11-06T15:24:19Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: redirect to already exisiting page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [https://docs.moodle.org/20/en/Installing_Moodle_from_Git_repository]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29401</id>
		<title>NanoGong/Converting to Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29401"/>
		<updated>2011-09-13T09:49:53Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Itamar&amp;#039; first shot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
This page is for collecting relevant information for converting NanoGong to Moodle 2.0. Any help is welcome!&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 10:35, 7 April 2011 (UTC)&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
== Java settings ==&lt;br /&gt;
(Not sure if this is relevant, but better to keep in mind ...)&lt;br /&gt;
* see [[Question type plugin how to#Configuration settings for your question type]]&lt;br /&gt;
&lt;br /&gt;
== Filter ==&lt;br /&gt;
General information on filters in Moodle 2.0 can be found at [[Filters 2.0]] and for developers at [[Filters 2.0]].&lt;br /&gt;
&lt;br /&gt;
=== Language folder ===&lt;br /&gt;
Add language folder: &#039;&#039;&#039;\lang\en\filter_nanogong.php&#039;&#039;&#039; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;filtername&#039;] = &#039;NanoGong audio&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Filter function ===&lt;br /&gt;
&lt;br /&gt;
The filter function is wrapped inside a class:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_nanogong extends moodle_text_filter {&lt;br /&gt;
    function filter($text, array $options = array()){&lt;br /&gt;
    ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The callback function has to be &#039;&#039;&#039;outside&#039;&#039;&#039; this class definition!&lt;br /&gt;
&lt;br /&gt;
=== Preventing caching ===&lt;br /&gt;
&lt;br /&gt;
 $CFG-&amp;gt;currenttextiscacheable = false;&lt;br /&gt;
&lt;br /&gt;
is deprecated, outcommented&lt;br /&gt;
&lt;br /&gt;
=== File API ===&lt;br /&gt;
&lt;br /&gt;
==== Proof of concept ====&lt;br /&gt;
* Instead of the old &#039;&#039;&#039;file.php&#039;&#039;&#039; the new File API uses &#039;&#039;&#039;pluginfile.php&#039;&#039;&#039;.&lt;br /&gt;
* I uploaded a test file (sentence.wav) as a resource to find out the internal URL Moodle uses for serving this file.&lt;br /&gt;
* I then added this full URL as attribute to the NanoGong tag:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;http://localhost/moodle-MOODLE_20_WEEKLY/pluginfile.php/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* As the URL is already in its full format I just commented out the following lines in &#039;&#039;&#039;filter.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* This works as a proof of concept.&lt;br /&gt;
&lt;br /&gt;
==== Getting closer ... ====&lt;br /&gt;
&lt;br /&gt;
This modification does also work:&lt;br /&gt;
&lt;br /&gt;
; NanoGong tag&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; filter.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url = &amp;quot;{$CFG-&amp;gt;wwwroot}/pluginfile.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
* see [[File API]] and [[Using the File API]]&lt;br /&gt;
&lt;br /&gt;
* [[File_API#File_serving]]&lt;br /&gt;
* [[Using_the_file_API#Serving_files_to_users]]&lt;br /&gt;
* [[File_storage_conversion_Quiz_and_Questions#Serving_files]]&lt;br /&gt;
* [[Using the File API in Moodle forms]]&lt;br /&gt;
&lt;br /&gt;
== Itamar&#039; first shot ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: Just copied over from http://moodle.org/mod/forum/post.php?reply=807695 --[[User:Frank Ralf|Frank Ralf]] 17:49, 13 September 2011 (WST)&lt;br /&gt;
&lt;br /&gt;
In response to Frank&#039;s request to join forces I thought I&#039;d give it a shot and happy to let ya all know that I&#039;ve managed to make it work as a dataform field:&lt;br /&gt;
&lt;br /&gt;
It is still just a stub and at any rate the implementation assumes all kinds of things the dataform does, so the code itself may not be very useful. But the approach and some relevant bits of code may help so here&#039;s a summary and if you have any questions just let me know.&lt;br /&gt;
&lt;br /&gt;
I basically create a draft area and pass the draft item id to the uploading php script which resides in a designated file. In that script I create an instance of repository_upload and call its upload method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$repo = new repository_upload($repo_id, null, array(&#039;ajax&#039;=&amp;gt;true, &#039;name&#039;=&amp;gt;&#039;&#039;, &#039;type&#039;=&amp;gt;&#039;upload&#039;)); &lt;br /&gt;
try {&lt;br /&gt;
    $ret = $repo-&amp;gt;upload($saveas_filename, $maxbytes);&lt;br /&gt;
} catch (moodle_exception $e) {&lt;br /&gt;
    print $e-&amp;gt;errorcode;&lt;br /&gt;
    die;&lt;br /&gt;
}&lt;br /&gt;
print $saveas_filename. &#039; uploaded&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The repository_upload looks for the uploaded file in a specific place and this place should be indicated in the javascript call in the upload button (btw, just a standard button, not a submit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
var ret = recorder.sendGongRequest(&#039;PostToForm&#039;,&lt;br /&gt;
                                  inputpage,&lt;br /&gt;
                                  &#039;repo_upload_file&#039;,&lt;br /&gt;
                                  &#039;&#039;,&lt;br /&gt;
                                  filename);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then all that remains is to fetch the file from the draft area and store it in a proper area.&lt;br /&gt;
&lt;br /&gt;
I use the repository_upload because I like to reuse existing code as much as possible. This may or may not be the best way. I&#039;ll look into that more thoroughly when I return to finalize the field.&lt;br /&gt;
&lt;br /&gt;
=== Further references ===&lt;br /&gt;
* [[Repository API]]&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
; Necessary updating for Moodle 1.9&lt;br /&gt;
* see [[User:Frank Ralf/NanoGong/1.9|subpage on Moodle 1.9]]&lt;br /&gt;
&lt;br /&gt;
; Migrating to Moodle 2.0&lt;br /&gt;
* [[Migrating contrib code to 2.0]]&lt;br /&gt;
* [[Migrating to 2.0 checklist]]&lt;br /&gt;
* [[Migrating contrib code to 2.0/Experience of converting a module to Moodle 2]]&lt;br /&gt;
&lt;br /&gt;
; Forums&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170422 Release of NanoGong 4.1, an important update for Moodle users]&lt;br /&gt;
&lt;br /&gt;
; Moodle plugin database &lt;br /&gt;
* [http://moodle.org/mod/data/view.php?d=13&amp;amp;mode=list&amp;amp;perpage=50&amp;amp;search=&amp;amp;sort=44&amp;amp;order=ASC&amp;amp;advanced=0&amp;amp;filter=1&amp;amp;advanced=1&amp;amp;f_44=&amp;amp;f_45=nanogong all NanoGong plugins]&lt;br /&gt;
&lt;br /&gt;
; NanoGong documentation&lt;br /&gt;
* http://gong.ust.hk/nanogong/moodle.html&lt;br /&gt;
* http://gong.ust.hk/nanogong/info_php.html&lt;br /&gt;
* [http://gong.ust.hk/moodle/course/view.php?id=2 Gong and NanoGong Demonstration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Moodle 2.0|NanoGong]]&lt;br /&gt;
[[Category:Project]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29400</id>
		<title>NanoGong/Converting to Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29400"/>
		<updated>2011-09-13T09:49:01Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Itamar&amp;#039; first shot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
This page is for collecting relevant information for converting NanoGong to Moodle 2.0. Any help is welcome!&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 10:35, 7 April 2011 (UTC)&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
== Java settings ==&lt;br /&gt;
(Not sure if this is relevant, but better to keep in mind ...)&lt;br /&gt;
* see [[Question type plugin how to#Configuration settings for your question type]]&lt;br /&gt;
&lt;br /&gt;
== Filter ==&lt;br /&gt;
General information on filters in Moodle 2.0 can be found at [[Filters 2.0]] and for developers at [[Filters 2.0]].&lt;br /&gt;
&lt;br /&gt;
=== Language folder ===&lt;br /&gt;
Add language folder: &#039;&#039;&#039;\lang\en\filter_nanogong.php&#039;&#039;&#039; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;filtername&#039;] = &#039;NanoGong audio&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Filter function ===&lt;br /&gt;
&lt;br /&gt;
The filter function is wrapped inside a class:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_nanogong extends moodle_text_filter {&lt;br /&gt;
    function filter($text, array $options = array()){&lt;br /&gt;
    ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The callback function has to be &#039;&#039;&#039;outside&#039;&#039;&#039; this class definition!&lt;br /&gt;
&lt;br /&gt;
=== Preventing caching ===&lt;br /&gt;
&lt;br /&gt;
 $CFG-&amp;gt;currenttextiscacheable = false;&lt;br /&gt;
&lt;br /&gt;
is deprecated, outcommented&lt;br /&gt;
&lt;br /&gt;
=== File API ===&lt;br /&gt;
&lt;br /&gt;
==== Proof of concept ====&lt;br /&gt;
* Instead of the old &#039;&#039;&#039;file.php&#039;&#039;&#039; the new File API uses &#039;&#039;&#039;pluginfile.php&#039;&#039;&#039;.&lt;br /&gt;
* I uploaded a test file (sentence.wav) as a resource to find out the internal URL Moodle uses for serving this file.&lt;br /&gt;
* I then added this full URL as attribute to the NanoGong tag:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;http://localhost/moodle-MOODLE_20_WEEKLY/pluginfile.php/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* As the URL is already in its full format I just commented out the following lines in &#039;&#039;&#039;filter.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* This works as a proof of concept.&lt;br /&gt;
&lt;br /&gt;
==== Getting closer ... ====&lt;br /&gt;
&lt;br /&gt;
This modification does also work:&lt;br /&gt;
&lt;br /&gt;
; NanoGong tag&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; filter.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url = &amp;quot;{$CFG-&amp;gt;wwwroot}/pluginfile.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
* see [[File API]] and [[Using the File API]]&lt;br /&gt;
&lt;br /&gt;
* [[File_API#File_serving]]&lt;br /&gt;
* [[Using_the_file_API#Serving_files_to_users]]&lt;br /&gt;
* [[File_storage_conversion_Quiz_and_Questions#Serving_files]]&lt;br /&gt;
* [[Using the File API in Moodle forms]]&lt;br /&gt;
&lt;br /&gt;
== Itamar&#039; first shot ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: Just copied over from http://moodle.org/mod/forum/post.php?reply=807695 --[[User:Frank Ralf|Frank Ralf]] 17:49, 13 September 2011 (WST)&lt;br /&gt;
&lt;br /&gt;
In response to Frank&#039;s request to join forces I thought I&#039;d give it a shot and happy to let ya all know that I&#039;ve managed to make it work as a dataform field:&lt;br /&gt;
&lt;br /&gt;
It is still just a stub and at any rate the implementation assumes all kinds of things the dataform does, so the code itself may not be very useful. But the approach and some relevant bits of code may help so here&#039;s a summary and if you have any questions just let me know.&lt;br /&gt;
&lt;br /&gt;
I basically create a draft area and pass the draft item id to the uploading php script which resides in a designated file. In that script I create an instance of repository_upload and call its upload method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$repo = new repository_upload($repo_id, null, array(&#039;ajax&#039;=&amp;gt;true, &#039;name&#039;=&amp;gt;&#039;&#039;, &#039;type&#039;=&amp;gt;&#039;upload&#039;)); &lt;br /&gt;
try {&lt;br /&gt;
    $ret = $repo-&amp;gt;upload($saveas_filename, $maxbytes);&lt;br /&gt;
} catch (moodle_exception $e) {&lt;br /&gt;
    print $e-&amp;gt;errorcode;&lt;br /&gt;
    die;&lt;br /&gt;
}&lt;br /&gt;
print $saveas_filename. &#039; uploaded&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The repository_upload looks for the uploaded file in a specific place and this place should be indicated in the javascript call in the upload button (btw, just a standard button, not a submit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt;code php&amp;gt;&lt;br /&gt;
    var ret = recorder.sendGongRequest(&#039;PostToForm&#039;,&lt;br /&gt;
                                        inputpage,&lt;br /&gt;
                                        &#039;repo_upload_file&#039;,&lt;br /&gt;
                                        &#039;&#039;,&lt;br /&gt;
                                        filename);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then all that remains is to fetch the file from the draft area and store it in a proper area.&lt;br /&gt;
&lt;br /&gt;
I use the repository_upload because I like to reuse existing code as much as possible. This may or may not be the best way. I&#039;ll look into that more thoroughly when I return to finalize the field.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
; Necessary updating for Moodle 1.9&lt;br /&gt;
* see [[User:Frank Ralf/NanoGong/1.9|subpage on Moodle 1.9]]&lt;br /&gt;
&lt;br /&gt;
; Migrating to Moodle 2.0&lt;br /&gt;
* [[Migrating contrib code to 2.0]]&lt;br /&gt;
* [[Migrating to 2.0 checklist]]&lt;br /&gt;
* [[Migrating contrib code to 2.0/Experience of converting a module to Moodle 2]]&lt;br /&gt;
&lt;br /&gt;
; Forums&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170422 Release of NanoGong 4.1, an important update for Moodle users]&lt;br /&gt;
&lt;br /&gt;
; Moodle plugin database &lt;br /&gt;
* [http://moodle.org/mod/data/view.php?d=13&amp;amp;mode=list&amp;amp;perpage=50&amp;amp;search=&amp;amp;sort=44&amp;amp;order=ASC&amp;amp;advanced=0&amp;amp;filter=1&amp;amp;advanced=1&amp;amp;f_44=&amp;amp;f_45=nanogong all NanoGong plugins]&lt;br /&gt;
&lt;br /&gt;
; NanoGong documentation&lt;br /&gt;
* http://gong.ust.hk/nanogong/moodle.html&lt;br /&gt;
* http://gong.ust.hk/nanogong/info_php.html&lt;br /&gt;
* [http://gong.ust.hk/moodle/course/view.php?id=2 Gong and NanoGong Demonstration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Moodle 2.0|NanoGong]]&lt;br /&gt;
[[Category:Project]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29399</id>
		<title>NanoGong/Converting to Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29399"/>
		<updated>2011-09-13T09:47:41Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Itamar&amp;#039; first shot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
This page is for collecting relevant information for converting NanoGong to Moodle 2.0. Any help is welcome!&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 10:35, 7 April 2011 (UTC)&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
== Java settings ==&lt;br /&gt;
(Not sure if this is relevant, but better to keep in mind ...)&lt;br /&gt;
* see [[Question type plugin how to#Configuration settings for your question type]]&lt;br /&gt;
&lt;br /&gt;
== Filter ==&lt;br /&gt;
General information on filters in Moodle 2.0 can be found at [[Filters 2.0]] and for developers at [[Filters 2.0]].&lt;br /&gt;
&lt;br /&gt;
=== Language folder ===&lt;br /&gt;
Add language folder: &#039;&#039;&#039;\lang\en\filter_nanogong.php&#039;&#039;&#039; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;filtername&#039;] = &#039;NanoGong audio&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Filter function ===&lt;br /&gt;
&lt;br /&gt;
The filter function is wrapped inside a class:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_nanogong extends moodle_text_filter {&lt;br /&gt;
    function filter($text, array $options = array()){&lt;br /&gt;
    ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The callback function has to be &#039;&#039;&#039;outside&#039;&#039;&#039; this class definition!&lt;br /&gt;
&lt;br /&gt;
=== Preventing caching ===&lt;br /&gt;
&lt;br /&gt;
 $CFG-&amp;gt;currenttextiscacheable = false;&lt;br /&gt;
&lt;br /&gt;
is deprecated, outcommented&lt;br /&gt;
&lt;br /&gt;
=== File API ===&lt;br /&gt;
&lt;br /&gt;
==== Proof of concept ====&lt;br /&gt;
* Instead of the old &#039;&#039;&#039;file.php&#039;&#039;&#039; the new File API uses &#039;&#039;&#039;pluginfile.php&#039;&#039;&#039;.&lt;br /&gt;
* I uploaded a test file (sentence.wav) as a resource to find out the internal URL Moodle uses for serving this file.&lt;br /&gt;
* I then added this full URL as attribute to the NanoGong tag:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;http://localhost/moodle-MOODLE_20_WEEKLY/pluginfile.php/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* As the URL is already in its full format I just commented out the following lines in &#039;&#039;&#039;filter.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* This works as a proof of concept.&lt;br /&gt;
&lt;br /&gt;
==== Getting closer ... ====&lt;br /&gt;
&lt;br /&gt;
This modification does also work:&lt;br /&gt;
&lt;br /&gt;
; NanoGong tag&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; filter.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url = &amp;quot;{$CFG-&amp;gt;wwwroot}/pluginfile.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
* see [[File API]] and [[Using the File API]]&lt;br /&gt;
&lt;br /&gt;
* [[File_API#File_serving]]&lt;br /&gt;
* [[Using_the_file_API#Serving_files_to_users]]&lt;br /&gt;
* [[File_storage_conversion_Quiz_and_Questions#Serving_files]]&lt;br /&gt;
* [[Using the File API in Moodle forms]]&lt;br /&gt;
&lt;br /&gt;
== Itamar&#039; first shot ==&lt;br /&gt;
&lt;br /&gt;
{{Note|&lt;br /&gt;
Just copied over from http://moodle.org/mod/forum/post.php?reply=807695&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
In response to Frank&#039;s request to join forces I thought I&#039;d give it a shot and happy to let ya all know that I&#039;ve managed to make it work as a dataform field:&lt;br /&gt;
&lt;br /&gt;
It is still just a stub and at any rate the implementation assumes all kinds of things the dataform does, so the code itself may not be very useful. But the approach and some relevant bits of code may help so here&#039;s a summary and if you have any questions just let me know.&lt;br /&gt;
&lt;br /&gt;
I basically create a draft area and pass the draft item id to the uploading php script which resides in a designated file. In that script I create an instance of repository_upload and call its upload method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$repo = new repository_upload($repo_id, null, array(&#039;ajax&#039;=&amp;gt;true, &#039;name&#039;=&amp;gt;&#039;&#039;, &#039;type&#039;=&amp;gt;&#039;upload&#039;)); &lt;br /&gt;
try {&lt;br /&gt;
    $ret = $repo-&amp;gt;upload($saveas_filename, $maxbytes);&lt;br /&gt;
} catch (moodle_exception $e) {&lt;br /&gt;
    print $e-&amp;gt;errorcode;&lt;br /&gt;
    die;&lt;br /&gt;
}&lt;br /&gt;
print $saveas_filename. &#039; uploaded&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The repository_upload looks for the uploaded file in a specific place and this place should be indicated in the javascript call in the upload button (btw, just a standard button, not a submit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt;code php&amp;gt;&lt;br /&gt;
    var ret = recorder.sendGongRequest(&#039;PostToForm&#039;,&lt;br /&gt;
                                        inputpage,&lt;br /&gt;
                                        &#039;repo_upload_file&#039;,&lt;br /&gt;
                                        &#039;&#039;,&lt;br /&gt;
                                        filename);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then all that remains is to fetch the file from the draft area and store it in a proper area.&lt;br /&gt;
&lt;br /&gt;
I use the repository_upload because I like to reuse existing code as much as possible. This may or may not be the best way. I&#039;ll look into that more thoroughly when I return to finalize the field.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
; Necessary updating for Moodle 1.9&lt;br /&gt;
* see [[User:Frank Ralf/NanoGong/1.9|subpage on Moodle 1.9]]&lt;br /&gt;
&lt;br /&gt;
; Migrating to Moodle 2.0&lt;br /&gt;
* [[Migrating contrib code to 2.0]]&lt;br /&gt;
* [[Migrating to 2.0 checklist]]&lt;br /&gt;
* [[Migrating contrib code to 2.0/Experience of converting a module to Moodle 2]]&lt;br /&gt;
&lt;br /&gt;
; Forums&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170422 Release of NanoGong 4.1, an important update for Moodle users]&lt;br /&gt;
&lt;br /&gt;
; Moodle plugin database &lt;br /&gt;
* [http://moodle.org/mod/data/view.php?d=13&amp;amp;mode=list&amp;amp;perpage=50&amp;amp;search=&amp;amp;sort=44&amp;amp;order=ASC&amp;amp;advanced=0&amp;amp;filter=1&amp;amp;advanced=1&amp;amp;f_44=&amp;amp;f_45=nanogong all NanoGong plugins]&lt;br /&gt;
&lt;br /&gt;
; NanoGong documentation&lt;br /&gt;
* http://gong.ust.hk/nanogong/moodle.html&lt;br /&gt;
* http://gong.ust.hk/nanogong/info_php.html&lt;br /&gt;
* [http://gong.ust.hk/moodle/course/view.php?id=2 Gong and NanoGong Demonstration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Moodle 2.0|NanoGong]]&lt;br /&gt;
[[Category:Project]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29398</id>
		<title>NanoGong/Converting to Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29398"/>
		<updated>2011-09-13T09:45:39Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Itamar&amp;#039; first shot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
This page is for collecting relevant information for converting NanoGong to Moodle 2.0. Any help is welcome!&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 10:35, 7 April 2011 (UTC)&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
== Java settings ==&lt;br /&gt;
(Not sure if this is relevant, but better to keep in mind ...)&lt;br /&gt;
* see [[Question type plugin how to#Configuration settings for your question type]]&lt;br /&gt;
&lt;br /&gt;
== Filter ==&lt;br /&gt;
General information on filters in Moodle 2.0 can be found at [[Filters 2.0]] and for developers at [[Filters 2.0]].&lt;br /&gt;
&lt;br /&gt;
=== Language folder ===&lt;br /&gt;
Add language folder: &#039;&#039;&#039;\lang\en\filter_nanogong.php&#039;&#039;&#039; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;filtername&#039;] = &#039;NanoGong audio&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Filter function ===&lt;br /&gt;
&lt;br /&gt;
The filter function is wrapped inside a class:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_nanogong extends moodle_text_filter {&lt;br /&gt;
    function filter($text, array $options = array()){&lt;br /&gt;
    ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The callback function has to be &#039;&#039;&#039;outside&#039;&#039;&#039; this class definition!&lt;br /&gt;
&lt;br /&gt;
=== Preventing caching ===&lt;br /&gt;
&lt;br /&gt;
 $CFG-&amp;gt;currenttextiscacheable = false;&lt;br /&gt;
&lt;br /&gt;
is deprecated, outcommented&lt;br /&gt;
&lt;br /&gt;
=== File API ===&lt;br /&gt;
&lt;br /&gt;
==== Proof of concept ====&lt;br /&gt;
* Instead of the old &#039;&#039;&#039;file.php&#039;&#039;&#039; the new File API uses &#039;&#039;&#039;pluginfile.php&#039;&#039;&#039;.&lt;br /&gt;
* I uploaded a test file (sentence.wav) as a resource to find out the internal URL Moodle uses for serving this file.&lt;br /&gt;
* I then added this full URL as attribute to the NanoGong tag:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;http://localhost/moodle-MOODLE_20_WEEKLY/pluginfile.php/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* As the URL is already in its full format I just commented out the following lines in &#039;&#039;&#039;filter.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* This works as a proof of concept.&lt;br /&gt;
&lt;br /&gt;
==== Getting closer ... ====&lt;br /&gt;
&lt;br /&gt;
This modification does also work:&lt;br /&gt;
&lt;br /&gt;
; NanoGong tag&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; filter.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url = &amp;quot;{$CFG-&amp;gt;wwwroot}/pluginfile.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
* see [[File API]] and [[Using the File API]]&lt;br /&gt;
&lt;br /&gt;
* [[File_API#File_serving]]&lt;br /&gt;
* [[Using_the_file_API#Serving_files_to_users]]&lt;br /&gt;
* [[File_storage_conversion_Quiz_and_Questions#Serving_files]]&lt;br /&gt;
* [[Using the File API in Moodle forms]]&lt;br /&gt;
&lt;br /&gt;
== Itamar&#039; first shot ==&lt;br /&gt;
&lt;br /&gt;
In response to Frank&#039;s request to join forces I thought I&#039;d give it a shot and happy to let ya all know that I&#039;ve managed to make it work as a dataform field:&lt;br /&gt;
&lt;br /&gt;
It is still just a stub and at any rate the implementation assumes all kinds of things the dataform does, so the code itself may not be very useful. But the approach and some relevant bits of code may help so here&#039;s a summary and if you have any questions just let me know.&lt;br /&gt;
&lt;br /&gt;
I basically create a draft area and pass the draft item id to the uploading php script which resides in a designated file. In that script I create an instance of repository_upload and call its upload method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$repo = new repository_upload($repo_id, null, array(&#039;ajax&#039;=&amp;gt;true, &#039;name&#039;=&amp;gt;&#039;&#039;, &#039;type&#039;=&amp;gt;&#039;upload&#039;)); &lt;br /&gt;
try {&lt;br /&gt;
    $ret = $repo-&amp;gt;upload($saveas_filename, $maxbytes);&lt;br /&gt;
} catch (moodle_exception $e) {&lt;br /&gt;
    print $e-&amp;gt;errorcode;&lt;br /&gt;
    die;&lt;br /&gt;
}&lt;br /&gt;
print $saveas_filename. &#039; uploaded&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The repository_upload looks for the uploaded file in a specific place and this place should be indicated in the javascript call in the upload button (btw, just a standard button, not a submit):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt;code php&amp;gt;&lt;br /&gt;
    var ret = recorder.sendGongRequest(&#039;PostToForm&#039;,&lt;br /&gt;
                                        inputpage,&lt;br /&gt;
                                        &#039;repo_upload_file&#039;,&lt;br /&gt;
                                        &#039;&#039;,&lt;br /&gt;
                                        filename);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then all that remains is to fetch the file from the draft area and store it in a proper area.&lt;br /&gt;
&lt;br /&gt;
I use the repository_upload because I like to reuse existing code as much as possible. This may or may not be the best way. I&#039;ll look into that more thoroughly when I return to finalize the field.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
; Necessary updating for Moodle 1.9&lt;br /&gt;
* see [[User:Frank Ralf/NanoGong/1.9|subpage on Moodle 1.9]]&lt;br /&gt;
&lt;br /&gt;
; Migrating to Moodle 2.0&lt;br /&gt;
* [[Migrating contrib code to 2.0]]&lt;br /&gt;
* [[Migrating to 2.0 checklist]]&lt;br /&gt;
* [[Migrating contrib code to 2.0/Experience of converting a module to Moodle 2]]&lt;br /&gt;
&lt;br /&gt;
; Forums&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170422 Release of NanoGong 4.1, an important update for Moodle users]&lt;br /&gt;
&lt;br /&gt;
; Moodle plugin database &lt;br /&gt;
* [http://moodle.org/mod/data/view.php?d=13&amp;amp;mode=list&amp;amp;perpage=50&amp;amp;search=&amp;amp;sort=44&amp;amp;order=ASC&amp;amp;advanced=0&amp;amp;filter=1&amp;amp;advanced=1&amp;amp;f_44=&amp;amp;f_45=nanogong all NanoGong plugins]&lt;br /&gt;
&lt;br /&gt;
; NanoGong documentation&lt;br /&gt;
* http://gong.ust.hk/nanogong/moodle.html&lt;br /&gt;
* http://gong.ust.hk/nanogong/info_php.html&lt;br /&gt;
* [http://gong.ust.hk/moodle/course/view.php?id=2 Gong and NanoGong Demonstration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Moodle 2.0|NanoGong]]&lt;br /&gt;
[[Category:Project]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29397</id>
		<title>NanoGong/Converting to Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29397"/>
		<updated>2011-09-13T09:44:57Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* See also */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
This page is for collecting relevant information for converting NanoGong to Moodle 2.0. Any help is welcome!&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 10:35, 7 April 2011 (UTC)&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
== Java settings ==&lt;br /&gt;
(Not sure if this is relevant, but better to keep in mind ...)&lt;br /&gt;
* see [[Question type plugin how to#Configuration settings for your question type]]&lt;br /&gt;
&lt;br /&gt;
== Filter ==&lt;br /&gt;
General information on filters in Moodle 2.0 can be found at [[Filters 2.0]] and for developers at [[Filters 2.0]].&lt;br /&gt;
&lt;br /&gt;
=== Language folder ===&lt;br /&gt;
Add language folder: &#039;&#039;&#039;\lang\en\filter_nanogong.php&#039;&#039;&#039; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;filtername&#039;] = &#039;NanoGong audio&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Filter function ===&lt;br /&gt;
&lt;br /&gt;
The filter function is wrapped inside a class:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_nanogong extends moodle_text_filter {&lt;br /&gt;
    function filter($text, array $options = array()){&lt;br /&gt;
    ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The callback function has to be &#039;&#039;&#039;outside&#039;&#039;&#039; this class definition!&lt;br /&gt;
&lt;br /&gt;
=== Preventing caching ===&lt;br /&gt;
&lt;br /&gt;
 $CFG-&amp;gt;currenttextiscacheable = false;&lt;br /&gt;
&lt;br /&gt;
is deprecated, outcommented&lt;br /&gt;
&lt;br /&gt;
=== File API ===&lt;br /&gt;
&lt;br /&gt;
==== Proof of concept ====&lt;br /&gt;
* Instead of the old &#039;&#039;&#039;file.php&#039;&#039;&#039; the new File API uses &#039;&#039;&#039;pluginfile.php&#039;&#039;&#039;.&lt;br /&gt;
* I uploaded a test file (sentence.wav) as a resource to find out the internal URL Moodle uses for serving this file.&lt;br /&gt;
* I then added this full URL as attribute to the NanoGong tag:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;http://localhost/moodle-MOODLE_20_WEEKLY/pluginfile.php/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* As the URL is already in its full format I just commented out the following lines in &#039;&#039;&#039;filter.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* This works as a proof of concept.&lt;br /&gt;
&lt;br /&gt;
==== Getting closer ... ====&lt;br /&gt;
&lt;br /&gt;
This modification does also work:&lt;br /&gt;
&lt;br /&gt;
; NanoGong tag&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; filter.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url = &amp;quot;{$CFG-&amp;gt;wwwroot}/pluginfile.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
* see [[File API]] and [[Using the File API]]&lt;br /&gt;
&lt;br /&gt;
* [[File_API#File_serving]]&lt;br /&gt;
* [[Using_the_file_API#Serving_files_to_users]]&lt;br /&gt;
* [[File_storage_conversion_Quiz_and_Questions#Serving_files]]&lt;br /&gt;
* [[Using the File API in Moodle forms]]&lt;br /&gt;
&lt;br /&gt;
== Itamar&#039; first shot ==&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
; Necessary updating for Moodle 1.9&lt;br /&gt;
* see [[User:Frank Ralf/NanoGong/1.9|subpage on Moodle 1.9]]&lt;br /&gt;
&lt;br /&gt;
; Migrating to Moodle 2.0&lt;br /&gt;
* [[Migrating contrib code to 2.0]]&lt;br /&gt;
* [[Migrating to 2.0 checklist]]&lt;br /&gt;
* [[Migrating contrib code to 2.0/Experience of converting a module to Moodle 2]]&lt;br /&gt;
&lt;br /&gt;
; Forums&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170422 Release of NanoGong 4.1, an important update for Moodle users]&lt;br /&gt;
&lt;br /&gt;
; Moodle plugin database &lt;br /&gt;
* [http://moodle.org/mod/data/view.php?d=13&amp;amp;mode=list&amp;amp;perpage=50&amp;amp;search=&amp;amp;sort=44&amp;amp;order=ASC&amp;amp;advanced=0&amp;amp;filter=1&amp;amp;advanced=1&amp;amp;f_44=&amp;amp;f_45=nanogong all NanoGong plugins]&lt;br /&gt;
&lt;br /&gt;
; NanoGong documentation&lt;br /&gt;
* http://gong.ust.hk/nanogong/moodle.html&lt;br /&gt;
* http://gong.ust.hk/nanogong/info_php.html&lt;br /&gt;
* [http://gong.ust.hk/moodle/course/view.php?id=2 Gong and NanoGong Demonstration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Moodle 2.0|NanoGong]]&lt;br /&gt;
[[Category:Project]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Repository_API&amp;diff=29393</id>
		<title>Repository API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Repository_API&amp;diff=29393"/>
		<updated>2011-09-12T18:10:43Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
The page is open for everyone so everyone can help correct mistakes and help with the evolution of this document.  However, if you have questions to ask, problems to report or major changes to suggest, please add them to the [[Development_talk:Repository_API|page comments]], or start a discussion in the [http://moodle.org/mod/forum/view.php?id=1807 Repositories forum]. We&#039;ll endeavor to merge all such suggestions into the further development and fix all kinds of problems.&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that parts of this document have been now split off into a separate [[File_API]]&lt;br /&gt;
&lt;br /&gt;
==Objectives==&lt;br /&gt;
&lt;br /&gt;
# Allow all Moodle users to easily bring content into Moodle from external repositories&lt;br /&gt;
# Provide a consistent interface to any external repository, for any Moodle module&lt;br /&gt;
&lt;br /&gt;
==Use cases==&lt;br /&gt;
&lt;br /&gt;
===Teacher adding an external file as a new resource===&lt;br /&gt;
&lt;br /&gt;
# Teacher wants to add a new resource to a course &lt;br /&gt;
# Teacher clicks the &amp;quot;Choose a resource&amp;quot; button&lt;br /&gt;
# Teacher is presented with a simple file picker to choose a file (with a menu to switch between multiple configured repositories)&lt;br /&gt;
# Teacher chooses a file in an external repository&lt;br /&gt;
# File is COPIED into Moodle and stored by the resource module&lt;br /&gt;
# File is marked as owned by that user&lt;br /&gt;
# Whenever someone wants to view that file, the resource module controls access  (see [[File API]] )&lt;br /&gt;
&lt;br /&gt;
===Teacher linking to an external file as a new resource (think video repository) ===&lt;br /&gt;
&lt;br /&gt;
# Teacher wants to display a file in the repository &lt;br /&gt;
# Teacher clicks the &amp;quot;Choose a resource&amp;quot; button&lt;br /&gt;
# Teacher is presented with a simple file picker to choose a file (with a menu to switch between multiple configured repositories)&lt;br /&gt;
# Teacher chooses a file in an external repository&lt;br /&gt;
# Link to the file is COPIED into Moodle and stored by the resource module&lt;br /&gt;
# Link is marked as owned by that user&lt;br /&gt;
# Whenever someone wants to follow that link, the resource module controls access  (see [[File API]] )&lt;br /&gt;
&lt;br /&gt;
===Student submitting an assignment===&lt;br /&gt;
# Student needs to submit an assignment and presses the &amp;quot;Choose files&amp;quot; button&lt;br /&gt;
# Student sees a &amp;quot;file picker&amp;quot; where they can see files listed on any of several configured repositories ([https://docs.moodle.org/en/Image:Filepicker_login.jpg file picker login], [https://docs.moodle.org/en/Image:Filepicker_browser.jpg file picker browser], [https://docs.moodle.org/en/Image:Filepicker_search.jpg file picker search])&lt;br /&gt;
# Student chooses MySpace from the list&lt;br /&gt;
# Student is prompted to enter MySpace username/password (if admin allows it, a checkbox could be there to &amp;quot;remember this for next time&amp;quot; but remember security)&lt;br /&gt;
# Student sees their files in MySpace and chooses one or more&lt;br /&gt;
# Files are copied from MySpace to Moodle &lt;br /&gt;
# Assignment module controls the permissions so that only the Student and assignment graders can see the file (other students would not have permission).&lt;br /&gt;
&lt;br /&gt;
===Student attaching an image to a forum===&lt;br /&gt;
# Student needs to attach an image and presses the &amp;quot;Choose files&amp;quot; button in the posting screen&lt;br /&gt;
# Student sees a &amp;quot;file picker&amp;quot; where they can see files listed on any of several configured repositories&lt;br /&gt;
# Student chooses Mahara from the list&lt;br /&gt;
# Student is prompted to enter Mahara username/password&lt;br /&gt;
# Student sees their files in Mahara and chooses one image&lt;br /&gt;
# Image is copied to Moodle &lt;br /&gt;
# Image file is attached to forum post by Forum module (by reference)&lt;br /&gt;
# Forum module controls permissions so that anyone who can read that forum can see that file&lt;br /&gt;
&lt;br /&gt;
===Student attaching the same image in another forum===&lt;br /&gt;
&lt;br /&gt;
# Student needs to submit an assignment and presses the &amp;quot;Choose files&amp;quot; button&lt;br /&gt;
# Student sees a &amp;quot;file picker&amp;quot; where they can see files listed on any of several configured repositories&lt;br /&gt;
# Student chooses &amp;quot;Local files&amp;quot; from the list and sees all the files they&#039;ve permission to use&lt;br /&gt;
# A COPY of the image file is attached to forum post by Forum module&lt;br /&gt;
# Forum module controls access to this file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Please add more use cases in this same format&lt;br /&gt;
&lt;br /&gt;
==Mock screenshots==&lt;br /&gt;
When you first call up the file picker and choose a repository, you might be asked to log in (if saving of passwords is not allowed):&lt;br /&gt;
&lt;br /&gt;
[[Image:Filepicker_login.jpg]]&lt;br /&gt;
&lt;br /&gt;
Browsing files could look something like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:Filepicker_browser.jpg]]&lt;br /&gt;
&lt;br /&gt;
And you can also search:&lt;br /&gt;
&lt;br /&gt;
[[Image:Filepicker_search.jpg]]&lt;br /&gt;
&lt;br /&gt;
==General architecture==&lt;br /&gt;
&lt;br /&gt;
Each repository plugin (a standard Moodle plugin stored under /repository/xxx) will subclass the standard API and override methods specific to that repository.&lt;br /&gt;
&lt;br /&gt;
As is usual in Moodle, there will be admin settings to disable/enable certain repository plugins as standard, as well as user settings so that users can add their own personal repositories to the standard list (eg [http://briefcase.yahoo.com Yahoo Briefcase] or [http://docs.google.com Google Docs]) and to select their default repository.&lt;br /&gt;
&lt;br /&gt;
Once a repository has been used the file will usually be copied into Moodle there and then.  However there will also be options to:&lt;br /&gt;
* only return the URL to the file if it&#039;s desired to keep it external (but this does present security and integrity risks), or&lt;br /&gt;
* refresh the local file copy regularly and automatically&lt;br /&gt;
* refresh the file manually if desired&lt;br /&gt;
&lt;br /&gt;
Once in Moodle, it is subject to the [[File API]] for access control like any other file.&lt;br /&gt;
&lt;br /&gt;
==Repository requirements==&lt;br /&gt;
&lt;br /&gt;
From the Moodle point of view, each repository is just a hierarchy of nodes.&lt;br /&gt;
&lt;br /&gt;
The repository MUST provide:&lt;br /&gt;
# A URI to download each node (eg file).&lt;br /&gt;
# A list of the nodes (eg files and directories) under a given node (eg directory).  This allows Moodle to construct a standard browse interface (much like a standard OS file picker).&lt;br /&gt;
&lt;br /&gt;
The repository can OPTIONALLY:&lt;br /&gt;
# Require some authentication credentials &lt;br /&gt;
# Provide more metadata about each node (mime type, size, dates, related files, dublin core stuff, etc)&lt;br /&gt;
# Describe a search facility (so that Moodle can construct a search form)&lt;br /&gt;
# Provide copyright and usage rules (or just information about the rules)&lt;br /&gt;
&lt;br /&gt;
==Repository plugins==&lt;br /&gt;
&lt;br /&gt;
Some plugins I&#039;d like to see developed for the first version are:&lt;br /&gt;
* box - an interface to [http://box.net box.net]&lt;br /&gt;
* mahara - an interface to a Mahara installation&lt;br /&gt;
* Server Files - very similar to the current course-based file manager, except user-based&lt;br /&gt;
* Remote Moodle - an interface to another Moodle site, accessed over a secure mnet connection&lt;br /&gt;
* googledocs - an interface to [http://docs.google.com Google Docs]&lt;br /&gt;
* s3 - an interface to [http://www.amazon.com/gp/browse.html?node=16427261 Amazon S3]&lt;br /&gt;
* flickr - an interface to [http://flickr.com flickr]&lt;br /&gt;
* WebDAV - to access arbitrary external WebDAV servers&lt;br /&gt;
* merlot - an interface to the learning materials in [http://www.merlot.org/merlot/materials.htm Merlot.org]&lt;br /&gt;
* File System - a plugin to list files on local file system, of course, you can mount remote files to this local directory&lt;br /&gt;
* youtube - an interface to [http://youtube.com YouTube]&lt;br /&gt;
* jsr170 - an interface that can talk to anything that supports jsr170 (eg [http://www.alfresco.com/ Alfresco])&lt;br /&gt;
* oki - an OKI emulator allowing us to access things with OKI interfaces,like [http://www.fedora.info/ Fedora]&lt;br /&gt;
* briefcase - an interface to [http://briefcase.yahoo.com/ Yahoo Briefcase]&lt;br /&gt;
* myspace - an interface to MySpace files (perhaps via [http://www.programmableweb.com/api/myspace this MySpace API])&lt;br /&gt;
* skydrive - an interface to Microsoft&#039;s [http://skydrive.live.com/ SkyDrive] files&lt;br /&gt;
* Dropbox - an interface to Dropbox files [http://www.dropbox.com]&lt;br /&gt;
* facebook - an interface to Facebook files&lt;br /&gt;
* [http://www.dspace.org/ Dspace] - a repository from MIT&lt;br /&gt;
* DOOR - another popular open source repository&lt;br /&gt;
* SMB shares - An interface for windows shares e.g. personal folders on network drives. Would need to link with LDAP as usernames will often be wholly/partially the same as network folder names. This could be done using SAMBA, but would also need to work on windows machines natively. See [http://moodle.org/mod/data/view.php?d=13&amp;amp;rid=991 this block] for a linux implementation.&lt;br /&gt;
&lt;br /&gt;
==Tables==&lt;br /&gt;
&lt;br /&gt;
=== repository ===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;type&#039;&#039;&#039;&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The type of the repository &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;visible&#039;&#039;&#039;&lt;br /&gt;
|tinyint(1)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|sortorder&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== repository_instances ===&lt;br /&gt;
&lt;br /&gt;
This table contains one entry for every configured external repository instance.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|name&lt;br /&gt;
|varchar 255&lt;br /&gt;
|&lt;br /&gt;
|A custom name for this repository (non-unique)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;typeid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|The id of repository type&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;userid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|The person who created this repository instance&lt;br /&gt;
&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 that this repository is available to ( = system context for site-wide ones)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|username&lt;br /&gt;
|varchar(255)&lt;br /&gt;
| &lt;br /&gt;
|username to log in with, if required (almost never!)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|password&lt;br /&gt;
|varchar(255)&lt;br /&gt;
| &lt;br /&gt;
|password to log in with, if required (almost never!)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The time this repository was created&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The last time the repository was modified&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== repository_instance_config ===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;instanceid&#039;&#039;&#039;&lt;br /&gt;
|int(int)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;name&#039;&#039;&#039;&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|value&lt;br /&gt;
|Text&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===File types===&lt;br /&gt;
&lt;br /&gt;
The context at which someone is inserting a file may require certain file types (eg uploading a new user profile image is only looking for images).  &lt;br /&gt;
&lt;br /&gt;
To support this, the calling code needs to be able to specify the required mimetypes, and the listing code should be able to filter the results based on these mimetypes.  Ideally the repository itself can do the filtering for ultimate speed (though not all repositories will support this).&lt;br /&gt;
&lt;br /&gt;
We will have to develop special new mimetypes for Moodle files like backups (application/vnd.moodle.backup) and IMS learning design (application/vnd.moodle.imsld) etc&lt;br /&gt;
&lt;br /&gt;
==Technical walkthrough==&lt;br /&gt;
&lt;br /&gt;
(See also the functional spec for the [[Repository_File_Picker]] )&lt;br /&gt;
&lt;br /&gt;
There are two main cases where the repository API will be used: as part of a Moodleform to add a file and as part of the HTML editor to add a media element into some HTML).  We also have to cater for the using Moodleforms without Javascript.&lt;br /&gt;
&lt;br /&gt;
In all of these cases the files will be uploaded to Moodle while using the file picker dialog and stored in a temporary file area owned by the currently active user.  It is only AFTER the submission of the entire Moodleform that we will know the full context, itemids to store the file properly, so at this time the file will be copied into the correct filearea.&lt;br /&gt;
&lt;br /&gt;
===Case 1: As part of a Moodleform with Javascript===&lt;br /&gt;
&lt;br /&gt;
1. Moodle module code calls a &amp;quot;filepicker&amp;quot; moodleform item whenever a file is required, which includes the following information to pass to the File API:&lt;br /&gt;
&lt;br /&gt;
 eg $mform-&amp;gt;addElement(&#039;filepicker&#039;, &#039;uniqueelementid&#039;, $fullname, $data)&lt;br /&gt;
 &lt;br /&gt;
2. When rendering the form, Moodle will display a read-only filename field with an &#039;&#039;&#039;&amp;quot;Add file&amp;quot;&#039;&#039;&#039; button next to it.  There will also be a hidden field to store a file reference later (this is what actually gets used, the filename field is just for users to see something).&lt;br /&gt;
&lt;br /&gt;
3. When the add file button is pressed, the form will be &amp;quot;replaced&amp;quot; in the page by a larger resizeable area containing an AJAX file picker.  (After picking the display can be closed).   (There could be a user option to make this a popup window instead, if required)&lt;br /&gt;
&lt;br /&gt;
4. The AJAX file picker interface will list all the active repositories as a menu, and list files in one of several formats (like Windows/Mac/Linux): Details, Names, Icons.&lt;br /&gt;
&lt;br /&gt;
5. For each plugin, the AJAX interface will prompt the user to login first (if required) asking the plugin to log in behind the scenes.  It&#039;ll also ask the plugin to return listing data in response to clicks and searches.  &lt;br /&gt;
&lt;br /&gt;
6. Finally, when the user selects a file and clicks the &amp;quot;Select&amp;quot; button, the AJAX interface will trigger a method in the plugin that will fetch the file and call the File Storage API to &#039;&#039;&#039;store&#039;&#039;&#039; the file using the &#039;&#039;&#039;uniqueelementid&#039;&#039;&#039; and the current user info.  While this is happening, the interface should show some sort of progress bar (ideally) or at least a &amp;quot;loading file&amp;quot; image/sign/message.  &lt;br /&gt;
&lt;br /&gt;
7. After a file has finally been selected we will have a file ID which we can pass back to the original Moodle form (to the hidden field named &#039;&#039;&#039;uniqueelementid_formid&#039;&#039;&#039;).  The picker can then rename the read-only filename field before it hides itself.&lt;br /&gt;
&lt;br /&gt;
8. Submitting the form will trigger the mform processing for this field, which will check fields, create things in the module etc.  Once this has been finally successful the developer must call an mform function to &amp;quot;fix&amp;quot; the info for each file and &amp;quot;move&amp;quot; it into the module file area:&lt;br /&gt;
&lt;br /&gt;
  eg $mform-&amp;gt;store_local_file(&#039;uniqueelementid&#039;, $context, $filearename, $itemid, $filepath);&lt;br /&gt;
&lt;br /&gt;
9. Cron jobs in File Storage api should automatically delete any files in the user&#039;s tempfile area that are older than 7 days or move them into a trash can in the user&#039;s file area (perhaps).&lt;br /&gt;
&lt;br /&gt;
===Case 2: As part of a Moodleform without Javascript===&lt;br /&gt;
&lt;br /&gt;
Steps 1-2 are the same as for the case with Javascript.&lt;br /&gt;
&lt;br /&gt;
3. The add file button is a submit button for the form with a different value.  When the add file button is pressed,&lt;br /&gt;
* the whole form will be &#039;&#039;submitted&#039;&#039; to the original location (but with a different submit button value)&lt;br /&gt;
* moodleforms get_data() will detect this is a &amp;quot;repository save&amp;quot; and can save the full POST info in the current session tagged with the id of the openfile element, together with the URL to return to&lt;br /&gt;
* moodleforms get_data() then redirects the user to a new page showing the main picker interface&lt;br /&gt;
&lt;br /&gt;
4. The file picker interface will have to be a completely new and separate interface from the AJAX one.  It could be a long hierarchy listing, or reload a lot.&lt;br /&gt;
&lt;br /&gt;
5. Finally, when the user selects a file and submits using the &amp;quot;Select&amp;quot; button to picker.php, it will trigger a method in the plugin that will fetch the file and call the [[File_API|File API]] to store the file using the filearea and context information we already had.   While this is happening, the interface can show some sort of progress bar (ideally) or at least a &amp;quot;loading file&amp;quot; image/sign/message.&lt;br /&gt;
&lt;br /&gt;
6. After this, picker.php will redirect/continue back to the original form page.  The form can be constructed as usual, however, when the form is rendered using display() method moodleforms should now look for relevant saved content in the session and use that to override any content in the form (and then delete the saved info in the session).&lt;br /&gt;
&lt;br /&gt;
Steps 8-9 are the same as for the case with Javascript.&lt;br /&gt;
&lt;br /&gt;
===Case 3: As part of a HTML editor===&lt;br /&gt;
&lt;br /&gt;
The key thing here is a move away from storing any absolute URLs to files in our HTML texts.  Instead we&#039;ll store relative names.&lt;br /&gt;
&lt;br /&gt;
1. The moodleform for a textarea (HTML editor) will require a path to the filearea associated with this HTML.  eg &#039;&#039;&#039;wwwroot/pluginfile.php/13/content/0/&#039;&#039;&#039;.  This would have to be the user_draft area if the filearea doesn&#039;t exist yet  eg &#039;&#039;&#039;wwwroot/draftfile.php/userid/tempfile/uniquelementid&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
2. All textarea content will also need to have str_replace done on it to replace @@pluginfile@@/somefilenames.jpg in the content to use this path so that it comes up right in the editor.  (Note this also needs to be done on every format_text command too when showing this text.)&lt;br /&gt;
&lt;br /&gt;
3. The path parameter also needs to be added to the editor configuration in the current page.&lt;br /&gt;
&lt;br /&gt;
4. Editor plugins can be modified to look for these variables in the editor configuration.&lt;br /&gt;
&lt;br /&gt;
5. When adding an image or other media element,  the same AJAX repository picker will show up as a popup div to allow people to pick from any repository and choose files to download.  The repository picker is responsible for downloading the file in real-time, storing it as a user temporary file if the filearea doesn&#039;t already exist, prefixing the supplied path to the filename and returning a URL back to the dialog text input before closing.&lt;br /&gt;
&lt;br /&gt;
6. On submission, and after the HTML is stored, we might now have a new permanent filearea, so we&#039;ll need to update any associated temporary files to make sure they have the proper file area information.&lt;br /&gt;
&lt;br /&gt;
==Repository plugins==&lt;br /&gt;
&lt;br /&gt;
===Required elements===&lt;br /&gt;
&lt;br /&gt;
Each repository plugin is required to contain the following elements:&lt;br /&gt;
&lt;br /&gt;
====class repository()====&lt;br /&gt;
&lt;br /&gt;
This class implements the interface to a particular repository, for browsing, selecting and updating files.  The base class (repository) is defined in /repository/lib.php, while each repository defines an inherited class (eg repository_alfresco) in /repository/repositoryname/repository.class.php&lt;br /&gt;
&lt;br /&gt;
===Optional elements===&lt;br /&gt;
&lt;br /&gt;
Repositories can redefine any of these methods as required (and in some instances, MUST redefine them):&lt;br /&gt;
&lt;br /&gt;
====__construct($repositoryid, $contextid, $options=array(), $readonly)====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUST&#039;&#039;&#039; redefine&lt;br /&gt;
&lt;br /&gt;
Accept necessary parameters, and do initialization of repository.&lt;br /&gt;
&lt;br /&gt;
====get_file($url, $file = &#039;&#039;)====&lt;br /&gt;
&lt;br /&gt;
Given a URL, download a file from there, save the file in a temporary directory.&lt;br /&gt;
&lt;br /&gt;
====get_link($info)====&lt;br /&gt;
Get the url of external resource&lt;br /&gt;
&lt;br /&gt;
====get_listing($path=&#039;/&#039;, $page=&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;)====&lt;br /&gt;
&lt;br /&gt;
Given a path, and perhaps a search, get a listing of files. In the case of AJAX file picker, this function should return json format Javascript array.&lt;br /&gt;
&lt;br /&gt;
====search($keyword)====&lt;br /&gt;
Search repository by given keyword, it will return an array of the same format of get_listing&lt;br /&gt;
&lt;br /&gt;
====print_login()====&lt;br /&gt;
&lt;br /&gt;
Show the login screen, if required. In the case of AJAX file picker, this function should return json format array which defined the login form.&lt;br /&gt;
&lt;br /&gt;
====print_search==== &lt;br /&gt;
&lt;br /&gt;
Print the search form, it will return a json string&lt;br /&gt;
&lt;br /&gt;
====get_meta()====&lt;br /&gt;
Return information for creating ajax request, it is private function, you don&#039;t need to rewrite it.&lt;br /&gt;
&lt;br /&gt;
====create()====&lt;br /&gt;
Create an instance&lt;br /&gt;
&lt;br /&gt;
====delete()====&lt;br /&gt;
Delete this instance from `repository` table&lt;br /&gt;
&lt;br /&gt;
====hide()====&lt;br /&gt;
Hide a repository instance from file picker list&lt;br /&gt;
&lt;br /&gt;
====set_option()====&lt;br /&gt;
set options in data1-data5 fields, can be overrided&lt;br /&gt;
&lt;br /&gt;
====get_option()====&lt;br /&gt;
get option list or a specific option from database&lt;br /&gt;
&lt;br /&gt;
====get_type_option_names()====&lt;br /&gt;
If this plugin needs admin settings, please refine this function to return option names.&lt;br /&gt;
&lt;br /&gt;
====type_config_form()====&lt;br /&gt;
If get_type_option_names return non empty array, this function &#039;&#039;&#039;MUST&#039;&#039;&#039; redefine, it will help to build the setting form.&lt;br /&gt;
&lt;br /&gt;
====type_form_validation()====&lt;br /&gt;
This function can be used for validating the data submitted by plugin setting form.&lt;br /&gt;
&lt;br /&gt;
====get_instance_option_names()====&lt;br /&gt;
If plugin instance needs settings, this function will return instance option names.&lt;br /&gt;
&lt;br /&gt;
====instance_config_form()====&lt;br /&gt;
If get_instance_option_names return non empty array, this function &#039;&#039;&#039;MUST&#039;&#039;&#039; redefine, it will help to build the instance setting form.&lt;br /&gt;
&lt;br /&gt;
====instance_form_validation()====&lt;br /&gt;
This function can be used for validating the data submitted by instance setting form.&lt;br /&gt;
&lt;br /&gt;
====supported_filetypes()====&lt;br /&gt;
What file types are supported by this repository plugin, it will return an array, the file type name is defined in a [http://freemind.sourceforge.net/wiki/index.php/Main_Page freemind] file in lib/file/file_types.mm&lt;br /&gt;
&lt;br /&gt;
====supported_returntypes()====&lt;br /&gt;
The repository plugin could support external link or copying files to moodle. If the plugin support file link only, developer should override this function to return FILE_EXTERNAL, if plugin support copying file only, it should return FILE_INTERNAL, by default, plugin supports both.&lt;br /&gt;
&lt;br /&gt;
====filter()====&lt;br /&gt;
Filter file listing to exclude specific file types&lt;br /&gt;
&lt;br /&gt;
===icon.png===&lt;br /&gt;
&lt;br /&gt;
A logo that represents the repository.  Ideally square but we should handle all sizes.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Repository Administration Specification]]&lt;br /&gt;
* [[Repository Interface for Moodle/Course/User]]&lt;br /&gt;
* [[Repository plugins]]&lt;br /&gt;
* [[Repository File Picker]]&lt;br /&gt;
* [[File API]]&lt;br /&gt;
* [[Portfolio API]]&lt;br /&gt;
* MDL-13766 and MDL-16543 Repository API Meta issues&lt;br /&gt;
&lt;br /&gt;
[[Category:Repositories]]&lt;br /&gt;
&lt;br /&gt;
[[ja:開発:リポジトリAPI]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Repository_API&amp;diff=29392</id>
		<title>Repository API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Repository_API&amp;diff=29392"/>
		<updated>2011-09-12T18:07:06Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: note template added&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}&lt;br /&gt;
&lt;br /&gt;
{{Note|&lt;br /&gt;
The page is open for everyone so everyone can help correct mistakes and help with the evolution of this document.  However, if you have questions to ask, problems to report or major changes to suggest, please add them to the [[Development_talk:Repository_API|page comments]], or start a discussion in the [http://moodle.org/mod/forum/view.php?id=1807 Repositories forum]. We&#039;ll endeavour to merge all such suggestions into the further development and fix all kinds of problems.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Note that parts of this document have been now split off into a separate [[File_API]]&lt;br /&gt;
&lt;br /&gt;
==Objectives==&lt;br /&gt;
&lt;br /&gt;
# Allow all Moodle users to easily bring content into Moodle from external repositories&lt;br /&gt;
# Provide a consistent interface to any external repository, for any Moodle module&lt;br /&gt;
&lt;br /&gt;
==Use cases==&lt;br /&gt;
&lt;br /&gt;
===Teacher adding an external file as a new resource===&lt;br /&gt;
&lt;br /&gt;
# Teacher wants to add a new resource to a course &lt;br /&gt;
# Teacher clicks the &amp;quot;Choose a resource&amp;quot; button&lt;br /&gt;
# Teacher is presented with a simple file picker to choose a file (with a menu to switch between multiple configured repositories)&lt;br /&gt;
# Teacher chooses a file in an external repository&lt;br /&gt;
# File is COPIED into Moodle and stored by the resource module&lt;br /&gt;
# File is marked as owned by that user&lt;br /&gt;
# Whenever someone wants to view that file, the resource module controls access  (see [[File API]] )&lt;br /&gt;
&lt;br /&gt;
===Teacher linking to an external file as a new resource (think video repository) ===&lt;br /&gt;
&lt;br /&gt;
# Teacher wants to display a file in the repository &lt;br /&gt;
# Teacher clicks the &amp;quot;Choose a resource&amp;quot; button&lt;br /&gt;
# Teacher is presented with a simple file picker to choose a file (with a menu to switch between multiple configured repositories)&lt;br /&gt;
# Teacher chooses a file in an external repository&lt;br /&gt;
# Link to the file is COPIED into Moodle and stored by the resource module&lt;br /&gt;
# Link is marked as owned by that user&lt;br /&gt;
# Whenever someone wants to follow that link, the resource module controls access  (see [[File API]] )&lt;br /&gt;
&lt;br /&gt;
===Student submitting an assignment===&lt;br /&gt;
# Student needs to submit an assignment and presses the &amp;quot;Choose files&amp;quot; button&lt;br /&gt;
# Student sees a &amp;quot;file picker&amp;quot; where they can see files listed on any of several configured repositories ([https://docs.moodle.org/en/Image:Filepicker_login.jpg file picker login], [https://docs.moodle.org/en/Image:Filepicker_browser.jpg file picker browser], [https://docs.moodle.org/en/Image:Filepicker_search.jpg file picker search])&lt;br /&gt;
# Student chooses MySpace from the list&lt;br /&gt;
# Student is prompted to enter MySpace username/password (if admin allows it, a checkbox could be there to &amp;quot;remember this for next time&amp;quot; but remember security)&lt;br /&gt;
# Student sees their files in MySpace and chooses one or more&lt;br /&gt;
# Files are copied from MySpace to Moodle &lt;br /&gt;
# Assignment module controls the permissions so that only the Student and assignment graders can see the file (other students would not have permission).&lt;br /&gt;
&lt;br /&gt;
===Student attaching an image to a forum===&lt;br /&gt;
# Student needs to attach an image and presses the &amp;quot;Choose files&amp;quot; button in the posting screen&lt;br /&gt;
# Student sees a &amp;quot;file picker&amp;quot; where they can see files listed on any of several configured repositories&lt;br /&gt;
# Student chooses Mahara from the list&lt;br /&gt;
# Student is prompted to enter Mahara username/password&lt;br /&gt;
# Student sees their files in Mahara and chooses one image&lt;br /&gt;
# Image is copied to Moodle &lt;br /&gt;
# Image file is attached to forum post by Forum module (by reference)&lt;br /&gt;
# Forum module controls permissions so that anyone who can read that forum can see that file&lt;br /&gt;
&lt;br /&gt;
===Student attaching the same image in another forum===&lt;br /&gt;
&lt;br /&gt;
# Student needs to submit an assignment and presses the &amp;quot;Choose files&amp;quot; button&lt;br /&gt;
# Student sees a &amp;quot;file picker&amp;quot; where they can see files listed on any of several configured repositories&lt;br /&gt;
# Student chooses &amp;quot;Local files&amp;quot; from the list and sees all the files they&#039;ve permission to use&lt;br /&gt;
# A COPY of the image file is attached to forum post by Forum module&lt;br /&gt;
# Forum module controls access to this file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Please add more use cases in this same format&lt;br /&gt;
&lt;br /&gt;
==Mock screenshots==&lt;br /&gt;
When you first call up the file picker and choose a repository, you might be asked to log in (if saving of passwords is not allowed):&lt;br /&gt;
&lt;br /&gt;
[[Image:Filepicker_login.jpg]]&lt;br /&gt;
&lt;br /&gt;
Browsing files could look something like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:Filepicker_browser.jpg]]&lt;br /&gt;
&lt;br /&gt;
And you can also search:&lt;br /&gt;
&lt;br /&gt;
[[Image:Filepicker_search.jpg]]&lt;br /&gt;
&lt;br /&gt;
==General architecture==&lt;br /&gt;
&lt;br /&gt;
Each repository plugin (a standard Moodle plugin stored under /repository/xxx) will subclass the standard API and override methods specific to that repository.&lt;br /&gt;
&lt;br /&gt;
As is usual in Moodle, there will be admin settings to disable/enable certain repository plugins as standard, as well as user settings so that users can add their own personal repositories to the standard list (eg [http://briefcase.yahoo.com Yahoo Briefcase] or [http://docs.google.com Google Docs]) and to select their default repository.&lt;br /&gt;
&lt;br /&gt;
Once a repository has been used the file will usually be copied into Moodle there and then.  However there will also be options to:&lt;br /&gt;
* only return the URL to the file if it&#039;s desired to keep it external (but this does present security and integrity risks), or&lt;br /&gt;
* refresh the local file copy regularly and automatically&lt;br /&gt;
* refresh the file manually if desired&lt;br /&gt;
&lt;br /&gt;
Once in Moodle, it is subject to the [[File API]] for access control like any other file.&lt;br /&gt;
&lt;br /&gt;
==Repository requirements==&lt;br /&gt;
&lt;br /&gt;
From the Moodle point of view, each repository is just a hierarchy of nodes.&lt;br /&gt;
&lt;br /&gt;
The repository MUST provide:&lt;br /&gt;
# A URI to download each node (eg file).&lt;br /&gt;
# A list of the nodes (eg files and directories) under a given node (eg directory).  This allows Moodle to construct a standard browse interface (much like a standard OS file picker).&lt;br /&gt;
&lt;br /&gt;
The repository can OPTIONALLY:&lt;br /&gt;
# Require some authentication credentials &lt;br /&gt;
# Provide more metadata about each node (mime type, size, dates, related files, dublin core stuff, etc)&lt;br /&gt;
# Describe a search facility (so that Moodle can construct a search form)&lt;br /&gt;
# Provide copyright and usage rules (or just information about the rules)&lt;br /&gt;
&lt;br /&gt;
==Repository plugins==&lt;br /&gt;
&lt;br /&gt;
Some plugins I&#039;d like to see developed for the first version are:&lt;br /&gt;
* box - an interface to [http://box.net box.net]&lt;br /&gt;
* mahara - an interface to a Mahara installation&lt;br /&gt;
* Server Files - very similar to the current course-based file manager, except user-based&lt;br /&gt;
* Remote Moodle - an interface to another Moodle site, accessed over a secure mnet connection&lt;br /&gt;
* googledocs - an interface to [http://docs.google.com Google Docs]&lt;br /&gt;
* s3 - an interface to [http://www.amazon.com/gp/browse.html?node=16427261 Amazon S3]&lt;br /&gt;
* flickr - an interface to [http://flickr.com flickr]&lt;br /&gt;
* WebDAV - to access arbitrary external WebDAV servers&lt;br /&gt;
* merlot - an interface to the learning materials in [http://www.merlot.org/merlot/materials.htm Merlot.org]&lt;br /&gt;
* File System - a plugin to list files on local file system, of course, you can mount remote files to this local directory&lt;br /&gt;
* youtube - an interface to [http://youtube.com YouTube]&lt;br /&gt;
* jsr170 - an interface that can talk to anything that supports jsr170 (eg [http://www.alfresco.com/ Alfresco])&lt;br /&gt;
* oki - an OKI emulator allowing us to access things with OKI interfaces,like [http://www.fedora.info/ Fedora]&lt;br /&gt;
* briefcase - an interface to [http://briefcase.yahoo.com/ Yahoo Briefcase]&lt;br /&gt;
* myspace - an interface to MySpace files (perhaps via [http://www.programmableweb.com/api/myspace this MySpace API])&lt;br /&gt;
* skydrive - an interface to Microsoft&#039;s [http://skydrive.live.com/ SkyDrive] files&lt;br /&gt;
* Dropbox - an interface to Dropbox files [http://www.dropbox.com]&lt;br /&gt;
* facebook - an interface to Facebook files&lt;br /&gt;
* [http://www.dspace.org/ Dspace] - a repository from MIT&lt;br /&gt;
* DOOR - another popular open source repository&lt;br /&gt;
* SMB shares - An interface for windows shares e.g. personal folders on network drives. Would need to link with LDAP as usernames will often be wholly/partially the same as network folder names. This could be done using SAMBA, but would also need to work on windows machines natively. See [http://moodle.org/mod/data/view.php?d=13&amp;amp;rid=991 this block] for a linux implementation.&lt;br /&gt;
&lt;br /&gt;
==Tables==&lt;br /&gt;
&lt;br /&gt;
=== repository ===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;type&#039;&#039;&#039;&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The type of the repository &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;visible&#039;&#039;&#039;&lt;br /&gt;
|tinyint(1)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|sortorder&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== repository_instances ===&lt;br /&gt;
&lt;br /&gt;
This table contains one entry for every configured external repository instance.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|name&lt;br /&gt;
|varchar 255&lt;br /&gt;
|&lt;br /&gt;
|A custom name for this repository (non-unique)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;typeid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|The id of repository type&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;userid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|The person who created this repository instance&lt;br /&gt;
&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 that this repository is available to ( = system context for site-wide ones)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|username&lt;br /&gt;
|varchar(255)&lt;br /&gt;
| &lt;br /&gt;
|username to log in with, if required (almost never!)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|password&lt;br /&gt;
|varchar(255)&lt;br /&gt;
| &lt;br /&gt;
|password to log in with, if required (almost never!)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The time this repository was created&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The last time the repository was modified&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== repository_instance_config ===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;instanceid&#039;&#039;&#039;&lt;br /&gt;
|int(int)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;name&#039;&#039;&#039;&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|value&lt;br /&gt;
|Text&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===File types===&lt;br /&gt;
&lt;br /&gt;
The context at which someone is inserting a file may require certain file types (eg uploading a new user profile image is only looking for images).  &lt;br /&gt;
&lt;br /&gt;
To support this, the calling code needs to be able to specify the required mimetypes, and the listing code should be able to filter the results based on these mimetypes.  Ideally the repository itself can do the filtering for ultimate speed (though not all repositories will support this).&lt;br /&gt;
&lt;br /&gt;
We will have to develop special new mimetypes for Moodle files like backups (application/vnd.moodle.backup) and IMS learning design (application/vnd.moodle.imsld) etc&lt;br /&gt;
&lt;br /&gt;
==Technical walkthrough==&lt;br /&gt;
&lt;br /&gt;
(See also the functional spec for the [[Repository_File_Picker]] )&lt;br /&gt;
&lt;br /&gt;
There are two main cases where the repository API will be used: as part of a Moodleform to add a file and as part of the HTML editor to add a media element into some HTML).  We also have to cater for the using Moodleforms without Javascript.&lt;br /&gt;
&lt;br /&gt;
In all of these cases the files will be uploaded to Moodle while using the file picker dialog and stored in a temporary file area owned by the currently active user.  It is only AFTER the submission of the entire Moodleform that we will know the full context, itemids to store the file properly, so at this time the file will be copied into the correct filearea.&lt;br /&gt;
&lt;br /&gt;
===Case 1: As part of a Moodleform with Javascript===&lt;br /&gt;
&lt;br /&gt;
1. Moodle module code calls a &amp;quot;filepicker&amp;quot; moodleform item whenever a file is required, which includes the following information to pass to the File API:&lt;br /&gt;
&lt;br /&gt;
 eg $mform-&amp;gt;addElement(&#039;filepicker&#039;, &#039;uniqueelementid&#039;, $fullname, $data)&lt;br /&gt;
 &lt;br /&gt;
2. When rendering the form, Moodle will display a read-only filename field with an &#039;&#039;&#039;&amp;quot;Add file&amp;quot;&#039;&#039;&#039; button next to it.  There will also be a hidden field to store a file reference later (this is what actually gets used, the filename field is just for users to see something).&lt;br /&gt;
&lt;br /&gt;
3. When the add file button is pressed, the form will be &amp;quot;replaced&amp;quot; in the page by a larger resizeable area containing an AJAX file picker.  (After picking the display can be closed).   (There could be a user option to make this a popup window instead, if required)&lt;br /&gt;
&lt;br /&gt;
4. The AJAX file picker interface will list all the active repositories as a menu, and list files in one of several formats (like Windows/Mac/Linux): Details, Names, Icons.&lt;br /&gt;
&lt;br /&gt;
5. For each plugin, the AJAX interface will prompt the user to login first (if required) asking the plugin to log in behind the scenes.  It&#039;ll also ask the plugin to return listing data in response to clicks and searches.  &lt;br /&gt;
&lt;br /&gt;
6. Finally, when the user selects a file and clicks the &amp;quot;Select&amp;quot; button, the AJAX interface will trigger a method in the plugin that will fetch the file and call the File Storage API to &#039;&#039;&#039;store&#039;&#039;&#039; the file using the &#039;&#039;&#039;uniqueelementid&#039;&#039;&#039; and the current user info.  While this is happening, the interface should show some sort of progress bar (ideally) or at least a &amp;quot;loading file&amp;quot; image/sign/message.  &lt;br /&gt;
&lt;br /&gt;
7. After a file has finally been selected we will have a file ID which we can pass back to the original Moodle form (to the hidden field named &#039;&#039;&#039;uniqueelementid_formid&#039;&#039;&#039;).  The picker can then rename the read-only filename field before it hides itself.&lt;br /&gt;
&lt;br /&gt;
8. Submitting the form will trigger the mform processing for this field, which will check fields, create things in the module etc.  Once this has been finally successful the developer must call an mform function to &amp;quot;fix&amp;quot; the info for each file and &amp;quot;move&amp;quot; it into the module file area:&lt;br /&gt;
&lt;br /&gt;
  eg $mform-&amp;gt;store_local_file(&#039;uniqueelementid&#039;, $context, $filearename, $itemid, $filepath);&lt;br /&gt;
&lt;br /&gt;
9. Cron jobs in File Storage api should automatically delete any files in the user&#039;s tempfile area that are older than 7 days or move them into a trash can in the user&#039;s file area (perhaps).&lt;br /&gt;
&lt;br /&gt;
===Case 2: As part of a Moodleform without Javascript===&lt;br /&gt;
&lt;br /&gt;
Steps 1-2 are the same as for the case with Javascript.&lt;br /&gt;
&lt;br /&gt;
3. The add file button is a submit button for the form with a different value.  When the add file button is pressed,&lt;br /&gt;
* the whole form will be &#039;&#039;submitted&#039;&#039; to the original location (but with a different submit button value)&lt;br /&gt;
* moodleforms get_data() will detect this is a &amp;quot;repository save&amp;quot; and can save the full POST info in the current session tagged with the id of the openfile element, together with the URL to return to&lt;br /&gt;
* moodleforms get_data() then redirects the user to a new page showing the main picker interface&lt;br /&gt;
&lt;br /&gt;
4. The file picker interface will have to be a completely new and separate interface from the AJAX one.  It could be a long hierarchy listing, or reload a lot.&lt;br /&gt;
&lt;br /&gt;
5. Finally, when the user selects a file and submits using the &amp;quot;Select&amp;quot; button to picker.php, it will trigger a method in the plugin that will fetch the file and call the [[File_API|File API]] to store the file using the filearea and context information we already had.   While this is happening, the interface can show some sort of progress bar (ideally) or at least a &amp;quot;loading file&amp;quot; image/sign/message.&lt;br /&gt;
&lt;br /&gt;
6. After this, picker.php will redirect/continue back to the original form page.  The form can be constructed as usual, however, when the form is rendered using display() method moodleforms should now look for relevant saved content in the session and use that to override any content in the form (and then delete the saved info in the session).&lt;br /&gt;
&lt;br /&gt;
Steps 8-9 are the same as for the case with Javascript.&lt;br /&gt;
&lt;br /&gt;
===Case 3: As part of a HTML editor===&lt;br /&gt;
&lt;br /&gt;
The key thing here is a move away from storing any absolute URLs to files in our HTML texts.  Instead we&#039;ll store relative names.&lt;br /&gt;
&lt;br /&gt;
1. The moodleform for a textarea (HTML editor) will require a path to the filearea associated with this HTML.  eg &#039;&#039;&#039;wwwroot/pluginfile.php/13/content/0/&#039;&#039;&#039;.  This would have to be the user_draft area if the filearea doesn&#039;t exist yet  eg &#039;&#039;&#039;wwwroot/draftfile.php/userid/tempfile/uniquelementid&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
2. All textarea content will also need to have str_replace done on it to replace @@pluginfile@@/somefilenames.jpg in the content to use this path so that it comes up right in the editor.  (Note this also needs to be done on every format_text command too when showing this text.)&lt;br /&gt;
&lt;br /&gt;
3. The path parameter also needs to be added to the editor configuration in the current page.&lt;br /&gt;
&lt;br /&gt;
4. Editor plugins can be modified to look for these variables in the editor configuration.&lt;br /&gt;
&lt;br /&gt;
5. When adding an image or other media element,  the same AJAX repository picker will show up as a popup div to allow people to pick from any repository and choose files to download.  The repository picker is responsible for downloading the file in real-time, storing it as a user temporary file if the filearea doesn&#039;t already exist, prefixing the supplied path to the filename and returning a URL back to the dialog text input before closing.&lt;br /&gt;
&lt;br /&gt;
6. On submission, and after the HTML is stored, we might now have a new permanent filearea, so we&#039;ll need to update any associated temporary files to make sure they have the proper file area information.&lt;br /&gt;
&lt;br /&gt;
==Repository plugins==&lt;br /&gt;
&lt;br /&gt;
===Required elements===&lt;br /&gt;
&lt;br /&gt;
Each repository plugin is required to contain the following elements:&lt;br /&gt;
&lt;br /&gt;
====class repository()====&lt;br /&gt;
&lt;br /&gt;
This class implements the interface to a particular repository, for browsing, selecting and updating files.  The base class (repository) is defined in /repository/lib.php, while each repository defines an inherited class (eg repository_alfresco) in /repository/repositoryname/repository.class.php&lt;br /&gt;
&lt;br /&gt;
===Optional elements===&lt;br /&gt;
&lt;br /&gt;
Repositories can redefine any of these methods as required (and in some instances, MUST redefine them):&lt;br /&gt;
&lt;br /&gt;
====__construct($repositoryid, $contextid, $options=array(), $readonly)====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUST&#039;&#039;&#039; redefine&lt;br /&gt;
&lt;br /&gt;
Accept necessary parameters, and do initialization of repository.&lt;br /&gt;
&lt;br /&gt;
====get_file($url, $file = &#039;&#039;)====&lt;br /&gt;
&lt;br /&gt;
Given a URL, download a file from there, save the file in a temporary directory.&lt;br /&gt;
&lt;br /&gt;
====get_link($info)====&lt;br /&gt;
Get the url of external resource&lt;br /&gt;
&lt;br /&gt;
====get_listing($path=&#039;/&#039;, $page=&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;)====&lt;br /&gt;
&lt;br /&gt;
Given a path, and perhaps a search, get a listing of files. In the case of AJAX file picker, this function should return json format Javascript array.&lt;br /&gt;
&lt;br /&gt;
====search($keyword)====&lt;br /&gt;
Search repository by given keyword, it will return an array of the same format of get_listing&lt;br /&gt;
&lt;br /&gt;
====print_login()====&lt;br /&gt;
&lt;br /&gt;
Show the login screen, if required. In the case of AJAX file picker, this function should return json format array which defined the login form.&lt;br /&gt;
&lt;br /&gt;
====print_search==== &lt;br /&gt;
&lt;br /&gt;
Print the search form, it will return a json string&lt;br /&gt;
&lt;br /&gt;
====get_meta()====&lt;br /&gt;
Return information for creating ajax request, it is private function, you don&#039;t need to rewrite it.&lt;br /&gt;
&lt;br /&gt;
====create()====&lt;br /&gt;
Create an instance&lt;br /&gt;
&lt;br /&gt;
====delete()====&lt;br /&gt;
Delete this instance from `repository` table&lt;br /&gt;
&lt;br /&gt;
====hide()====&lt;br /&gt;
Hide a repository instance from file picker list&lt;br /&gt;
&lt;br /&gt;
====set_option()====&lt;br /&gt;
set options in data1-data5 fields, can be overrided&lt;br /&gt;
&lt;br /&gt;
====get_option()====&lt;br /&gt;
get option list or a specific option from database&lt;br /&gt;
&lt;br /&gt;
====get_type_option_names()====&lt;br /&gt;
If this plugin needs admin settings, please refine this function to return option names.&lt;br /&gt;
&lt;br /&gt;
====type_config_form()====&lt;br /&gt;
If get_type_option_names return non empty array, this function &#039;&#039;&#039;MUST&#039;&#039;&#039; redefine, it will help to build the setting form.&lt;br /&gt;
&lt;br /&gt;
====type_form_validation()====&lt;br /&gt;
This function can be used for validating the data submitted by plugin setting form.&lt;br /&gt;
&lt;br /&gt;
====get_instance_option_names()====&lt;br /&gt;
If plugin instance needs settings, this function will return instance option names.&lt;br /&gt;
&lt;br /&gt;
====instance_config_form()====&lt;br /&gt;
If get_instance_option_names return non empty array, this function &#039;&#039;&#039;MUST&#039;&#039;&#039; redefine, it will help to build the instance setting form.&lt;br /&gt;
&lt;br /&gt;
====instance_form_validation()====&lt;br /&gt;
This function can be used for validating the data submitted by instance setting form.&lt;br /&gt;
&lt;br /&gt;
====supported_filetypes()====&lt;br /&gt;
What file types are supported by this repository plugin, it will return an array, the file type name is defined in a [http://freemind.sourceforge.net/wiki/index.php/Main_Page freemind] file in lib/file/file_types.mm&lt;br /&gt;
&lt;br /&gt;
====supported_returntypes()====&lt;br /&gt;
The repository plugin could support external link or copying files to moodle. If the plugin support file link only, developer should override this function to return FILE_EXTERNAL, if plugin support copying file only, it should return FILE_INTERNAL, by default, plugin supports both.&lt;br /&gt;
&lt;br /&gt;
====filter()====&lt;br /&gt;
Filter file listing to exclude specific file types&lt;br /&gt;
&lt;br /&gt;
===icon.png===&lt;br /&gt;
&lt;br /&gt;
A logo that represents the repository.  Ideally square but we should handle all sizes.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Repository Administration Specification]]&lt;br /&gt;
* [[Repository Interface for Moodle/Course/User]]&lt;br /&gt;
* [[Repository plugins]]&lt;br /&gt;
* [[Repository File Picker]]&lt;br /&gt;
* [[File API]]&lt;br /&gt;
* [[Portfolio API]]&lt;br /&gt;
* MDL-13766 and MDL-16543 Repository API Meta issues&lt;br /&gt;
&lt;br /&gt;
[[Category:Repositories]]&lt;br /&gt;
&lt;br /&gt;
[[ja:開発:リポジトリAPI]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29389</id>
		<title>NanoGong/Converting to Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=NanoGong/Converting_to_Moodle_2.0&amp;diff=29389"/>
		<updated>2011-09-11T14:06:45Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Documentation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
This page is for collecting relevant information for converting NanoGong to Moodle 2.0. Any help is welcome!&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 10:35, 7 April 2011 (UTC)&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
== Java settings ==&lt;br /&gt;
(Not sure if this is relevant, but better to keep in mind ...)&lt;br /&gt;
* see [[Question type plugin how to#Configuration settings for your question type]]&lt;br /&gt;
&lt;br /&gt;
== Filter ==&lt;br /&gt;
General information on filters in Moodle 2.0 can be found at [[Filters 2.0]] and for developers at [[Filters 2.0]].&lt;br /&gt;
&lt;br /&gt;
=== Language folder ===&lt;br /&gt;
Add language folder: &#039;&#039;&#039;\lang\en\filter_nanogong.php&#039;&#039;&#039; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;filtername&#039;] = &#039;NanoGong audio&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Filter function ===&lt;br /&gt;
&lt;br /&gt;
The filter function is wrapped inside a class:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class filter_nanogong extends moodle_text_filter {&lt;br /&gt;
    function filter($text, array $options = array()){&lt;br /&gt;
    ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The callback function has to be &#039;&#039;&#039;outside&#039;&#039;&#039; this class definition!&lt;br /&gt;
&lt;br /&gt;
=== Preventing caching ===&lt;br /&gt;
&lt;br /&gt;
 $CFG-&amp;gt;currenttextiscacheable = false;&lt;br /&gt;
&lt;br /&gt;
is deprecated, outcommented&lt;br /&gt;
&lt;br /&gt;
=== File API ===&lt;br /&gt;
&lt;br /&gt;
==== Proof of concept ====&lt;br /&gt;
* Instead of the old &#039;&#039;&#039;file.php&#039;&#039;&#039; the new File API uses &#039;&#039;&#039;pluginfile.php&#039;&#039;&#039;.&lt;br /&gt;
* I uploaded a test file (sentence.wav) as a resource to find out the internal URL Moodle uses for serving this file.&lt;br /&gt;
* I then added this full URL as attribute to the NanoGong tag:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;http://localhost/moodle-MOODLE_20_WEEKLY/pluginfile.php/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* As the URL is already in its full format I just commented out the following lines in &#039;&#039;&#039;filter.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* This works as a proof of concept.&lt;br /&gt;
&lt;br /&gt;
==== Getting closer ... ====&lt;br /&gt;
&lt;br /&gt;
This modification does also work:&lt;br /&gt;
&lt;br /&gt;
; NanoGong tag&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;nanogong &lt;br /&gt;
    caption=&amp;quot;Testing NanoGong...&amp;quot; &lt;br /&gt;
    url=&amp;quot;/130/mod_resource/content/1/sentence.wav&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; filter.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($url != &amp;quot;&amp;quot;) {&lt;br /&gt;
    if ($CFG-&amp;gt;slasharguments)&lt;br /&gt;
        $url = &amp;quot;{$CFG-&amp;gt;wwwroot}/pluginfile.php$url&amp;quot;;&lt;br /&gt;
    else&lt;br /&gt;
        $url; # = &amp;quot;{$CFG-&amp;gt;wwwroot}/file.php?file=$url&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
* see [[File API]] and [[Using the File API]]&lt;br /&gt;
&lt;br /&gt;
* [[File_API#File_serving]]&lt;br /&gt;
* [[Using_the_file_API#Serving_files_to_users]]&lt;br /&gt;
* [[File_storage_conversion_Quiz_and_Questions#Serving_files]]&lt;br /&gt;
* [[Using the File API in Moodle forms]]&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
; Necessary updating for Moodle 1.9&lt;br /&gt;
* see [[User:Frank Ralf/NanoGong/1.9|subpage on Moodle 1.9]]&lt;br /&gt;
&lt;br /&gt;
; Migrating to Moodle 2.0&lt;br /&gt;
* [[Migrating contrib code to 2.0]]&lt;br /&gt;
* [[Migrating to 2.0 checklist]]&lt;br /&gt;
* [[Migrating contrib code to 2.0/Experience of converting a module to Moodle 2]]&lt;br /&gt;
&lt;br /&gt;
; Forums&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170422 Release of NanoGong 4.1, an important update for Moodle users]&lt;br /&gt;
&lt;br /&gt;
; Moodle plugin database &lt;br /&gt;
* [http://moodle.org/mod/data/view.php?d=13&amp;amp;mode=list&amp;amp;perpage=50&amp;amp;search=&amp;amp;sort=44&amp;amp;order=ASC&amp;amp;advanced=0&amp;amp;filter=1&amp;amp;advanced=1&amp;amp;f_44=&amp;amp;f_45=nanogong all NanoGong plugins]&lt;br /&gt;
&lt;br /&gt;
; NanoGong documentation&lt;br /&gt;
* http://gong.ust.hk/nanogong/moodle.html&lt;br /&gt;
* http://gong.ust.hk/nanogong/info_php.html&lt;br /&gt;
* [http://gong.ust.hk/moodle/course/view.php?id=2 Gong and NanoGong Demonstration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Moodle 2.0|NanoGong]]&lt;br /&gt;
[[Category:Project]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Setting_up_Netbeans&amp;diff=29351</id>
		<title>Setting up Netbeans</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Setting_up_Netbeans&amp;diff=29351"/>
		<updated>2011-09-06T14:52:49Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Optimization */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.netbeans.org/features/php/index.html NetBeans] has got a good PHP support. You find a host of information on the website (tutorials, developer blog, screen casts, etc.).&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
* CVS integration: see all changes, lines deletion, diff in real time, show annotations, diff history...&lt;br /&gt;
* Ctrl+Click: Go to declaration&lt;br /&gt;
* Export/Import Diff Patch&lt;br /&gt;
* Easy navigation&lt;br /&gt;
* List of functions&lt;br /&gt;
* Code completion&lt;br /&gt;
* Instant rename&lt;br /&gt;
* HTML, CSS, JavaScript support&lt;br /&gt;
* MySQL manager&lt;br /&gt;
* Quick Search&lt;br /&gt;
* Very few bugs&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
* Download the latest stable version from http://netbeans.org. Get the bundle that contains only PHP support.&lt;br /&gt;
* Install and run it.&lt;br /&gt;
&lt;br /&gt;
== Set up for Moodle development ==&lt;br /&gt;
&lt;br /&gt;
* Checkout your Moodle project with a &#039;&#039;&#039;CVS client&#039;&#039;&#039; - see [[:en:CVS for Administrators]] or [[CVS for developers|CVS for Developers]].&lt;br /&gt;
&lt;br /&gt;
* Open File &amp;gt; New Project &amp;gt; PHP &amp;gt; PHP Application &amp;gt; Next &lt;br /&gt;
: You&#039;re going to set the project now. &#039;&#039;Name&#039;&#039;, &#039;&#039;Location&#039;&#039; and &#039;&#039;Folder&#039;&#039; are used by NetBeans and are not related to the source code. So you can choose whatever you like, except your source folder. &#039;&#039;Sources&#039;&#039; has to be your checked out Moodle branch/head folder. The rest is clear enough. Don&#039;t forget to choose UTF-8 for &#039;&#039;Default Encoding&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
: Example:&lt;br /&gt;
&lt;br /&gt;
 Project Name:        Moodle 1.9 Stable&lt;br /&gt;
 Project Location:    C:\Users\jerome\Documents\NetBeansProjects&lt;br /&gt;
 Project Folder:      C:\Users\jerome\Documents\NetBeansProjects\Moodle 1.9 Stable&lt;br /&gt;
 Project Sources:     C:\Users\jerome\Projects\branch19_STABLE\moodle&lt;br /&gt;
 Project URL:         http://localhost/moodle19/&lt;br /&gt;
 Index File:          index.php&lt;br /&gt;
 Create:              unchecked&lt;br /&gt;
 Default Encoding:    UTF-8&lt;br /&gt;
 Set as Main Project: unchecked&lt;br /&gt;
&lt;br /&gt;
* Click on Finish.&lt;br /&gt;
&lt;br /&gt;
* Start coding!&lt;br /&gt;
&lt;br /&gt;
== CVS with NetBeans ==&lt;br /&gt;
&lt;br /&gt;
NetBeans comes with &#039;&#039;&#039;integrated CVS support&#039;&#039;&#039; which might be the easiest way to check out Moodle.&lt;br /&gt;
&lt;br /&gt;
=== Anonymous checkout ===&lt;br /&gt;
&lt;br /&gt;
# In NetBeans, select Window-&amp;gt;Versioning-&amp;gt;CVS-&amp;gt;Checkout&lt;br /&gt;
# Select Team-&amp;gt;CVS-&amp;gt;Checkout&lt;br /&gt;
# Enter into CVS Root: &#039;&#039;:pserver:anonymous@us.cvs.moodle.org:/cvsroot/moodle&#039;&#039;&lt;br /&gt;
: (Non-US-residents might use one of the other [https://docs.moodle.org/en/CVS_for_Administrators#CVS_Servers Moodle CVS servers] nearer to them.)&lt;br /&gt;
# Click &#039;&#039;Next&#039;&#039;&lt;br /&gt;
# Browse or enter into &#039;&#039;Module:&#039;&#039; moodle&lt;br /&gt;
# Browse or enter into &#039;&#039;Branch:&#039;&#039; MOODLE_19_STABLE&lt;br /&gt;
# Browse or enter into &#039;&#039;Local Folder:&#039;&#039; C:\xampp\htdocs&lt;br /&gt;
# Click &#039;&#039;Finish&#039;&#039; (and wait a few minutes for Moodle to be checked out)&lt;br /&gt;
# When you get the dialog box &amp;quot;Do you want to create an IDE project from the checked-out sources?&amp;quot;, Click &amp;quot;Create Project...&amp;quot;&lt;br /&gt;
# Select PHP Application with Existing Sources, and click Next&lt;br /&gt;
# Browse or enter into &#039;&#039;Sources Folder&#039;&#039; C:\xampp\htdocs\moodle&lt;br /&gt;
# Enter into &#039;&#039;Project Name:&#039;&#039; moodle&lt;br /&gt;
# Keep the other defaults and click next&lt;br /&gt;
# &#039;&#039;Run As:&#039;&#039; should have selected &#039;&#039;Local Web Site (running on local web server)&#039;&#039;&lt;br /&gt;
# Enter into Project URL http://localhost/moodle/&lt;br /&gt;
# Browse or enter into &#039;&#039;Index File:&#039;&#039; index.php&lt;br /&gt;
# Click &#039;&#039;Finish&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Checkout for developers with write access ===&lt;br /&gt;
&lt;br /&gt;
As far as I can tell, there is no way to get NetBeans to work with CVS keys on Mac/Linux via the system SSH binary, so you will need to use the internal SSH and your password (which you can choose to save).&lt;br /&gt;
&lt;br /&gt;
If you wish to use NetBeans CVS and have CVS write access, the procedure is as follows:&lt;br /&gt;
&lt;br /&gt;
====Checkout of main Moodle codebase====&lt;br /&gt;
&lt;br /&gt;
# Follow the above instructions except for point 3&lt;br /&gt;
# At part 3, substitute &#039;&#039;:ext:yourusername&#039;&#039; for the part before the @ and remove the country code from after it, leaving it like this  &#039;&#039;&#039;&#039;&#039;:ext:yourusername&#039;&#039;&#039;@cvs.moodle.org:/cvsroot/moodle&#039;&#039;&lt;br /&gt;
# Follow the rest of the instructions as above&lt;br /&gt;
&lt;br /&gt;
If you wish to work with a plugin, you will need to check this out separately and add it to the Moodle code project you have just made. This because for some reason, the &#039;Do you want to create a project&#039; option fails to show any of the files once you open it if you try to check out the plugin on its own (NetBeans 6.7.1 on a Mac).&lt;br /&gt;
&lt;br /&gt;
====Checkout of contrib code====&lt;br /&gt;
&lt;br /&gt;
# Follow the above steps up to point 3, and again substitute &#039;&#039;:ext:yourusername&#039;&#039; and click &#039;next&#039;. Note that you should not try to specify the full contrib repository path yet.&lt;br /&gt;
# Click &#039;Browse...&#039; next to the &#039;Module&#039; dialogue&lt;br /&gt;
# Find the plugin you wish to work with in CONTRIB&lt;br /&gt;
# Click &#039;OK&#039;&lt;br /&gt;
# Proceed as before up to point 6, then press &#039;Close&#039; when asked if you want to create a new project.&lt;br /&gt;
# Now navigate to the folder you earlier specified in the &#039;local folder&#039; dialogue and you will find a folder marked &#039;contrib&#039;.&lt;br /&gt;
# navigate to the plugin folder, copy that folder, and then navigate to your main Moodle project folder and paste it where it belongs.&lt;br /&gt;
&lt;br /&gt;
Note that the last three points may cause you some grief when attempting to commit changes. CVS doesn&#039;t seem to like having subfolders with different origins. A workaround that operates well for me is to check out the contrib code manually into a folder using the command line as specified in the CVS for developers instructions, then import that code as a new project with existing sources. This allows easy commits and updates, whilst keeping it separate. You can then make a symlink from you main Moodle project to your contrib directory, restart NetBeans (the CVS info and symlink stuff is only refreshed on restart) and then right click the linked directory and choose &#039;Ignore&#039;, then do the same and choose &#039;Exclude from commit&#039;. You can now use the code as if it were part of Moodle, still getting all the code completion stuff, and also do clean updates and commits from the secondary project. [[User:Matt Gibson|Matt Gibson]] 19:53, 6 June 2010 (UTC)&lt;br /&gt;
&lt;br /&gt;
===Adding a branch to your plugin===&lt;br /&gt;
Your plugin will likely start with just a HEAD tag and at some point, you will want to branch it so that you can have versions for different Moodles. To add a MOODLE_20_STABLE branch, for example, do the following.&lt;br /&gt;
&lt;br /&gt;
# Checkout as above, but use the MOODLE_20_STABLE branch instead of MOODLE_19_STABLE&lt;br /&gt;
# You will end up with an empty folder, which you copy into place as above.&lt;br /&gt;
# Find the folder in NetBeans, then right click and choose CVS-&amp;gt;Merge changes from branch...&lt;br /&gt;
# Choose to merge from whatever branch has all the current code, using the help link if not sure what to do.&lt;br /&gt;
&lt;br /&gt;
There is a small chance that the Merge link will not be there, in which case, you will have to copy the files into the directory by hand outside netbeans and then check them in. If you do this, make sure you don&#039;t copy over the &#039;CVS&#039; folders that will have been made when you checked out the MOODLE_19_STABLE code.&lt;br /&gt;
&lt;br /&gt;
=== A few warnings ===&lt;br /&gt;
&lt;br /&gt;
Some of these warnings could also apply to other IDEs:&lt;br /&gt;
&lt;br /&gt;
* If you want to delete a file from your computer but not from CVS, delete it from Windows Explorer/Nautilus/Finder. Otherwise your next commit could delete the file from CVS. &amp;lt;br/&amp;gt;In case you have already deleted a wrong file from NetBeans:  with Windows Explorer/Nautilus/Finder, delete all the folder content of this deleted file (including the CVS folder) and update the folder.&lt;br /&gt;
* If you rename files and that other people are working on them, NetBeans could end up to mess up your CVS folder (even though that is quite rare). Then NetBeans CVS will refuse to update your code displaying a no explicit error as &#039;&#039;&#039;Update Failed&#039;&#039;&#039;. In this case, delete the content of the damaged folder with Windows Explorer/Nautilus/Finder. Then update this folder.&lt;br /&gt;
* If you create a patch or a new PHP file with NetBeans on Microsoft Windows, please check that it&#039;s a Unix format file. You may want to use another software to create Unix format patch/php files.&lt;br /&gt;
&lt;br /&gt;
== Git with NetBeans ==&lt;br /&gt;
&lt;br /&gt;
=== NBGit | Git Support for NetBeans ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;NBGit is a module for the NetBeans IDE that adds support for working with the Git version control system. It uses the JGit library created as part of EGit to interact with Git repositories. Because the module is Java code all the way, it should work better cross-platform modulo platform specific differences, such as file system behavior. It is based on the NetBeans Mercurial module.&amp;quot; &lt;br /&gt;
(http://nbgit.org)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p class=&amp;quot;note orange&amp;quot;&amp;gt;&lt;br /&gt;
Unfortunately, the key word in that quote is &#039;&#039;&#039;should&#039;&#039;&#039;. In reality, because JGit is a rewrite from scratch of the tried-and-tested git core C code, it is still buggy, incomplete and unreliable. Therefore, the NetBeans and Eclipse git plugins are still only beta quality, and pretty sucky. Well, they mostly work for browsing the contents of the repository, but there is a small chance that if you use them for update operations, they will corrupt your repository. Hence, I am still doing git from the command-line.--[[User:Tim Hunt|Tim Hunt]] 11:11, 19 January 2010 (UTC)&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Optimization ==&lt;br /&gt;
&lt;br /&gt;
=== Sun JDK ===&lt;br /&gt;
1. You may want to run NetBeans with the Sun JDK. NetBeans seems to work a bit better with the [http://java.sun.com/javase/downloads/index.jsp Sun JDK]. You&#039;ll have to edit NetBeans config file. Open netbeans/etc/netbeans.conf. Then uncomment and edit: &lt;br /&gt;
&lt;br /&gt;
 netbeans_jdkhome=&amp;quot;your_JDK_path&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2. To change the NetBeans look and feel run netbeans in the command line with this parameter:&lt;br /&gt;
 &amp;quot;netbeans&amp;quot;  --laf javax.swing.plaf.metal.MetalLookAndFeel &lt;br /&gt;
&lt;br /&gt;
3. If NetBeans starts to slow down, give it more memory&lt;br /&gt;
 &amp;quot;netbeans&amp;quot; -J-Xmx600m&lt;br /&gt;
See FAQ for more [http://wiki.netbeans.org/FaqSettingHeapSize details on memory optimisation].&lt;br /&gt;
&lt;br /&gt;
=== ScanOnDemand ===&lt;br /&gt;
To prevent NetBeans from scanning the whole code with every start up you can use the following plug-in: http://wiki.netbeans.org/ScanOnDemand&lt;br /&gt;
&lt;br /&gt;
== Coding faster ==&lt;br /&gt;
&lt;br /&gt;
=== Keyboard shortcuts ===&lt;br /&gt;
(Note: Some shortcuts might not work for PHP development.)&lt;br /&gt;
* [http://www.phpmag.ru/2009/01/23/extremely-usefull-netbeans-shortcuts/ Extremely Useful NetBeans Shortcuts] &lt;br /&gt;
* [http://netbeanside61.blogspot.com/2008/04/top-10-netbeans-ide-keyboard-shortcuts.html Top 10 NetBeans IDE Keyboard Shortcuts I use the most]&lt;br /&gt;
* [http://wiki.netbeans.org/KeymapProfileFor60 NetBeans IDE 6.x Keyboard Shortcuts Specification]&lt;br /&gt;
&lt;br /&gt;
=== PHPUnit support ===&lt;br /&gt;
See [http://blogs.sun.com/netbeansphp/entry/recent_improvements_in_phpunit_support &amp;quot;Recent improvements in PHPUnit-support&amp;quot;] on how to use PHPUnit with NetBeans.&lt;br /&gt;
&lt;br /&gt;
===JIRA support===&lt;br /&gt;
You can install the optional JIRA module in order to be able to interact with the Moodle Tracker from within NetBeans. This has the advantage of avoiding constantly switching back and forth from the browser and works quite well. Go to &#039;&#039;Tools-&amp;gt;plugins-&amp;gt;available plugins&#039;&#039; and search for JIRA. Once installed, right click &#039;issue trackers&#039; in the &#039;services&#039; pane to make a new tracker instance, then enter your details.&lt;br /&gt;
&lt;br /&gt;
== See also: ==&lt;br /&gt;
Moodle forums &lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=112972 NetBeans 6.5 for moodle/PHP/debugging is a better experience v.s Eclipse]&lt;br /&gt;
&lt;br /&gt;
Online resources&lt;br /&gt;
* [http://www.netbeans.org/kb/trails/php.html NetBeans PHP Learning Trail]&lt;br /&gt;
* [http://wiki.netbeans.org/PHP NetBeans PHP Wiki]&lt;br /&gt;
* Sun&#039;s [http://blogs.sun.com/netbeansphp/ NetBeans PHP Team Blog]&lt;br /&gt;
&lt;br /&gt;
Book&lt;br /&gt;
* [http://www.packtpub.com/netbeans-platform-6-8-developers-guide/book NetBeans Platform 6.8 Developer&#039;s Guide] by Jürgen Petri (March 2010), focus on Java and Swing &lt;br /&gt;
&lt;br /&gt;
[[Category:Developer tools|NetBeans]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Required_code_upgrades&amp;diff=29317</id>
		<title>Required code upgrades</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Required_code_upgrades&amp;diff=29317"/>
		<updated>2011-08-30T10:30:09Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* See also: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page lists all changes that may be needed to be done in 3rd party modules and other integration code.&lt;br /&gt;
&lt;br /&gt;
=Moodle 2.0=&lt;br /&gt;
&lt;br /&gt;
Note to developrs: please keep adding more info here ;-)&lt;br /&gt;
&lt;br /&gt;
==Mandatory==&lt;br /&gt;
&lt;br /&gt;
===New Data Manipulation Layer (DML)===&lt;br /&gt;
* All database calls must be updated - new bound parameters syntax, magic quotes not used any more, see [https://docs.moodle.org/en/Development:DB_layer_2.0_migration_docs DB layer 2.0 migration].&lt;br /&gt;
&lt;br /&gt;
===File API===&lt;br /&gt;
It consists of [https://docs.moodle.org/en/Development:File_API three parts]:&lt;br /&gt;
# file storage - modules can not access the course files anymore, they must store alll files in own area&lt;br /&gt;
# file browsing - each module/plugin defines what files are browsable and acessible&lt;br /&gt;
# file serving - each plugin/module is responsible for file sending though pluginfile.php&lt;br /&gt;
&lt;br /&gt;
* Handling of files in backup/restore needs to be fully rewritten too.&lt;br /&gt;
* File uploading in formslib fully rewritten - old API should not be used&lt;br /&gt;
&lt;br /&gt;
===Upgrade code===&lt;br /&gt;
* Upgrade allowed only from 1.9.x (upgrade of contrib modules does not do version tests yet)&lt;br /&gt;
* Backup/restore must use new DDL API.&lt;br /&gt;
* Concurrent upgrades are now prevented.&lt;br /&gt;
&lt;br /&gt;
=== Messaging reimplemented ===&lt;br /&gt;
Mailing and notifications from modules needs to be updated (not finished yet)&lt;br /&gt;
&lt;br /&gt;
==Optional==&lt;br /&gt;
&lt;br /&gt;
* [https://docs.moodle.org/en/Development:Portfolio_API Portfolio integration]&lt;br /&gt;
* [https://docs.moodle.org/en/Development:Conditional_activities Conditional activities]&lt;br /&gt;
&lt;br /&gt;
==Other changes==&lt;br /&gt;
These changes might affect some unsupported core code customisations. Modules and other plugins should not be affected.&lt;br /&gt;
&lt;br /&gt;
* rewritten course category sorting&lt;br /&gt;
&lt;br /&gt;
== See also: ==&lt;br /&gt;
* [[Migrating contrib code to 2.0]]&lt;br /&gt;
* [[User:Frank Ralf/Experience of converting a module to Moodle 2]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Required_code_upgrades&amp;diff=29315</id>
		<title>Required code upgrades</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Required_code_upgrades&amp;diff=29315"/>
		<updated>2011-08-30T10:27:03Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* See also: */  section added&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page lists all changes that may be needed to be done in 3rd party modules and other integration code.&lt;br /&gt;
&lt;br /&gt;
=Moodle 2.0=&lt;br /&gt;
&lt;br /&gt;
Note to developrs: please keep adding more info here ;-)&lt;br /&gt;
&lt;br /&gt;
==Mandatory==&lt;br /&gt;
&lt;br /&gt;
===New Data Manipulation Layer (DML)===&lt;br /&gt;
* All database calls must be updated - new bound parameters syntax, magic quotes not used any more, see [https://docs.moodle.org/en/Development:DB_layer_2.0_migration_docs DB layer 2.0 migration].&lt;br /&gt;
&lt;br /&gt;
===File API===&lt;br /&gt;
It consists of [https://docs.moodle.org/en/Development:File_API three parts]:&lt;br /&gt;
# file storage - modules can not access the course files anymore, they must store alll files in own area&lt;br /&gt;
# file browsing - each module/plugin defines what files are browsable and acessible&lt;br /&gt;
# file serving - each plugin/module is responsible for file sending though pluginfile.php&lt;br /&gt;
&lt;br /&gt;
* Handling of files in backup/restore needs to be fully rewritten too.&lt;br /&gt;
* File uploading in formslib fully rewritten - old API should not be used&lt;br /&gt;
&lt;br /&gt;
===Upgrade code===&lt;br /&gt;
* Upgrade allowed only from 1.9.x (upgrade of contrib modules does not do version tests yet)&lt;br /&gt;
* Backup/restore must use new DDL API.&lt;br /&gt;
* Concurrent upgrades are now prevented.&lt;br /&gt;
&lt;br /&gt;
=== Messaging reimplemented ===&lt;br /&gt;
Mailing and notifications from modules needs to be updated (not finished yet)&lt;br /&gt;
&lt;br /&gt;
==Optional==&lt;br /&gt;
&lt;br /&gt;
* [https://docs.moodle.org/en/Development:Portfolio_API Portfolio integration]&lt;br /&gt;
* [https://docs.moodle.org/en/Development:Conditional_activities Conditional activities]&lt;br /&gt;
&lt;br /&gt;
==Other changes==&lt;br /&gt;
These changes might affect some unsupported core code customisations. Modules and other plugins should not be affected.&lt;br /&gt;
&lt;br /&gt;
* rewritten course category sorting&lt;br /&gt;
&lt;br /&gt;
== See also: ==&lt;br /&gt;
[TODO]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Required_code_upgrades&amp;diff=29314</id>
		<title>Required code upgrades</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Required_code_upgrades&amp;diff=29314"/>
		<updated>2011-08-30T10:26:25Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Mandatory */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page lists all changes that may be needed to be done in 3rd party modules and other integration code.&lt;br /&gt;
&lt;br /&gt;
=Moodle 2.0=&lt;br /&gt;
&lt;br /&gt;
Note to developrs: please keep adding more info here ;-)&lt;br /&gt;
&lt;br /&gt;
==Mandatory==&lt;br /&gt;
&lt;br /&gt;
===New Data Manipulation Layer (DML)===&lt;br /&gt;
* All database calls must be updated - new bound parameters syntax, magic quotes not used any more, see [https://docs.moodle.org/en/Development:DB_layer_2.0_migration_docs DB layer 2.0 migration].&lt;br /&gt;
&lt;br /&gt;
===File API===&lt;br /&gt;
It consists of [https://docs.moodle.org/en/Development:File_API three parts]:&lt;br /&gt;
# file storage - modules can not access the course files anymore, they must store alll files in own area&lt;br /&gt;
# file browsing - each module/plugin defines what files are browsable and acessible&lt;br /&gt;
# file serving - each plugin/module is responsible for file sending though pluginfile.php&lt;br /&gt;
&lt;br /&gt;
* Handling of files in backup/restore needs to be fully rewritten too.&lt;br /&gt;
* File uploading in formslib fully rewritten - old API should not be used&lt;br /&gt;
&lt;br /&gt;
===Upgrade code===&lt;br /&gt;
* Upgrade allowed only from 1.9.x (upgrade of contrib modules does not do version tests yet)&lt;br /&gt;
* Backup/restore must use new DDL API.&lt;br /&gt;
* Concurrent upgrades are now prevented.&lt;br /&gt;
&lt;br /&gt;
=== Messaging reimplemented ===&lt;br /&gt;
Mailing and notifications from modules needs to be updated (not finished yet)&lt;br /&gt;
&lt;br /&gt;
==Optional==&lt;br /&gt;
&lt;br /&gt;
* [https://docs.moodle.org/en/Development:Portfolio_API Portfolio integration]&lt;br /&gt;
* [https://docs.moodle.org/en/Development:Conditional_activities Conditional activities]&lt;br /&gt;
&lt;br /&gt;
==Other changes==&lt;br /&gt;
These changes might affect some unsupported core code customisations. Modules and other plugins should not be affected.&lt;br /&gt;
&lt;br /&gt;
* rewritten course category sorting&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Required_code_upgrades&amp;diff=29313</id>
		<title>Required code upgrades</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Required_code_upgrades&amp;diff=29313"/>
		<updated>2011-08-30T10:25:24Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Optional */ better link format&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page lists all changes that may be needed to be done in 3rd party modules and other integration code.&lt;br /&gt;
&lt;br /&gt;
=Moodle 2.0=&lt;br /&gt;
&lt;br /&gt;
Note to developrs: please keep adding more info here ;-)&lt;br /&gt;
&lt;br /&gt;
==Mandatory==&lt;br /&gt;
&lt;br /&gt;
===New Data Manipulation Layer (DML)===&lt;br /&gt;
* All database calls must be updated - new bound parameters syntax, magic quotes not used any more [https://docs.moodle.org/en/Development:DB_layer_2.0_migration_docs]&lt;br /&gt;
&lt;br /&gt;
===File API===&lt;br /&gt;
It consists of three parts [https://docs.moodle.org/en/Development:File_API]:&lt;br /&gt;
# file storage - modules can not access the course files anymore, they must store alll files in own area&lt;br /&gt;
# file browsing - each module/plugin defines what files are browsable and acessible&lt;br /&gt;
# file serving - each plugin/module is responsible for file sending though pluginfile.php&lt;br /&gt;
&lt;br /&gt;
* Handling of files in backup/restore needs to be fully rewritten too.&lt;br /&gt;
* File uploading in formslib fully rewritten - old API should not be used&lt;br /&gt;
&lt;br /&gt;
===Upgrade code===&lt;br /&gt;
* Upgrade allowed only from 1.9.x (upgrade of contrib modules does not do version tests yet)&lt;br /&gt;
* Backup/restore must use new DDL API.&lt;br /&gt;
* Concurrent upgrades are now prevented.&lt;br /&gt;
&lt;br /&gt;
=== Messaging reimplemented ===&lt;br /&gt;
Mailing and notifications from modules needs to be updated (not finished yet)&lt;br /&gt;
&lt;br /&gt;
==Optional==&lt;br /&gt;
&lt;br /&gt;
* [https://docs.moodle.org/en/Development:Portfolio_API Portfolio integration]&lt;br /&gt;
* [https://docs.moodle.org/en/Development:Conditional_activities Conditional activities]&lt;br /&gt;
&lt;br /&gt;
==Other changes==&lt;br /&gt;
These changes might affect some unsupported core code customisations. Modules and other plugins should not be affected.&lt;br /&gt;
&lt;br /&gt;
* rewritten course category sorting&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Template:Back_from_dev&amp;diff=29185</id>
		<title>Template:Back from dev</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Template:Back_from_dev&amp;diff=29185"/>
		<updated>2011-08-15T17:00:37Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
* This template redirects pages from the Developer Docs back to Moodle 2.0 Docs.&lt;br /&gt;
* Especially useful for accidentally cloned user pages, see [[User:Frank_Ralf/NanoGong]] for an example.&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 00:55, 16 August 2011 (WST)&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
{{Note|&lt;br /&gt;
Please see the original page at https://docs.moodle.org/20/en/{{FULLPAGENAMEE}} - thanks!&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf/NanoGong&amp;diff=29184</id>
		<title>User:Frank Ralf/NanoGong</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf/NanoGong&amp;diff=29184"/>
		<updated>2011-08-15T16:57:20Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Back from dev}}&lt;br /&gt;
&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 00:57, 16 August 2011 (WST)&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Template:Back_from_dev&amp;diff=29183</id>
		<title>Template:Back from dev</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Template:Back_from_dev&amp;diff=29183"/>
		<updated>2011-08-15T16:56:50Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
* This template redirects pages from the Developer Docs back to Moodle 2.0 Docs.&lt;br /&gt;
* Especially useful for accidentally cloned user pages.&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 00:55, 16 August 2011 (WST)&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
{{Note|&lt;br /&gt;
Please see the original page at https://docs.moodle.org/20/en/{{FULLPAGENAMEE}} - thanks!&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Template:Back_from_dev&amp;diff=29182</id>
		<title>Template:Back from dev</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Template:Back_from_dev&amp;diff=29182"/>
		<updated>2011-08-15T16:55:32Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: Created page with &amp;quot;&amp;lt;noinclude&amp;gt; * This template redirects pages from the Developer Docs back to Moodle 2.0 Docs. * Especially useful for accidentally cloned user pages. --~~~~ &amp;lt;/noinclude&amp;gt; {{Note| P...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
* This template redirects pages from the Developer Docs back to Moodle 2.0 Docs.&lt;br /&gt;
* Especially useful for accidentally cloned user pages.&lt;br /&gt;
--[[User:Frank Ralf|Frank Ralf]] 00:55, 16 August 2011 (WST)&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
{{Note|&lt;br /&gt;
Please see the original page at https://docs.moodle.org/20/en/{{FULLPAGENAME}} - thanks!&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf/NanoGong&amp;diff=29181</id>
		<title>User:Frank Ralf/NanoGong</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf/NanoGong&amp;diff=29181"/>
		<updated>2011-08-15T16:52:09Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See https://docs.moodle.org/20/en/User:Frank_Ralf/NanoGong --[[User:Frank Ralf|Frank Ralf]] 00:51, 16 August 2011 (WST)&lt;br /&gt;
&lt;br /&gt;
{{FULLPAGENAME}}&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf/NanoGong&amp;diff=29180</id>
		<title>User:Frank Ralf/NanoGong</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf/NanoGong&amp;diff=29180"/>
		<updated>2011-08-15T16:51:47Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: Replaced content with &amp;quot;See https://docs.moodle.org/20/en/User:Frank_Ralf/NanoGong --~~~~&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See https://docs.moodle.org/20/en/User:Frank_Ralf/NanoGong --[[User:Frank Ralf|Frank Ralf]] 00:51, 16 August 2011 (WST)&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf/NanoGong/1.9&amp;diff=29179</id>
		<title>User:Frank Ralf/NanoGong/1.9</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf/NanoGong/1.9&amp;diff=29179"/>
		<updated>2011-08-15T16:50:13Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: Replaced content with &amp;quot;See https://docs.moodle.org/20/en/User:Frank_Ralf/NanoGong/1.9&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See https://docs.moodle.org/20/en/User:Frank_Ralf/NanoGong/1.9&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf&amp;diff=29178</id>
		<title>User:Frank Ralf</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf&amp;diff=29178"/>
		<updated>2011-08-15T11:41:32Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: deleted everything and redirect to other user page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Please see my user profile at https://docs.moodle.org/20/en/User:Frank_Ralf --[[User:Frank Ralf|Frank Ralf]] 19:41, 15 August 2011 (WST)&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf/NanoGong/1.9&amp;diff=29130</id>
		<title>User:Frank Ralf/NanoGong/1.9</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Frank_Ralf/NanoGong/1.9&amp;diff=29130"/>
		<updated>2011-08-13T20:11:50Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* See also: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;NanoGong for Moodle 1.9 also needs some updating.&lt;br /&gt;
&lt;br /&gt;
== build_navigation() ==&lt;br /&gt;
Navigation needs to be updated to use build_navigation()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 print_header() was sent a string as 3rd (-&amp;gt; NanoGongs -&amp;gt; NanoGong) parameter. &lt;br /&gt;
 This is deprecated in favour of an array built by build_navigation(). Please upgrade your code.&lt;br /&gt;
 &lt;br /&gt;
 * line 2539 of lib\weblib.php: call to debugging()&lt;br /&gt;
 * line 61 of mod\nanogong\view.php: call to print_header()&lt;br /&gt;
&lt;br /&gt;
 * line 3759 of lib\weblib.php: call to debugging()&lt;br /&gt;
 * line 37 of theme\standard\header.html: call to print_navigation()&lt;br /&gt;
 * line 2765 of lib\weblib.php: call to include()&lt;br /&gt;
 * line 61 of mod\nanogong\view.php: call to print_header()&lt;br /&gt;
&lt;br /&gt;
See [[lib/weblib.php#function_build_navigation]]&lt;br /&gt;
&lt;br /&gt;
=== Moodle 2.0 ===&lt;br /&gt;
* [[Navigation 2.0]]&lt;br /&gt;
* [[Navigation 2.0 implementation plan]]&lt;br /&gt;
&lt;br /&gt;
== $cm ==&lt;br /&gt;
 The field $cm-&amp;gt;modname should be set if you call build_navigation with a $cm parameter. &lt;br /&gt;
 If you get $cm  using &#039;&#039;get_coursemodule_from_instance&#039;&#039; or &#039;&#039;&#039;get_coursemodule_from_id&#039;&#039;&#039;, &lt;br /&gt;
 this will be done automatically.&lt;br /&gt;
 &lt;br /&gt;
 * line 3894 of lib\weblib.php: call to debugging()&lt;br /&gt;
 * line 59 of mod\nanogong\view.php: call to build_navigation()&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;No sound has been submitted for the message.&amp;quot; ==&lt;br /&gt;
See &lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=92516 Release of NanoGong 2 with Full Moodle Integration]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=135714 won&#039;t start recording]&lt;br /&gt;
&lt;br /&gt;
== Github repository ==&lt;br /&gt;
The current code is available at https://github.com/nakohdo/moodle-mod_nanogong&lt;br /&gt;
&lt;br /&gt;
== See also: ==&lt;br /&gt;
* [http://gitorious.org/~doctorlard/moodle/doctorlards-moodle/commit/eac1256499273f638ee89dd14f2f1d0d5d65e433 Patch for adding NanoGong recording applet and HTMLArea button] by [http://moodle.org/user/profile.php?id=155540 Jonathan Harker] &lt;br /&gt;
&lt;br /&gt;
[[Category:Moodle 1.9|NanoGong]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Languages:Tim%27s_crazy_proposal_based_on_maketext&amp;diff=28688</id>
		<title>Languages:Tim&#039;s crazy proposal based on maketext</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Languages:Tim%27s_crazy_proposal_based_on_maketext&amp;diff=28688"/>
		<updated>2011-07-20T16:34:50Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;They say that the border between madness and genius is is very narrow. Here goes.&lt;br /&gt;
&lt;br /&gt;
The best article I know about the problems of localising software is http://search.cpan.org/~ferreira/Locale-Maketext-1.13_82/lib/Locale/Maketext/TPJ13.pod. I particularly like the narrative in the first half. Also, I must warn you that I have not read much about localisation, so my endorsement may not mean much.&lt;br /&gt;
: The article mentioned is also available from the authors&#039; homepage: [http://interglacial.com/tpj/13/ &amp;quot;Localizing Your Perl Programs&amp;quot;] --[[User:Frank Ralf|Frank Ralf]] 00:34, 21 July 2011 (WST)&lt;br /&gt;
&lt;br /&gt;
OK, so the key point it makes is that really, a language string like &amp;quot;There have been $a quiz attempts&amp;quot; is really a function (in the mathematical sense of a mapping, not necessarily as a programming language construct). Depending on $a, we want it to output&lt;br /&gt;
* There have been no quiz attempts&lt;br /&gt;
* There has been one quiz attempt&lt;br /&gt;
* There have been 42 quiz attempts&lt;br /&gt;
&lt;br /&gt;
So the question is, why not make it a function in the programming language construct sense as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Two representations==&lt;br /&gt;
&lt;br /&gt;
Up to Moodle 1.9, the files Moodle used at runtime were exactly the same as the files that translators edited. That was convenient, but limited us to a human-readable and editable format. Also, it meant that Moodle had to do a lot of searching at runtime.&lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 we are already proposing to split the representations, which lets us optimise the runtime format to be pretty much whatever we like, without making the format edited by translators impossible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Proposed runtime lang file syntax==&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;tt&amp;gt;moodledata/lang/&amp;lt;/tt&amp;gt; there are subfolders like &amp;lt;tt&amp;gt;en/&amp;lt;/tt&amp;gt; (note we lose the legacy &amp;lt;tt&amp;gt;_utf8&amp;lt;/tt&amp;gt;) that contains files like &amp;lt;tt&amp;gt;mod_quiz.php&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;core_moodle.php&amp;lt;/tt&amp;gt;, that is, using the new component naming convention.&lt;br /&gt;
&lt;br /&gt;
Suppose we have a hypothetical plugin &amp;lt;tt&amp;gt;admin/report/dylan&amp;lt;/tt&amp;gt; with its current lang file &amp;lt;tt&amp;gt;admin/report/dylan/lang/en_utf8/report_dylan.php&amp;lt;/tt&amp;gt; that contains:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$string[&#039;howmanyroads&#039;] = &#039;How many roads must a man walk down?&#039;;&lt;br /&gt;
$string[&#039;roadsx&#039;] = &#039;Roads: $a&#039;;&lt;br /&gt;
$string[&#039;xroadsfromytowns&#039;] = &#039;$a-&amp;gt;numroads roads from at least $a-&amp;gt;numcities different cities.&#039;;&lt;br /&gt;
// Can&#039;t really handle pluralisation in that last one in Moodle :-(&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In my new proposal, &amp;lt;tt&amp;gt;moodledata/lang/en/report_dylan.php&amp;lt;/tt&amp;gt; will contain:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class strings_en_report_capability extends strings_base {&lt;br /&gt;
    protected static $helper = lang_helper_en::get(); // Get singleton instance.&lt;br /&gt;
    public function howmanyroads($a) { return &#039;How many roads must a man walk down?&#039;; }&lt;br /&gt;
    public function roadsx($a) { return self::$helper-&amp;gt;quant($a, &#039;road&#039;); }&lt;br /&gt;
    public function xroadsfromytowns($a) { &lt;br /&gt;
        return self::$helper-&amp;gt;quant($a-&amp;gt;numroads, &#039;road&#039;) . &#039; from at least &#039; .&lt;br /&gt;
                self::$helper-&amp;gt;quant($a-&amp;gt; numcities, &#039;different city&#039;, &#039;different cities&#039;) . &#039;.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
(I manually line-wrapped that last example to make it readable. in reality, remember that this file is being automatically compiled from some more human-readiable source.)&lt;br /&gt;
&lt;br /&gt;
Also note that &amp;lt;tt&amp;gt;moodledata/lang/fr/report_dylan.php&amp;lt;/tt&amp;gt; will look like:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include_once($CFG-&amp;gt;langdir . &#039;/en/report_dylan.php&#039;);&lt;br /&gt;
class strings_fr_report_capability extends strings_en_report_capability {&lt;br /&gt;
    protected static $helper = lang_helper_fr::get(); // Get singleton instance.&lt;br /&gt;
    public function howmanyroads() { return &#039;Combien de rue ...&#039;; }&lt;br /&gt;
    // etc.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And &amp;lt;tt&amp;gt;moodledata/lang/fr_ca/report_dylan.php&amp;lt;/tt&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include_once($CFG-&amp;gt;langdir . &#039;/fr/report_dylan.php&#039;);&lt;br /&gt;
class strings_fr_ca_report_capability extends strings_en_report_capability {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using that runtime format==&lt;br /&gt;
&lt;br /&gt;
Then, &amp;lt;tt&amp;gt;string_manager()&amp;lt;/tt&amp;gt; becomes:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class string_manager {&lt;br /&gt;
    protected $stringclasses = array();&lt;br /&gt;
    public function get_string($identifier, $component = &#039;&#039;, $a = null) {&lt;br /&gt;
        $component = $this-&amp;gt;fix_legacy_component_names($component);&lt;br /&gt;
        $this-&amp;gt;get_string_class(current_language(), $component)-&amp;gt;$identifier($a);&lt;br /&gt;
    }&lt;br /&gt;
    protected function get_string_class($lang, $component) {&lt;br /&gt;
        global $CFG;&lt;br /&gt;
        if (!isset($this-&amp;gt;stringclasses[$lang][$component])) {&lt;br /&gt;
            $this-&amp;gt;stringclasses[$lang][$component] = &lt;br /&gt;
                    $this-&amp;gt;load_string_class($lang, $component)();&lt;br /&gt;
        }&lt;br /&gt;
        return $this-&amp;gt;stringclasses[$lang][$component];&lt;br /&gt;
    }&lt;br /&gt;
    protected function load_string_class($lang, $component) {&lt;br /&gt;
        $file = &amp;quot;$CFG-&amp;gt;langdir/$lang/$component.php&amp;quot;;&lt;br /&gt;
        $class = &amp;quot;strings_{$lang}_{$component}&amp;quot;;&lt;br /&gt;
        if ($CFG-&amp;gt;langediting &amp;amp;&amp;amp; !$this-&amp;gt;is_up_to_date($file)) {&lt;br /&gt;
            compile_lang_strings($lang, $component);&lt;br /&gt;
        }&lt;br /&gt;
        if (!is_readable($file)) {&lt;br /&gt;
            return new strings_base(); // See below.&lt;br /&gt;
        }&lt;br /&gt;
        include_once($file);&lt;br /&gt;
        if (!class_exists()) {&lt;br /&gt;
            throw new coding_exception($file . &#039; did not define the &#039; . $class .&lt;br /&gt;
                    &#039;class. There must be a bug in compile_lang_strings.&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        return new $class();&lt;br /&gt;
    }&lt;br /&gt;
    // A few other methods omitted.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note that if you have a developer/translator flag on (&amp;lt;tt&amp;gt;$CFG-&amp;gt;langediting&amp;lt;/tt&amp;gt;) then &amp;lt;tt&amp;gt; is_up_to_date&amp;lt;/tt&amp;gt; checks various file timestamps, so that lang files can automatically be recompiled as needed for those people, without hurting runtime performance for production sites. &lt;br /&gt;
&lt;br /&gt;
As a final bit of magic, we have&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class strings_base {&lt;br /&gt;
    public function __call($name, $arguments) {&lt;br /&gt;
        return &amp;quot;[[$name]]&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember that the strings_en_report_capability inherited form this. This gives us our classing missing string fallback. Also:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class lang_helper_en {&lt;br /&gt;
    private static $inst = null;&lt;br /&gt;
    public static get() {&lt;br /&gt;
        if (!$inst) {&lt;br /&gt;
            $inst = new lang_helper_en();&lt;br /&gt;
        }&lt;br /&gt;
        return $inst;&lt;br /&gt;
    }&lt;br /&gt;
    public function quant($number, $singular, $plural = &#039;&#039;) {&lt;br /&gt;
        if ($number = 1) {&lt;br /&gt;
            return &amp;quot;$number $singular&amp;quot;;&lt;br /&gt;
        } else if ($plural) {&lt;br /&gt;
            return &amp;quot;$number $plural&amp;quot;;&lt;br /&gt;
        } else {&lt;br /&gt;
            return &amp;quot;$number {$singular}s&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    // Other helper functions.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Problems==&lt;br /&gt;
&lt;br /&gt;
I think this gives us the fastest possible runtime performance (particularly when combined with a PHP accelerator. Of course, it leaves open the following problems:&lt;br /&gt;
* what string format do translators to edit? (I suggest we copy the maketext format.)&lt;br /&gt;
* can we write the &amp;lt;tt&amp;gt;compile_lang_strings&amp;lt;/tt&amp;gt; function?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Languages]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Language]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Talk:Themes_FAQ&amp;diff=26554</id>
		<title>Talk:Themes FAQ</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Talk:Themes_FAQ&amp;diff=26554"/>
		<updated>2011-07-01T16:22:01Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Only for developers? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Only for developers? ==&lt;br /&gt;
&lt;br /&gt;
I&#039;m not happy to see this FAQ categorized as developer documentation. They contain much information on issues regularly asked in the forum. [[Firebug]] on the other hand, which is really for developers, is still &amp;quot;normal&amp;quot; documentation. --[[User:Frank Ralf|Frank Ralf]] 00:20, 2 July 2011 (WST)&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Talk:Themes_FAQ&amp;diff=26553</id>
		<title>Talk:Themes FAQ</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Talk:Themes_FAQ&amp;diff=26553"/>
		<updated>2011-07-01T16:20:24Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: Only for developers?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Only for developers? ==&lt;br /&gt;
&lt;br /&gt;
I&#039;m not happy to see this FAQ categorized as developer documentation. They contain much information on issues regularly asked in the forum. --[[User:Frank Ralf|Frank Ralf]] 00:20, 2 July 2011 (WST)&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Plugin_contribution&amp;diff=26539</id>
		<title>Plugin contribution</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Plugin_contribution&amp;diff=26539"/>
		<updated>2011-06-30T13:31:22Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* How to request that your code be tested/reviewed */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the various ways to share and work collaboratively on contributed code. In the past, Moodle CONTRIB code was often stored on the Moodle CVS server. As Moodle code transitions to [https://docs.moodle.org/en/Git Git], CONTRIB code will also be transitioning with a goal of having all code maintained via git repositories by August 2011. Don&#039;t worry if you have code on CVS and do not know Git as there are many people willing to help you with the transition especially [https://docs.moodle.org/en/User:Anthony_Borrow Anthony Borrow]. &lt;br /&gt;
&lt;br /&gt;
For those neither familiar with CVS nor Git, it is recommended that you [https://docs.moodle.org/en/Git#Books_and_tutorials learn Git basics]. The nice thing about using Git is that it will leave control of the code in the hands of contributor. &lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Sharing code&#039;&#039;&#039; - Contributed code will be shared and maintained using a public Git repository (i.e. github.com, gitorious.org, etc.).&lt;br /&gt;
*&#039;&#039;&#039;Reviewing code&#039;&#039;&#039; - If you would like your code to be tested and reviewed, please create an issue in the [http://tracker.moodle.org/ Moodle Tracker] under [http://tracker.moodle.org/browse/CONTRIB CONTRIB]. &lt;br /&gt;
*&#039;&#039;&#039;Documenting code&#039;&#039;&#039; - You can maintain helpful documentation of the features and installation instructions in [https://docs.moodle.org Moodle Docs]&lt;br /&gt;
*&#039;&#039;&#039;Distributing code&#039;&#039;&#039; - You can help others find your code by adding an entry to the [http://moodle.org/mod/data/view.php?id=6009 Modules and Plugins database] &lt;br /&gt;
*&#039;&#039;&#039;Discussing the code&#039;&#039;&#039; - You can discuss the functionality and answer questions about how to use your code in the [http://moodle.org/course/view.php?id=5 Using Moodle forums]. Users can ask questions, discuss possible ideas for improvement, etc. Once the discussion matures to a point where you may want to take action, then you can create an issue in the [http://tracker.moodle.org/ Moodle Tracker]. &lt;br /&gt;
*&#039;&#039;&#039;Maintaining code&#039;&#039;&#039; - You can further develop your code by fixing issues, adding features, and responding to other issues with the [http://tracker.moodle.org/ Moodle Tracker]. Each plugin or patch can have its own component in the CONTRIB project; however, you will need to request that the component be created in the CONTRIB project. Once created, users can easily create issues related to your contributed code. The primary maintainer of the code will be assigned as the component lead and have their tracker privileges bumped so that they can manage issues related to their contributed code. Those issues will automatically be assigned to the component lead/primary maintainer. &lt;br /&gt;
&lt;br /&gt;
==The CONTRIB Frequently Asked Question (FAQ)==&lt;br /&gt;
&#039;&#039;&#039;Question:&#039;&#039;&#039; I have written a new block, activity, patch or theme to share with the Moodle community. What is the process for contributing the code? &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Answer:&#039;&#039;&#039; First, thank you for your generosity and desire to share your work with the rest of the Moodle community. Code contributions are highly valued and Moodle wants to encourage creativity and generosity in keeping with [https://docs.moodle.org/en/Pedagogy Moodle&#039;s social constructionist pedagogy]. &lt;br /&gt;
&lt;br /&gt;
As mentioned, there are various tools to help you share and support your contribution. By using a consistent methodology, your Moodle related code will be more easily found, tested, used, maintained, documented, and evaluated by fellow Moodlers and developers. Learning to use the various tools common among Moodle developers will help you to efficiently and effectively develop, share and maintain your contributed code. &lt;br /&gt;
&lt;br /&gt;
==How to submit code==&lt;br /&gt;
You have created some code that you would like to contribute to the Moodle community.  The first step is to choose which Git code hosting site you want to use. Both [http://github.com/ github.com] and [http://gitorious.org/ gitorious.org] are popular. Follow the instructions for creating a public repository for your code. This will allow others to download your code as well as suggest possible patches. As you become more familiar with collaborating with others you can also control who else you want to be able to push changes into your repository (i.e. write access). &lt;br /&gt;
&lt;br /&gt;
In order to facilitate a common naming convention of Moodle related repositories, it is suggested that you use the following format: moodle-{plugintype}_{pluginname}. For example, the birthday block has a repository name of moodle-block_birthday and is located at https://github.com/arborrow/moodle-block_birthday. Other developers can fork the code and work from their repositories.&lt;br /&gt;
&lt;br /&gt;
==How to request that your code be tested/reviewed==&lt;br /&gt;
&lt;br /&gt;
#If you do not already have an account on the [http://tracker.moodle.org Moodle Tracker], create one and login. &lt;br /&gt;
#Create a new &amp;quot;Task&amp;quot; issue in the &amp;quot;Non-core contributed modules&amp;quot; project. Your issue will be a request that the code be tested and reviewed. &lt;br /&gt;
#Provide the link to the repository (i.e. https://github.com/arborrow/moodle-block_birthday) &lt;br /&gt;
#Provide a clear description of what the code does and the functionality that it adds to Moodle. &lt;br /&gt;
#Provide any other links to supporting documentation&lt;br /&gt;
&lt;br /&gt;
The Contrib Coordinator will then work directly with the code contributor via the Tracker to help evaluate the code, work on resolving any questions/issues found, etc. &lt;br /&gt;
&lt;br /&gt;
*Contributors are encouraged to follow Moodle&#039;s coding guidelines [[Coding]].&lt;br /&gt;
&lt;br /&gt;
*Contributors are encouraged to maintain a branch for each major Moodle release (i.e. 1.8, 1.9, etc.). The HEAD branch should be used as the development branch. &lt;br /&gt;
&lt;br /&gt;
*Contributors are encouraged to associate each change made to an issue in the tracker. This practice is done by Moodle core developers and it is a good habit to get into so that you can go back and see why various changes were made to the code. Simple practices like these help to create good documentation of the code as it develops and matures. Commits should begin with the Moodle tracker issue number followed by a brief description of the change.&lt;br /&gt;
&lt;br /&gt;
==How to provide documentation==&lt;br /&gt;
Having great code available to the community is wonderful. It is also important to educate users about how to use the code with documentation in [[Main Page|Moodle Docs]]. See [[MoodleDocs:Guidelines for contributors|guidelines for contributors]] for more help. &lt;br /&gt;
&lt;br /&gt;
A documentation page for a contributed module should have some basic elements.  A brief introduction of what the code does, a &amp;quot;Features&amp;quot; heading, perhaps a &amp;quot;Installation&amp;quot;, &amp;quot;Tips and tricks&amp;quot; and &amp;quot;See also&amp;quot; headings, all with content of course.  Sometimes a screenshot is worth 1000 words. In the &amp;quot;See also&amp;quot; put a link to the Modules and Plug database, with a note that this is the place to download versions, and the forum where questions can be answered at moodle.org.&lt;br /&gt;
&lt;br /&gt;
Other information might include: Languages Supported, Known Issues, Supported Versions (for Moodle 1.8, 1.9), and maybe which are being actively supported. The page should be added to the contributed code category by typing &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;[[Category:Contributed code]]&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; at the bottom of the page.&lt;br /&gt;
&lt;br /&gt;
===Possible format for a contributed code Moodle Docs page===&lt;br /&gt;
You can copy and paste the below into your Moodle documentation page.  Please add the URL links. &#039;&#039;&#039;This is only a suggestion&#039;&#039;&#039;. &lt;br /&gt;
 Start with a 2-4 line introduction. The Plug In Name works with the activity module. &lt;br /&gt;
 It makes the life of the teacher easier by ... &lt;br /&gt;
 ==Features==&lt;br /&gt;
 * Add features list or overview description here &amp;lt;nowiki&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; [[Image:Sample_screen_shot|thumb|500px|center|Title of screenshot]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 ==Installation==&lt;br /&gt;
 Place install instructions here&lt;br /&gt;
 ==Tips and tricks==&lt;br /&gt;
 *Optional section, usually added by users later&lt;br /&gt;
 ==See also==&lt;br /&gt;
 *[Place http_address_for_M&amp;amp;P_entry_here  Name of Entry here] is a Modules and plugins database page that has download links and more information.&lt;br /&gt;
 *Discussions: [Place http_address_for_moodle_forum_or_thread_here Name of forum]&lt;br /&gt;
 &lt;br /&gt;
When no thread or forum exists for discussion, put a link to the Contributed Code forum.&lt;br /&gt;
 *Discussions: Please create or find a discussion topic in the &amp;lt;nowiki&amp;gt;[http://moodle.org/mod/forum/view.php?id=44  Contributed Code forum]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Share code with modules and plugins database==&lt;br /&gt;
Now you have a place for users to get your code, how will they know it exists? Moodle users can easily find information about contributed code in the [http://moodle.org/mod/data/view.php?id=6009 Modules and Plugins database]. When you add an entry to this database, remember that it is searchable by any field. Every field is not required, but providing as much information as possible will help people who would like to find, read about, and then install your contribution.  In particular, the name of the module and the description of its function are important.  Screen shots are helpful (but please, not larger than 350px wide!). Links to discussion (on Moodle.org or other forum) and documentation (link to your page in the [https://docs.moodle.org Moodle docs] or another location) should be included if possible.  &lt;br /&gt;
&lt;br /&gt;
Users may leave comments on your entry, so please check it periodically for questions and bug reports.  If you include contact information or a link to a discussion, your users may be able to contact you more directly.  The comments are not emailed automatically.&lt;br /&gt;
&lt;br /&gt;
Keep in mind that entries added to the [http://moodle.org/mod/data/view.php?id=6009 Modules and Plugins database] require approval by a moderator before they will be visible to other Moodle users.&lt;br /&gt;
&lt;br /&gt;
==Support code and get feedback with forums==&lt;br /&gt;
As users become familiar with the contributed code, discussions about the code are likely to emerge. We strongly urge you to place links to at least one forum at moodle.org in your Modules and Plugins entry as well as in MoodleDocs. &lt;br /&gt;
&lt;br /&gt;
It is important to respond to users who have questions about how to use the code, suggestions for how to make it better, etc. If it looks like there is sufficient need for some contributed code to have its own forum, please create a request via the tracker in the MDL-SITE section. Moodle has included many contributed code projects and ideas into its core. &lt;br /&gt;
&lt;br /&gt;
*[http://moodle.org/mod/forum/view.php?id=44 Contributed code forum] is a generic forum.  Maybe create a thread &amp;quot;New Mousetrap module&amp;quot;.&lt;br /&gt;
*[http://moodle.org/course/view.php?id=5 Using Moodle forums] The English speaking list of forums on many different subjects. &lt;br /&gt;
*[http://moodle.org/course/ A list of] other language forums and special areas in Moodle&lt;br /&gt;
&lt;br /&gt;
==Maintain and refine code with Tracker==&lt;br /&gt;
&lt;br /&gt;
In order to facilitate keeping track of feature requests, bugs, and other code issues, the contributor may request that a component be created in the CONTRIB section of the Moodle tracker. (This is the section where you initially made the request to have code included in CVS.moodle.org.)&lt;br /&gt;
&lt;br /&gt;
The contributor can be added to the group of CONTRIB developers in the tracker to manage the issues assigned to them and better coordinate with other developers. Users of the contributed code can then add issues which can be assigned directly to the maintainer. The tracker helps to manage the work flow involved in fixing bugs, working through feature requests, and maintaining the code. When committing changes, maintainers are strongly encouraged to begin the commit comment with a tracker number. &lt;br /&gt;
&lt;br /&gt;
We strongly encourage users to involve themselves in the process of creating a useful issue in tracker.  For the developer and code contributor,  describing the fix can help clarify the need for the code change. Further, it helps to establish good documentation about how the code developed. Others will be able to identify the issues addressed and understand why a particular decision was made.&lt;br /&gt;
&lt;br /&gt;
==Summary==&lt;br /&gt;
&lt;br /&gt;
It is hoped that following this procedure, will help guide contributors of code in the process of learning the tools and skills used by the Moodle developers. Learning to submit code by using the tracker, work the code with CVS, support the code by providing documentation, share code in Modules and Plugins, maintain the code by using the tracker, and evolve the code by using the Moodle.org forums will assist you in successfully contributing to the Moodle community and working with Moodle&#039;s developers. &lt;br /&gt;
&lt;br /&gt;
Throughout this process, the CONTRIB Coordinator is here to encourage and support those contributing code to the Moodle community and fostering the development of tomorrow&#039;s Moodle developers.&lt;br /&gt;
&lt;br /&gt;
Eventually, the community may wish for the code to become part of the Moodle core. By following the steps above, the developers will be able to evaluate the merit of the contributed code, understand how users have used the code, see the issues that have emerged and thus have more information to make an informed decision about whether or not to incorporate the contributed code into core.&lt;br /&gt;
&lt;br /&gt;
==Most common recommendations for contributed code==&lt;br /&gt;
&lt;br /&gt;
There are several small issues that the CONTRIB Coordinator will typically check for when reviewing the code prior to committing to the CVS server. These issues help to ensure consistency and quality of code. Any suggestions made by the CONTRIB Coordinator should be considered recommendations aimed to help folks new to the Moodle community collaborate in a way consistent with standard practices within the Moodle community. The suggestions are meant to help avoid potential issues and facilitate the acceptance of your code.&lt;br /&gt;
&lt;br /&gt;
=== Does the file structure conform to Moodle standards? ===&lt;br /&gt;
Generally speaking, each file should have an appropriate extension (i.e. php, html, css, txt, etc.). To avoid issues with case sensitivity, folders and files are normally lower case; however, there are exceptions to this. Another common recommendation is to place the lang folder within the block or module rather than asking the user to copy files into the main lang folder. Changes made in the get_string function for Moodle 1.9 and onward make it possible to do this which goes a long way in keeping things modular. Similarly, to respect the modular nature of Moodle if a block requires a library it is usually preferred to keep it as a subdirectory in the block&#039;s folder. This can be especially helpful if your code is dependent upon a particular version of an external library.&lt;br /&gt;
&lt;br /&gt;
=== Does the code work without any obvious errors? ===&lt;br /&gt;
The CONTRIB Coordinator will try to install the block or module on a fresh Moodle installation and report back any errors. This testing is done with Debugging set to show All PHP notices and errors (not developer mode). The most common notices have to do with attempting to use a variable that has not been initialized or checked for existence. &lt;br /&gt;
&lt;br /&gt;
=== Does the code use the config_plugins table? ===&lt;br /&gt;
&lt;br /&gt;
Contributed blocks and modules are encouraged to make use of the &#039;&#039;&#039;config_plugins&#039;&#039;&#039; table rather than the config table. Maintainers are encouraged to read the documentation provided for the [http://xref.moodle.org/lib/moodlelib.php.html#get_config get_config()] and [http://xref.moodle.org/lib/moodlelib.php.html#set_config set_config()]] functions in the /lib/moodlelib.php file to help ensure that the footprint for the global $CFG variable does not become bloated.&lt;br /&gt;
&lt;br /&gt;
=== Does the code follow the [[Coding|Coding Guidelines]]? ===&lt;br /&gt;
The CONTRIB Coordinator will look at the code for general readability and point out any obvious deviations from the [[Coding|Coding Guidelines]]. While not all code needs to conform with each and every guideline, maintainers are encouraged to follow those guidelines as closely as possible. One useful tool for Moodle 2.0 is the [http://moodle.org/mod/data/view.php?d=13&amp;amp;rid=4682|Code checker local plugin] which helps to identify many issues.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
*[[Migrating contrib code to 2.0]]&lt;br /&gt;
*[[contrib]] CONTRIB is an area in the Moodle CVS &lt;br /&gt;
*[[Translation]] for information on the translation of contributed code&lt;br /&gt;
*[[Overview]]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/discuss.php?d=99037 Best practices for code modification?] forum discussion&lt;br /&gt;
&lt;br /&gt;
[[Category:Guidelines for contributors]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=CSS&amp;diff=26533</id>
		<title>CSS</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=CSS&amp;diff=26533"/>
		<updated>2011-06-29T15:25:41Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* See Also */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;CSS or Cascading Style Sheets are used to control the way web pages look.  By changing a CSS definition, the change is made on every Moodle webpage that uses that definition.    &lt;br /&gt;
&lt;br /&gt;
CSS files are located in the [[Theme|theme]] folder being used by Moodle. The normal Moodle practice is to have 3 main CSS files: [[CSS styles_ color.css|styles_color]], [[CSS styles_layout.css|styles_layout]], [[CSS styles_fonts.css|styles_fonts]].  When a CSS definition is not found in a theme CSS file, the CSS files located in the standard theme serves as the default.  &lt;br /&gt;
&lt;br /&gt;
There may also be CSS files for Internet Explorer, Mozilla or other internet browsers.&lt;br /&gt;
&lt;br /&gt;
==Basic Moodle page parts==&lt;br /&gt;
A web page is broken up into pieces or elements.  Not every page contains the same parts.&lt;br /&gt;
&lt;br /&gt;
These parts or elements include: core, forms, header, footer,admin, blocks, blog, calendar, course, doc, grades, login, message, notes, mymoodle, question, tabs, tags, user and many of the modules.&lt;br /&gt;
&lt;br /&gt;
==Basic CSS files==&lt;br /&gt;
*[[CSS styles_ layout.css]] contains the layout specifications for various page elements.  &lt;br /&gt;
&lt;br /&gt;
*[[CSS styles_color.css]] contains the colors used in the page elements. &lt;br /&gt;
&lt;br /&gt;
*[[CSS styles_fonts.css]] defines the fonts used in the page elements.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[CSS FAQ]]&lt;br /&gt;
* [[Themes FAQ]]&lt;br /&gt;
* [[CSS styles moz.css]]&lt;br /&gt;
* [[Themes]]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Cascading_Style_Sheets Wikipedia Cascading Style Sheets]&lt;br /&gt;
* [http://www.w3schools.com/css/default.asp W3schools]&lt;br /&gt;
&lt;br /&gt;
[[Category:Themes]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Web_developer_extension&amp;diff=26521</id>
		<title>Web developer extension</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Web_developer_extension&amp;diff=26521"/>
		<updated>2011-06-24T12:05:03Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* See also */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The &#039;&#039;&#039;Web developer extension&#039;&#039;&#039; for [[Firefox]] is an essential tool for web developers. It has too many functions to list them all but some of the most useful for use with Moodle development are explained below. Visit the [http://chrispederick.com/work/webdeveloper/documentation/ web developer extension&#039;s homepage] to download and install and for more documentation. Or get it from the [https://addons.mozilla.org/en-US/firefox/addon/60 Firefox Add-on page].&lt;br /&gt;
&lt;br /&gt;
== For CSS development ==&lt;br /&gt;
&lt;br /&gt;
These functions are useful for anyone creating and debugging [[CSS]]:&lt;br /&gt;
&lt;br /&gt;
* Disable Cache - this prevents Firefox from storing old versions of CSS files as you make changes&lt;br /&gt;
* Edit CSS - this allows you to make changes to the CSS and see the changes instantly. Great for exploring and testing.&lt;br /&gt;
* Display Element Information - show information about page elements, such as ancestors as you hover over them with the mouse.&lt;br /&gt;
* Display Id &amp;amp; Class details - displays all class and id names where-ever they are used.&lt;br /&gt;
* Enable only print style in normal view - useful for developing [[Print style|print styles]].&lt;br /&gt;
&lt;br /&gt;
== For HTML development ==&lt;br /&gt;
&lt;br /&gt;
These functions are useful when creating HTML code:&lt;br /&gt;
&lt;br /&gt;
* Outline Tables/Headings/Deprecated elements - the outline function puts colored boxes around the elements you specify to make them easier to locate and work with.&lt;br /&gt;
* Validate local HTML - sends the HTML you are looking at to the [[W3C_validation| Validator]]. Most Moodle pages are protected by passwords so validators cannot visit them, which is the usual practice.&lt;br /&gt;
&lt;br /&gt;
== For accessibility ==&lt;br /&gt;
&lt;br /&gt;
See the [[Accessibility]] page for more tools.&lt;br /&gt;
&lt;br /&gt;
* Replace images with alt attributes - lets you read what those with images unavailable or turned off will receive.&lt;br /&gt;
* Linearize page - shows the order in which audio or small screen browsers will present information&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Firebug]] for information about Firebug, another powerful Firefox extension for web developers&lt;br /&gt;
* [http://www.microsoft.com/downloadS/details.aspx?familyid=E59C3964-672D-4511-BB3E-2D5E1DB91038&amp;amp;displaylang=en Internet Explorer Developer Toolbar] for a similar tool for Microsoft Internet Explorer&lt;br /&gt;
* [http://www.sitepoint.com/blogs/2010/03/23/chrome-web-developer-toolbar/ &amp;quot;The Web Developer Toolbar Comes to Chrome&amp;quot;] &lt;br /&gt;
* [https://addons.mozilla.org/en-US/firefox/addon/toggle-web-developer-toolbar/ Toggle Web Developer Toolbar] Firefox add-on&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer tools]]&lt;br /&gt;
[[Category:Themes]]&lt;br /&gt;
[[Category:Firefox extensions]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Web_developer_toolbar&amp;diff=26520</id>
		<title>Web developer toolbar</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Web_developer_toolbar&amp;diff=26520"/>
		<updated>2011-06-24T12:04:21Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: Redirected page to dev:Web developer extension&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Dev:Web_developer_extension]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Maintaining_Moodle_customisations_with_Git&amp;diff=26384</id>
		<title>Maintaining Moodle customisations with Git</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Maintaining_Moodle_customisations_with_Git&amp;diff=26384"/>
		<updated>2011-06-21T10:42:02Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* See also: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{draft}}&lt;br /&gt;
&lt;br /&gt;
Git makes it very easy to maintain local customisations to Moodle alongside updates from the official Moodle repository.&lt;br /&gt;
This page assumes you installed Moodle following [[Installing Moodle from Git repository]]. It also assumes you understand basic version control principles (repositories, branches, conflicts) and have a basic working knowledge of Git.&lt;br /&gt;
&lt;br /&gt;
First, from your moodle directory, create a branch of &amp;lt;tt&amp;gt;master&amp;lt;/tt&amp;gt; which will contain your customised Moodle. This is the branch you&#039;ll publish to other servers.&lt;br /&gt;
 git checkout master&lt;br /&gt;
 git checkout -b mycustommoodle&lt;br /&gt;
&lt;br /&gt;
 Switched to a new branch &#039;mycustommoodle&#039;&lt;br /&gt;
Next, create a new branch for developing your customisation, and move to that branch:&lt;br /&gt;
 git checkout -b myfeature&lt;br /&gt;
&lt;br /&gt;
 Switched to a new branch &#039;myfeature&#039;&lt;br /&gt;
This will give you a seperate copy of the Moodle code to work on without affecting the master branch. It&#039;s not recommended that you do this on your production server, as it will cause the code to be available immediately as it&#039;s saved.&lt;br /&gt;
&lt;br /&gt;
Do your customisations, add any new files to git, and commit the changes&lt;br /&gt;
 git add .&lt;br /&gt;
 git status&lt;br /&gt;
 git commit -m &amp;quot;Description of the change&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Switch back to the &amp;lt;tt&amp;gt;mycustommoodle&amp;lt;/tt&amp;gt; branch and merge the changes&lt;br /&gt;
 git checkout mycustommoodle&lt;br /&gt;
 git merge myfeature&lt;br /&gt;
Any future development can take place on the myfeature branch, and be merged into mycustommoodle in this way&lt;br /&gt;
&lt;br /&gt;
Even after local modifications have been made, you can pull in updates from the official git repository&lt;br /&gt;
 git checkout master&lt;br /&gt;
 git fetch&lt;br /&gt;
 git merge origin/master&lt;br /&gt;
 git checkout mycustommoodle&lt;br /&gt;
 git merge master&lt;br /&gt;
&lt;br /&gt;
=== Conflicts ===&lt;br /&gt;
If you create a local modification to a file, and that same file is modified in the official repository, you may find that a conflict is created when you do &amp;lt;tt&amp;gt;git merge origin/master&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To resolve this, you will need to find the conflicts (the attempt to merge will tell you where they are) and resolve them.  The [http://www.kernel.org/pub/software/scm/git/docs/git-merge.html|&amp;lt;tt&amp;gt;git merge&amp;lt;/tt&amp;gt; man page] has more information on the presentation and resolution of conflicts.&lt;br /&gt;
&lt;br /&gt;
After the conflict is resolved, you&#039;ll need to commit the resolved files.&lt;br /&gt;
 git commit -a&lt;br /&gt;
Once a conflict has been resolved, even if you keep your local modification over the upstream modification, there wont be a conflict next time you merge.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
; Moodle forum discussions&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=168094 GIT help needed]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=165236 Best way to manage CONTRIB code with GIT]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=167063 Handy Git tip for tracking 3rd-party modules and plugins]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=167730 Moodle Git repositories]&lt;br /&gt;
&lt;br /&gt;
; External resources &lt;br /&gt;
* [http://www.kernel.org/pub/software/scm/git/docs/everyday.html Everyday GIT With 20 Commands Or So]&lt;br /&gt;
* [http://gitref.org/ Git Reference]&lt;br /&gt;
* [http://progit.org/book/ Pro Git book]&lt;br /&gt;
&lt;br /&gt;
[[Category:Git]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Git_repositories_for_contrib_modules&amp;diff=26383</id>
		<title>Git repositories for contrib modules</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Git_repositories_for_contrib_modules&amp;diff=26383"/>
		<updated>2011-06-21T10:41:44Z</updated>

		<summary type="html">&lt;p&gt;Nakohdo: /* Moving a plugin to it&amp;#039;s own repository */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{draft}}&lt;br /&gt;
&lt;br /&gt;
The instructions here assume that you have a working knowledge of git.&lt;br /&gt;
&lt;br /&gt;
Managing contrib plugin with git makes it easy to make local modifications and pull in updates.&lt;br /&gt;
There are several options for managing a contrib plugin with git:&lt;br /&gt;
# Add the module to your main moodle git repository&lt;br /&gt;
# Create a self-contained repository for the plugin in a subdirectory&lt;br /&gt;
# Create a git submodule, which creates a repository for the plugin but tracks updates in the main repository too. It also allows you to clone the plugins with the main repository&lt;br /&gt;
Having used all of the above, the method I&#039;d recommend is number 2, as it&#039;s most convenient. The main disadvantage of 1 is that you can&#039;t easily pull in updates, and the main disadvantage of 3 is that you have to make each commit to the module twice (once in the submodule, and once in the main repo).&lt;br /&gt;
&lt;br /&gt;
== Creating a new plugin ==&lt;br /&gt;
If you intend to publish the plugin, I highly recommend creating a [http://github.org github] repository from the start. This will allow you to publish with a single command when you&#039;re ready.  Just go to the site, sign up for an account, create a new repository. The naming convention for Moodle plugin repositories is &amp;lt;tt&amp;gt;moodle-plugintype_pluginname&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Github will give you instructions for cloning your (blank) repository. If you&#039;re using option 2 above, navigate to the directory for the plugin type you&#039;re developing (e.g. /block, /mod, /local) and run the &amp;lt;tt&amp;gt;git clone&amp;lt;/tt&amp;gt; command there. This will create a subdirectory called &amp;lt;tt&amp;gt;moodle-plugintype_pluginname&amp;lt;/tt&amp;gt;, rename this to &amp;lt;tt&amp;gt;pluginname&amp;lt;/tt&amp;gt; and you&#039;re good to go. &amp;lt;tt&amp;gt;cd&amp;lt;/tt&amp;gt; into this directory to perform git commands on this plugin&#039;s repository - outside of this directory will perform them on the main Moodle repository.&lt;br /&gt;
&lt;br /&gt;
You can then develop and test your changes locally, and when you&#039;re ready to publish, run&lt;br /&gt;
 git push origin&lt;br /&gt;
&lt;br /&gt;
== Installing a third-party plugin ==&lt;br /&gt;
Installing a plugin developed by a third party differs depending on whether they have used git or not.&lt;br /&gt;
If they have used git, you can follow the instructions above, but skip out the part where you create a new github repository and clone their own github (or other public repository) instead.&lt;br /&gt;
&lt;br /&gt;
If they haven&#039;t used git, you can still use git (and even github, if you like) to track local modifications to the plugin. Simply create a directory for the plugin&lt;br /&gt;
 mkdir ~/moodle/blocks/newblock&lt;br /&gt;
Initialise a new git repository there&lt;br /&gt;
 cd ~/moodle/blocks/newblock&lt;br /&gt;
 git init&lt;br /&gt;
unzip or copy the files to the repository&lt;br /&gt;
 cd ~&lt;br /&gt;
 tar -xf newblock.tar.gz&lt;br /&gt;
 cp newblock/* ~/moodle/blocks/newblock&lt;br /&gt;
add and commit the files&lt;br /&gt;
 cd ~/moodle/blocks/newblock&lt;br /&gt;
 git add .&lt;br /&gt;
 git commit . -m &amp;quot;Added files for newblock&amp;quot;&lt;br /&gt;
When installing updates, you&#039;ll need to copy the new files in place of the old one, and re run the &amp;lt;tt&amp;gt;git add&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;git commit&amp;lt;/tt&amp;gt; commands.&lt;br /&gt;
&lt;br /&gt;
== Moving a plugin to it&#039;s own repository ==&lt;br /&gt;
You may have developed a plugin within your main Moodle repository, but want to move it to a separate one to make it easier to publish.  Using &amp;lt;tt&amp;gt;git filter-branch&amp;lt;/tt&amp;gt;, we can achieve this and maintain all existing history for the plugin&#039;s files.&lt;br /&gt;
&lt;br /&gt;
First, you&#039;ll need a fresh copy of moodle (this will become your new moodle repository)&lt;br /&gt;
 mkdir ~/newmoodle&lt;br /&gt;
 cd ~/newmoodle&lt;br /&gt;
 git clone git://git.moodle.org/moodle.git&lt;br /&gt;
This will create a clone of Moodle without your plugins in ~/newmoodle/moodle&lt;br /&gt;
Next, you need to clone your local Moodle repository (why will become clear).&lt;br /&gt;
 mkdir ~/moodleclone&lt;br /&gt;
 cd ~/moodleclone&lt;br /&gt;
 git clone ~/moodle&lt;br /&gt;
This will create a clone of your current moodle development repository, with your plugins, in ~/moodleclone/moodle&lt;br /&gt;
Now, we&#039;ll use the &amp;lt;tt&amp;gt;git filter-branch&amp;lt;/tt&amp;gt; command to reduce this clone to just the plugin. &lt;br /&gt;
 cd ~/moodleclone/moodle&lt;br /&gt;
 git filter-branch --subdirectory-filter blocks/myblock/&lt;br /&gt;
The files from blocks/myblock/ will now be at the root of the repository. All other files will have been removed (this is why we&#039;re using a clone).&lt;br /&gt;
You can now track the plugin in it&#039;s own repository by cloning this reduced repository to a subdirectory of the new moodle clone.&lt;br /&gt;
 cd ~/newmoodle/blocks&lt;br /&gt;
 git clone ~/moodleclone/moodle myblock&lt;br /&gt;
This will clone the block&#039;s files, with the full history, into their own repository in the myblock subdirectory.&lt;br /&gt;
You can now delete ~/moodleclone/moodle, and repeat with any other plugins you&#039;ve developed.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
; Moodle forum discussions&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=168094 GIT help needed]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=165236 Best way to manage CONTRIB code with GIT]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=167063 Handy Git tip for tracking 3rd-party modules and plugins]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=167730 Moodle Git repositories]&lt;br /&gt;
&lt;br /&gt;
; External resources &lt;br /&gt;
* [http://www.kernel.org/pub/software/scm/git/docs/everyday.html Everyday GIT With 20 Commands Or So]&lt;br /&gt;
* [http://gitref.org/ Git Reference]&lt;br /&gt;
* [http://progit.org/book/ Pro Git book]&lt;br /&gt;
&lt;br /&gt;
[[Category:Git]]&lt;/div&gt;</summary>
		<author><name>Nakohdo</name></author>
	</entry>
</feed>