<?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=Nicolasconnault</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=Nicolasconnault"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/Special:Contributions/Nicolasconnault"/>
	<updated>2026-08-04T12:39:14Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Files_usability_2.3&amp;diff=36103</id>
		<title>Files usability 2.3</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Files_usability_2.3&amp;diff=36103"/>
		<updated>2012-11-07T02:34:59Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Background */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Files Usability 2.3&lt;br /&gt;
|state = Specifications, feedback sought&lt;br /&gt;
|tracker = MDL-31907&lt;br /&gt;
|discussion = &lt;br /&gt;
|assignee = Moodle HQ DEV team&lt;br /&gt;
}}&lt;br /&gt;
{{Moodle 2.3}}&lt;br /&gt;
&lt;br /&gt;
[[Image:comicstrip-miscommunication.png|600px|The team was also asked to get rid of bugs in stables.]]&lt;br /&gt;
&lt;br /&gt;
==Background==&lt;br /&gt;
&lt;br /&gt;
The file handling of Moodle 1.x was a very simple system with basically one server directory of files per course and very basic access controls.  This system had the benefits of being simple, which meant it was easy to understand and to perform hacks around, but its naivety had a lot of disadvantages in security, consistency, disk use, activity portability, and in some cases even led to dataloss.   It also encouraged a certain workflow of &amp;quot;dumping&amp;quot; huge amounts of content into the course in an unstructured way.&lt;br /&gt;
&lt;br /&gt;
The Files system was re-designed in Moodle 2.0 to solve these problems by introducing a model where files are directly associated with texts and &amp;quot;file areas&amp;quot; within different plugins in Moodle, and access to them is finely controlled by those same plugins.   For some examples, the assignment module is now able to control access to assignment submissions depending on due dates and so on.   Backups now accurately contain all the files they need, and multiple copies of the same file take up no more disk space than a single copy.  Files can be drawn as easily from external repositories as from your own computer.  The files system in Moodle 2.0 is more capable and detailed than pretty much any other system around.&lt;br /&gt;
&lt;br /&gt;
However, the interfaces to CONTROL all this had to increase significantly in complexity, and this, combined with limited development time and technical constraints in web browsers have led to a large drop in usability.   Not only do Moodle users have to contend with a different mindset when adding their resources to Moodle, but they also had to contend with a long list of unfamiliar interface issues.&lt;br /&gt;
&lt;br /&gt;
This project aims to make large improvements in the interface when dealing with files in Moodle and improve usability significantly in Moodle 2.3.&lt;br /&gt;
&lt;br /&gt;
==Critical problems==&lt;br /&gt;
&lt;br /&gt;
This short list of problems has been identified from user feedback via Moodle partners, tracker issues with votes, user surveys, usability studies and common sense.  It&#039;s not exhaustive, but covers the most severe issues that we think need to be fixed.   If you&#039;d like to comment about this list, please use the discussion page for this project or comment directly on the linked tracker issues.&lt;br /&gt;
&lt;br /&gt;
===Problem A: Uploading files take a lot of clicks ===&lt;br /&gt;
&lt;br /&gt;
The most common case for most people is to upload files from their own desktop (not a repository).   Since the file picker must be used for this, this can take a lot of clicks, and only one file can be uploaded at a time (unless you know how to use zip).&lt;br /&gt;
&lt;br /&gt;
====Solution A1: Allow drag+drop files from desktop.====&lt;br /&gt;
&lt;br /&gt;
We can add the ability to drag files from the desktop in multiple places.  See other issue below about licensing.&lt;br /&gt;
&lt;br /&gt;
# File manager form elements - this is already implemented in 2.3&lt;br /&gt;
# Into file picker (private files)&lt;br /&gt;
# Into file picker (upload)&lt;br /&gt;
# Into course sections (auto-creation of file resources)&lt;br /&gt;
# Into HTML editor window?   (could add to filearea and insert link/image into text?)&lt;br /&gt;
&lt;br /&gt;
====Solution A2: Add &amp;quot;quick upload&amp;quot; buttons that bring up dialog as quickly as possible====&lt;br /&gt;
&lt;br /&gt;
We can add small buttons that take the user directly to a browse-and-upload button as fast as possible, in the following places:&lt;br /&gt;
&lt;br /&gt;
# File manager form element&lt;br /&gt;
# HTML editor image dialog&lt;br /&gt;
# HTML editor media dialog&lt;br /&gt;
&lt;br /&gt;
eg see MDL-27236&lt;br /&gt;
&lt;br /&gt;
====Solution A3: Allow multiple files to be uploaded at the same time====&lt;br /&gt;
&lt;br /&gt;
We can add the ability to browse/select multiple files from the desktop via one form, along with a way to update license information for all of them before saving.&lt;br /&gt;
&lt;br /&gt;
This requires some sort of parameter from the calling form to be passed to the file picker, indicating how many new files are expected/allowed.&lt;br /&gt;
&lt;br /&gt;
====Solution A4: More stickiness in file picker between invocations====&lt;br /&gt;
&lt;br /&gt;
The file picker should remember more of your settings from the last time you used it. &lt;br /&gt;
&lt;br /&gt;
# Last Icon/List view &lt;br /&gt;
# Last server files location&lt;br /&gt;
&lt;br /&gt;
====Solution A5: Add infrastructure for default licensing, and license editing====&lt;br /&gt;
&lt;br /&gt;
We need some infrastructure for licensing to set default licenses at site/course/user level, because there will be no opportunity to select that at upload time during a drag and drop.   File manager form elements should have an &amp;quot;Info&amp;quot; item (in the context menu for each item) that gives a dialog to edit name/license.&lt;br /&gt;
&lt;br /&gt;
====Solution A6: Where possible, link directly to the editing form for file areas====&lt;br /&gt;
&lt;br /&gt;
In areas like &amp;quot;Private files&amp;quot;, &amp;quot;Assignment submissions&amp;quot;, and &amp;quot;Restore backup&amp;quot; we can probably link users directly to the editing page of the filearea, rather than showing them the browse version first.&lt;br /&gt;
&lt;br /&gt;
===Problem B: Private files management===&lt;br /&gt;
&lt;br /&gt;
The &#039;Private files&amp;quot; area is the main place that all users have to store files privately in Moodle.  It is like a mini-repository that people can use to store files before sharing them in courses and other public places.&lt;br /&gt;
&lt;br /&gt;
====Solution B1: Prevent leaving the Private Files page until changes are saved====&lt;br /&gt;
&lt;br /&gt;
A big problem for people currently is that they can leave the filemanager page before saving their changes.  A simple fix is to detect this situation just like Gmail and our Tracker do, and throw up a dialog to confirm they want to do this.  The &amp;quot;Save&amp;quot; button can also be more a lot more obvious (should we also offer auto-save?)&lt;br /&gt;
&lt;br /&gt;
====Solution B2: Allow multiple uploads directly via Filepicker repository page====&lt;br /&gt;
 &lt;br /&gt;
In the file picker, we should allow people to upload files directly into their Private files and then select it for use in whatever filearea they were editing, all in one step.&lt;br /&gt;
&lt;br /&gt;
====Solution B3: Allow users to &amp;quot;link&amp;quot; to files in their private files====&lt;br /&gt;
&lt;br /&gt;
Add the linking ability to private files, so that people can manage and update multiple copies of files.&lt;br /&gt;
&lt;br /&gt;
===Problem C: File picker looks &amp;quot;ugly&amp;quot;===&lt;br /&gt;
&lt;br /&gt;
Many people say that they just don&#039;t like the &amp;quot;MS Windows&amp;quot; look of the current filepicker.  This look has been somewhat dictated by the choice of YUI2 to implement it, but it can be improved a lot.&lt;br /&gt;
&lt;br /&gt;
====Solution C1: Improve default graphics styling/layout for the dialog====&lt;br /&gt;
&lt;br /&gt;
It needs to look more like the rest of the Moodle interface.  &lt;br /&gt;
&lt;br /&gt;
It should use lightboxing to prevent access to the page, and should be larger by default.&lt;br /&gt;
&lt;br /&gt;
Thumbnails and titles should flow more nicely with less cropping.&lt;br /&gt;
&lt;br /&gt;
====Solution C2: Implement real thumbnail images where possible====&lt;br /&gt;
&lt;br /&gt;
Currently we show images based on the mimetype of the file.  For images, we should generate and cache small, efficient thumbnail images of the file for viewing in the file picker via lazy-loading.&lt;br /&gt;
&lt;br /&gt;
Along with this, we should increase the length of pages as much as possible and reduce the number of pages.  Scrolling works better than paging.&lt;br /&gt;
&lt;br /&gt;
See:  MDL-23044&lt;br /&gt;
&lt;br /&gt;
====Solution C3: Add better CSS/renderers so themes can style it better====&lt;br /&gt;
&lt;br /&gt;
====Solution C4: Add better descriptions/help for options====&lt;br /&gt;
&lt;br /&gt;
Options all need help icons with translated popup help.  &amp;quot;linking files&amp;quot; needs to be better explained.&lt;br /&gt;
&lt;br /&gt;
====Solution C5: Simplify &amp;quot;Server Files&amp;quot;====&lt;br /&gt;
&lt;br /&gt;
Server files can be made simpler.&lt;br /&gt;
&lt;br /&gt;
# Only show courses that you have access to (can ignore categories, make it more like My Courses)&lt;br /&gt;
# Don&#039;t even show folders that are empty&lt;br /&gt;
# Remove some of the levels that are not needed, eg see MDL-27236&lt;br /&gt;
# Add support for all core modules, and add docs for the callback to help 3rd party authors (MDL-31675)&lt;br /&gt;
&lt;br /&gt;
===Problem D: File area management=== &lt;br /&gt;
&lt;br /&gt;
====Solution D1: A dialog in the HTML editor to manage embedded files directly====&lt;br /&gt;
&lt;br /&gt;
We need a way to edit the filearea that is associated with a HTML text.  We currently do have one for non-Javascript users but it&#039;s never seen.  We need a proper one available in the HTML editor, probably accessed as a popup dialog from a button with a yellow folder on it.   Warnings need to be shown to the effect that renaming or removing files can break the HTML text.&lt;br /&gt;
&lt;br /&gt;
====Solution D2: Add the ability to &amp;quot;replace&amp;quot; files.====&lt;br /&gt;
&lt;br /&gt;
When a file is added to the file area with the same name as one that exists, a dialog should be shown allowing the user to &amp;quot;replace the existing file&amp;quot; or &amp;quot;rename the new file&amp;quot;.  The dialog should also indicate how many &amp;quot;[[Improved_support_for_external_File_content|linked copies]]&amp;quot; will be affected, if that is the case.  &lt;br /&gt;
&lt;br /&gt;
&amp;quot;Replace&amp;quot; means that the old file record is updated with the new content (perhaps with a version increment?) - this means that any LINKS to that file are retained, and it becomes possible to update many copies at once.  This needs to be a new function in the file API.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
*[[Talk:Files usability 2.3]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Profiling_PHP&amp;diff=36102</id>
		<title>Profiling PHP</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Profiling_PHP&amp;diff=36102"/>
		<updated>2012-11-07T02:33:54Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;PHP has two significant profilers. One is [http://www.xdebug.org/docs/profiler XDebug]. Tthis is well known and understood and its trace output is supported by tools like [http://kcachegrind.sourceforge.net/html/Home.html KCachegrind]. The other is [http://pecl.php.net/package/xhprof xhprof], a profiler released by facebook&#039;s engineering team, designed for capturing profile traces on live servers.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Xdebug==&lt;br /&gt;
&lt;br /&gt;
[http://xdebug.org/docs/profiler Xdebug] is a powerful PHP debugging tool. The first release of Xdebug was in 2002, since that it keeps growing and remains popular among PHP developers. Among its major features are stack and function tracing, code coverage analysis, remote debugging and scripts profiling. As the page topic suggests, we will focus on its profiling feature, which provides developer with detailed information about the script performance, helps identifying which parts of the code are slow. Collected information is being stored in cachegrind compatible file and can be analysed using one of external tools, such as [http://kcachegrind.sourceforge.net/html/Home.html KCachegrind], [http://sourceforge.net/projects/wincachegrind/ WinCacheGrind], [http://code.google.com/p/xdebugtoolkit/ xdebugtoolkit] or web-based analyser [https://github.com/jokkedk/webgrind#readme Webgrind]. Xdebug is simple to install and operate, it does not require code changes.&lt;br /&gt;
&lt;br /&gt;
===Installing Xdebug extension===&lt;br /&gt;
&lt;br /&gt;
The first step that has to be done is installing Xdebug extension. This procedure depends on the OS you are using, but general idea is to obtain Xdebug php extension and specify full path to it using &#039;&#039;zend_extension&#039;&#039; setting in &#039;&#039;php.inf&#039;&#039; file. Official [http://www.xdebug.org/docs/install Xdebug installation documentation] explains its installation on Windows, installing through PEAR/PECL and compiling from source.&lt;br /&gt;
Xdebug has been packaged for many Linux distributions, so it can be installed using corresponding package management tool. On Debian or Ubuntu, for example one would need to execute:&lt;br /&gt;
&lt;br /&gt;
 # apt-get install php5-xdebug&lt;br /&gt;
&lt;br /&gt;
This will put &#039;&#039;xdebug.so&#039;&#039; in default php modules directory and create &#039;&#039;/etc/php5/conf.d/xdebug.ini&#039;&#039; file with single line:&lt;br /&gt;
&lt;br /&gt;
 zend_extension=/usr/lib/php5/20090626/xdebug.so&lt;br /&gt;
&lt;br /&gt;
Note that this is equal to specifying the same line in &#039;&#039;php.inf&#039;&#039;, example above just reflects split php configuration.&lt;br /&gt;
&lt;br /&gt;
Once the Xdebug extension is installed and specified in php configuration, one may restart the webserver and information about Xdebug should appear in &#039;&#039;phpinfo()&#039;&#039; function output. If not, make sure that &#039;&#039;zend_extension&#039;&#039; line is not commented out, extension file exists in specified location and refer to webserver logs for more details.&lt;br /&gt;
&lt;br /&gt;
===Configuring Xdebug Profiler===&lt;br /&gt;
&lt;br /&gt;
When Xdebug extension is installed, it is time to configure profiling functionality. There are number of parameters related to profiling, all of them start with &#039;&#039;xdebug.profiler_&#039;&#039; prefix. First of all, profiler should be enabled. There are two ways of doing it. One way is keeping it always enabled, so profiling information will be generated on every page request:&lt;br /&gt;
&lt;br /&gt;
 xdebug.profiler_enable=1&lt;br /&gt;
&lt;br /&gt;
Another way is making Xdebug writing profiling information on demand by triggering it with GET/POST or COOKIE variable named &#039;&#039;XDEBUG_PROFILE&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
 xdebug.profiler_enable_trigger = 1&lt;br /&gt;
&lt;br /&gt;
This option is preferable for several reasons: profiling data files are relatively large especially on complex scripts and you may run out of disk-space pretty quick, it allows generating profiling information only when you need it, thus it is easier to find the generated file in output directory, finally, on demand profiling can be done on production servers, though it is not recommended. If you have enabled &#039;&#039;xdebug.profiler_enable_trigger&#039;&#039; option, make sure that &#039;&#039;xdebug.profiler_enable&#039;&#039; is disabled, otherwise this will lead to dump file being generated on each request.&lt;br /&gt;
&lt;br /&gt;
Whatever method of profiling enabling you have chosen, profiling data files will be generated on each request (or request with a trigger parameter) in directory specified with &#039;&#039;xdebug.profiler_output_dir&#039;&#039; directive. By default is it set to &#039;&#039;/tmp&#039;&#039;, but you may change it to any more suitable location.&lt;br /&gt;
&lt;br /&gt;
 xdebug.profiler_output_dir=/tmp&lt;br /&gt;
&lt;br /&gt;
Generated file will be named in accordance with &#039;&#039;xdebug.profiler_output_name&#039;&#039; setting. This setting can handle some specifiers and use them in the file name. Default name pattern is &#039;&#039;cachegrind.out.%p&#039;&#039;; in the actual file name %p will be replaced with process ID value. The fill list of specifier can be found [http://www.xdebug.org/docs/all_settings#trace_output_name here]. More intuitive naming is recommended, so that it is easier to find the file you have just generated in the bunch of others:&lt;br /&gt;
&lt;br /&gt;
 xdebug.profiler_output_name=cachegrind.out.%R.%t&lt;br /&gt;
&lt;br /&gt;
With this naming pattern, timestamp and script name will be reflected in the file name.&lt;br /&gt;
&lt;br /&gt;
===Profiling a page===&lt;br /&gt;
&lt;br /&gt;
When you are done with configuration, it is time to test it. First of all, the webserver has to be restarted, so that the new config will come into effect. Now assuming that Xdebug was configured to use a trigger for script profiling, open any php page on your server in your browser having added &#039;&#039;XDEBUG_PROFILE&#039;&#039; parameter to URL string:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://servername/moodle2/index.php?XDEBUG_PROFILE&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or if it already has some parameters, just add our trigger to the URL end:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://servername/moodle2/mod/forum/view.php?id=5&amp;amp;XDEBUG_PROFILE&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As a result, the new file should be generated in directory you specified with &#039;&#039;xdebug.profiler_output_dir&#039;&#039; directive:&lt;br /&gt;
&lt;br /&gt;
 cachegrind.out._moodle2_mod_forum_view_php.1289838411&lt;br /&gt;
 cachegrind.out._moodle2_index_php.1289837892&lt;br /&gt;
&lt;br /&gt;
Triggering profiling with POST requests or AJAX queries is also possible without code changes. [https://addons.mozilla.org/en-US/firefox/addon/58688/ Easy Xdebug] plugin for FireFox has profiling toggle button that inserts XDEBUG_PROFILE variable into cookie data, thus making profiling enabled for as long as you wish for all requests. Similar plugins exist for [https://chrome.google.com/extensions/detail/eadndfjplgieldjbigjakmdgkmoaaaoc Chrome] and [https://github.com/benmatselby/xdebug-toggler Safari] browsers.&lt;br /&gt;
&lt;br /&gt;
===Analyzing Xdebug profiling files===&lt;br /&gt;
&lt;br /&gt;
As it was pointed out earlier, profiling data is recoded in cachegrind format, so it can be analysed using one of external tools, such as [http://kcachegrind.sourceforge.net/html/Home.html KCachegrind], [http://sourceforge.net/projects/wincachegrind/ WinCacheGrind], [http://code.google.com/p/xdebugtoolkit/ xdebugtoolkit] or web-based analyser [https://github.com/jokkedk/webgrind#readme Webgrind]. Using these tools is pretty simple. I prefer KCachegrind which has a feature to show the matching code if web-server is run on the same box. Xdebug profiler documentation page has a [http://xdebug.org/docs/profiler#misc section] about KCachegrind that worth reading for everyone who starts using KCachegrind.&lt;br /&gt;
&lt;br /&gt;
===Quick summary===&lt;br /&gt;
&lt;br /&gt;
1. Install XDebug extension on your server.&lt;br /&gt;
&lt;br /&gt;
2. Add something like this to your php.ini file:&lt;br /&gt;
 zend_extension = /usr/lib/php5/20090626/xdebug.so&lt;br /&gt;
 xdebug.profiler_enable = 0&lt;br /&gt;
 xdebug.profiler_enable_trigger = 1&lt;br /&gt;
 xdebug.profiler_output_dir = /var/tmp&lt;br /&gt;
 xdebug.profiler_output_name = cachegrind.out.%R.%t&lt;br /&gt;
or add something like this to your .htaccess file&lt;br /&gt;
 php_flag xdebug.profiler_enable off&lt;br /&gt;
 php_flag xdebug.profiler_enable_trigger on&lt;br /&gt;
 php_value xdebug.profiler_output_dir /var/tmp&lt;br /&gt;
 php_value xdebug.profiler_output_name cachegrind.out.%R.%t&lt;br /&gt;
&lt;br /&gt;
3. When you want to profile a page, add &amp;lt;tt&amp;gt;&amp;amp;XDEBUG_PROFILE&amp;lt;/tt&amp;gt; to the end of the URL.&lt;br /&gt;
&lt;br /&gt;
4. Open the &amp;lt;tt&amp;gt;cachegrind.out....&amp;lt;/tt&amp;gt; file that is generated with one of the tools mentioned above.&lt;br /&gt;
&lt;br /&gt;
==XHProf==&lt;br /&gt;
&lt;br /&gt;
Compared to XDebug, XHProf aims to have the minimum impact on execution times and require relatively little space to store a trace, so you can run it live without a noticeable impact on users and without filling disks. The trade off with XHProf is in &#039;&#039;slightly&#039;&#039; less detail and a trace format that needs its own GUI to visualize (which ships with XHProf). The reason you&#039;d want to profile in a live environments (vs. on development) is certain categories of problem may only been seen live, e.g. (data related) what happens when the user table grows 100 times, the impact on &amp;quot;login.php&amp;quot;  or (service related) server to server HTTP requests. Did a presentation once [https://docs.google.com/present/view?id=dcbkgbgf_45fbg3rnmk XHProf - Facebook&#039;s PHP profiler] which explains in more detail - Penny saw the talk which is how I ended up writing this.&lt;br /&gt;
&lt;br /&gt;
[[Setting up xhprof on Moodle]]&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=162045 Forum thread about profiling Moodle 2.0]&lt;br /&gt;
XHProf articles:&lt;br /&gt;
* [https://docs.google.com/present/view?id=dcbkgbgf_45fbg3rnmk&amp;amp; Presentation about XHProf]&lt;br /&gt;
* [http://techportal.ibuildings.com/2009/12/01/profiling-with-xhprof/ Profiling with XHProf]&lt;br /&gt;
* [http://www.open.ac.uk/blogs/XHProf/?page_id=46 XHProf – Profiling and Reporting] from Moodle perspective written by [http://moodle.org/user/view.php?id=264538&amp;amp;course=5 James Brisland]&lt;br /&gt;
Xdebug articles:&lt;br /&gt;
* [http://devzone.zend.com/article/2803-Introducing-xdebug Introducing xdebug]&lt;br /&gt;
* [http://blog.teamlazerbeez.com/2010/05/04/xdebug-quickstart-profiling-in-php/ Xdebug Quickstart: Profiling in PHP]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=XMLDB_Creating_new_DDL_functions&amp;diff=36101</id>
		<title>XMLDB Creating new DDL functions</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=XMLDB_Creating_new_DDL_functions&amp;diff=36101"/>
		<updated>2012-11-07T02:33:05Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* The example, explained */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}[[XMLDB Documentation|XMLDB Documentation]] &amp;gt; [[XMLDB roadmap|Roadmap]] &amp;gt; XMLDB Creating new DDL functions ----__NOTOC__&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note:&#039;&#039;&#039; All the functions showed in this page are for use in &#039;&#039;&#039;Moodle 2.0 upwards&#039;&#039;&#039;, where we changed the [[DB layer 2.0|DB layer]] to support some new features. If you need information for previous Moodle version, take a look to the [[XMLDB creating new DDL functions - pre 2.0|pre 2.0]] page.&lt;br /&gt;
&lt;br /&gt;
== Justification ==&lt;br /&gt;
&lt;br /&gt;
Once the [[XMLDB Defining one XML structure|XMLDB Structure]] has been well defined and we are able to abstract any DB model with it, it&#039;s time to build one group of functions that will create the needed DB objects based in the information supplied by such XML structure. All those functions will use the underlying XMLB objects (XMLDBTable, XMLDBField, XMLDBKey, XMLDBIndex) in order to keep the level of abstraction across them.&lt;br /&gt;
&lt;br /&gt;
The objectives to be supplied by these functions are:&lt;br /&gt;
&lt;br /&gt;
* Be used by the &#039;&#039;&#039;installation&#039;&#039;&#039; process, replacing the old *.sql usage, to create all the needed DB objects.&lt;br /&gt;
* Be used by the &#039;&#039;&#039;upgrade&#039;&#039;&#039; process, in the new upgrade.php script, to handle all the DB objects.&lt;br /&gt;
&lt;br /&gt;
Although initially it wasn&#039;t completely decided if all those XMLDB objects were going to be used by the DDL functions, or we were going to create a bunch of new functions, completely independent from the XMLDB schema, finally we decided to use the previously created object, mainly because:&lt;br /&gt;
&lt;br /&gt;
* If we have some objects representing tables, fields and so on, it hasn&#039;t too much sense to build new functions requiring the same type of objects as parameters and not to use them.&lt;br /&gt;
* Any change in the XMLDB objects will be immediately available to the DDL functions (one new column type...).&lt;br /&gt;
* Code inside the DDL functions will be pretty simple because all the SQL code generation will be the responsibility of the XMLDB classes themselves.&lt;br /&gt;
* By using the XMLDB objects, the XMLDB Editor (the tool to edit XMLDB structures easily) could be improved in order to generate automatically the needed PHP code to be used by the upgrade.php scripts. Just select one object (table/field/index/key), one action to perform (create/drop/rename/alter) and, wow!, you will obtain all the PHP code to be pasted in the upgrade script. (coming soon) ;-)&lt;br /&gt;
&lt;br /&gt;
The only drawback we have found is that upgrade code will be a bit longer, because it will need to include all the XMLDB objects creation and definition code. But, in the other hand, it will be an easily readable code.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
=== Basic Concepts ===&lt;br /&gt;
&lt;br /&gt;
All the functions will:&lt;br /&gt;
&lt;br /&gt;
* accept one XMLDB objects (the one to be modified) as parameter.&lt;br /&gt;
* perform all the possible checks before executing any SQL command against DB.&lt;br /&gt;
* return true/false on success/error (the XMLDB object will contain extra info on error, available with the XMLDBObject-&amp;gt;getError() function).&lt;br /&gt;
* output debug info if enabled.&lt;br /&gt;
&lt;br /&gt;
=== Handling XMLDB objects ===&lt;br /&gt;
&lt;br /&gt;
==== Introduction ====&lt;br /&gt;
&lt;br /&gt;
Before calling the DDL functions themselves, the proper XMLDB objects must be properly created and contain all the needed info in order to allow the functions to do their work. The basic code structure for any DDL operation should be:&lt;br /&gt;
&lt;br /&gt;
# Creation of the XMLDB objects programatically.&lt;br /&gt;
# Invocation of the DDL function.&lt;br /&gt;
# Check for errors&lt;br /&gt;
&lt;br /&gt;
==== Basic example ====&lt;br /&gt;
&lt;br /&gt;
One short (hehe!) example of these steps could be the following PHP lines, used to create a simple table with one primary key, one foreign key and one index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt; &lt;br /&gt;
$field1 = new xmldb_field(&#039;id&#039;);&lt;br /&gt;
$field1-&amp;gt;set_attributes(XMLDB_TYPE_INTEGER, &#039;10&#039;, XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, [null, null,] null, null); // [XMLDB_ENUM, null,] Moodle 2.x deprecated  &lt;br /&gt;
$field2 = new xmldb_field(&#039;name&#039;);&lt;br /&gt;
$field2-&amp;gt;set_attributes(XMLDB_TYPE_CHAR, &#039;255&#039;, null, XMLDB_NOTNULL, null, [null, null,] &#039;default name&#039;, &#039;id&#039;); // [XMLDB_ENUM, null,] Moodle 2.x deprecated &lt;br /&gt;
$field3 = new xmldb_field(&#039;course&#039;);&lt;br /&gt;
$field3-&amp;gt;set_attributes(XMLDB_TYPE_INTEGER, &#039;10&#039;, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, [null, null,] null, &#039;name&#039;); //  [XMLDB_ENUM, null,] Moodle 2.x deprecated &lt;br /&gt;
$field4 = new xmldb_field(&#039;type&#039;);&lt;br /&gt;
$field4-&amp;gt;set_attributes(XMLDB_TYPE_CHAR, &#039;20&#039;, null, XMLDB_NOTNULL, null, [XMLDB_ENUM, array(&#039;type1&#039;, &#039;type2&#039;, &#039;type3&#039;),] &#039;type1&#039;, &#039;course&#039;); //  [XMLDB_ENUM, null,] Moodle 2.x deprecated &lt;br /&gt;
$field5 = new xmldb_field(&#039;summary&#039;);&lt;br /&gt;
$field5-&amp;gt;set_attributes(XMLDB_TYPE_TEXT, &#039;medium&#039;, null, null, null, [null, null,] null, &#039;type&#039;); //  [XMLDB_ENUM, null,] Moodle 2.x deprecated &lt;br /&gt;
$key1 = xmldb_key(&#039;primary&#039;);&lt;br /&gt;
$key1-&amp;gt;set_attributes(XMLDB_KEY_PRIMARY, array(&#039;id&#039;), null, null);&lt;br /&gt;
$key2 = xmldb_key(&#039;foreignkey1&#039;);&lt;br /&gt;
$key2-&amp;gt;set_attributes(XMLDB_KEY_FOREIGN, array(&#039;courseid&#039;), &#039;course&#039;, array(&#039;id&#039;)); &lt;br /&gt;
 &lt;br /&gt;
$index1 = xmldb_index(&#039;type&#039;);&lt;br /&gt;
$index1-&amp;gt;set_attributes(XMLDB_INDEX_NOTUNIQUE, array(&#039;type&#039;));&lt;br /&gt;
 &lt;br /&gt;
$table = new xmldb_table(&#039;my_first_xmldb_table&#039;);&lt;br /&gt;
$table-&amp;gt;addField($field1);&lt;br /&gt;
$table-&amp;gt;addField($field2);&lt;br /&gt;
$table-&amp;gt;addField($field3);&lt;br /&gt;
$table-&amp;gt;addField($field4);&lt;br /&gt;
$table-&amp;gt;addField($field5);&lt;br /&gt;
 &lt;br /&gt;
$table-&amp;gt;addKey($key1);&lt;br /&gt;
$table-&amp;gt;addKey($key2);&lt;br /&gt;
 &lt;br /&gt;
$table-&amp;gt;addIndex($index1);&lt;br /&gt;
 &lt;br /&gt;
$status = $dbman-&amp;gt;create_table($table);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First of all, don&#039;t panic!, the code above can be easily reduced to, exactly, 10 lines, but in order to explain it with some detail we have posted here the long version. ;-) Also, don&#039;t forget that one of the upcoming features of the use of the XMLDB objects will be the &#039;&#039;&#039;ability of the XMLDB Editor to generate all this PHP code&#039;&#039;&#039; automatically for you.&lt;br /&gt;
&lt;br /&gt;
==== The example, explained ====&lt;br /&gt;
&lt;br /&gt;
Lets start with the very two first lines:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$field1 = new xmldb_field(&#039;id&#039;);&lt;br /&gt;
$field1-&amp;gt;set_attributes(XMLDB_TYPE_INTEGER, &#039;10&#039;, XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, [null, null,] null, null); //  [XMLDB_ENUM, null,] Moodle 2.x deprecated &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the first line we create a new field, called &amp;quot;id&amp;quot; (that will be the name of the column in the table, so do forget to follow the [[XMLDB Defining one XML structure#Conventions|XMLDB naming conventions]] and the [[Database|coding guidelines]].&lt;br /&gt;
&lt;br /&gt;
In the second line we define all the attributes of the field with a simple function call. The list of parameters is:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Column Type:&#039;&#039;&#039; Can be one of these: XMLDB_TYPE_INTEGER, XMLDB_TYPE_NUMBER, XMLDB_TYPE_CHAR, XMLDB_TYPE_TEXT, XMLDB_TYPE_BINARY. Note that XMLDB internals support other column types but they are only for compatibility with some special tables. Moodle shouldn&#039;t use anything out from the list above.&lt;br /&gt;
# &#039;&#039;&#039;Column precision:&#039;&#039;&#039; Its length for INTEGERS and CHARS, two comma-separated numbers (total length and number of decimal positions) for NUMBERS and one of this: &amp;quot;small&amp;quot;, &amp;quot;medium&amp;quot;, &amp;quot;big, for both TEXTS and BINARIES.&lt;br /&gt;
# &#039;&#039;&#039;Unsigned:&#039;&#039;&#039; To specify if the numeric field will be unsigned, we&#039;ll use the XMLDB_UNSIGNED constant. If signed, we&#039;ll pass null.&lt;br /&gt;
# &#039;&#039;&#039;Not Null:&#039;&#039;&#039; To specify if the field will be not null, we&#039;ll use the XMLDB_NOTNULL constant. If nullable, we&#039;ll pass null.&lt;br /&gt;
# &#039;&#039;&#039;Sequence:&#039;&#039;&#039; To specify if the field will be a sequence (or auto-numeric, or auto-incremented, or whatever you call it), we&#039;ll use the XMLDB_SEQUENCE constant. If not, we&#039;ll pass null.&lt;br /&gt;
# &#039;&#039;&#039;Enum:&#039;&#039;&#039; To specify if the field only will contain a limited number of possible values, we&#039;ll use the XMLDB_ENUM constant. If not, we&#039;ll pass null. // Moodle 2.0 deprecated&lt;br /&gt;
# &#039;&#039;&#039;Enum values:&#039;&#039;&#039; If the field has been defined as XMLDB_ENUM, this paramenter will have one array containing all the possible values for the field. Else, null. // Moodle 2.0 deprecated&lt;br /&gt;
# &#039;&#039;&#039;Default value:&#039;&#039;&#039; If the field has some meaningful default value to be defined this parameter will contain it. Else, null.&lt;br /&gt;
# &#039;&#039;&#039;After-field:&#039;&#039;&#039; If you want to enforce the field to be created after some other exact column in the DB, this parameter will allow you to do so. Note that, if not specified, every added field is created after the latest one so, in this example we could not define it at all. We&#039;ll use null once again if undefined.&lt;br /&gt;
So, the lines of code above are creating one Field, named &amp;quot;id&amp;quot;, that will be an integer of 10 digits, not null and auto-numeric. Let&#039;s see the next fields:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$field2 = new xmldb_field(&#039;name&#039;);&lt;br /&gt;
$field2-&amp;gt;set_attributes(XMLDB_TYPE_CHAR, &#039;255&#039;, null, XMLDB_NOTNULL, null, [null, null,] &#039;default name&#039;, &#039;id&#039;); //  [XMLDB_ENUM, Enum values,] Moodle 2.x deprecated &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we are creating one new field, called &amp;quot;name&amp;quot;, defined as one char with maximum length of 255cc, not null, with one default value (&amp;quot;default name&amp;quot;) and to be created after the &amp;quot;id&amp;quot; field (note again that, to build tables, this order isn&#039;t useful at all and the order of adding the fields to the table will be used).&lt;br /&gt;
&lt;br /&gt;
Lets examine the next one:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$field3 = new xmldb_field(&#039;course&#039;);&lt;br /&gt;
$field3-&amp;gt;set_attributes(XMLDB_TYPE_INTEGER, &#039;10&#039;, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, [null, null,] null, &#039;name&#039;); //  [XMLDB_ENUM, Enum values,] Moodle 2.x deprecated &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Another new field, called &amp;quot;course&amp;quot;, integer with a maximum length of 10, unsigned and not null (forget the &#039;name&#039; value, to define &#039;&#039;&#039;after-field&#039;&#039;&#039; info as we said some lines above).&lt;br /&gt;
&lt;br /&gt;
Another one field:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$field4 = new xmldb_field(&#039;type&#039;);&lt;br /&gt;
$field4-&amp;gt;set_attributes(XMLDB_TYPE_CHAR, &#039;20&#039;, null, XMLDB_NOTNULL, null, [XMLDB_ENUM, array(&#039;type1&#039;, &#039;type2&#039;, &#039;type3&#039;),] &#039;type1&#039;, &#039;course&#039;); //  [XMLDB_ENUM, Enum values,] Moodle 2.x deprecated &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This field will be called &amp;quot;type&amp;quot;, char(20), not null, will have three possible values (&amp;quot;type1&amp;quot;, &amp;quot;type2&amp;quot; and &amp;quot;type3&amp;quot;) and its default value will be &amp;quot;type1&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
And the final field in our example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$field5 = new xmldb_field(&#039;summary&#039;);&lt;br /&gt;
$field5-&amp;gt;set_attributes(XMLDB_TYPE_TEXT, &#039;medium&#039;, null, null, null, [null, null,] null, &#039;type&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That will be a field name &amp;quot;summary&amp;quot; of type text with a &amp;quot;medium&amp;quot; length.&lt;br /&gt;
&lt;br /&gt;
Also note that it isn&#039;t mandatory to specify all those &amp;quot;null&amp;quot; parameters and, starting from the right, we can safely avoid them, so the last field we have seen could be (if we ignore, once more, the &#039;&#039;&#039;after-field&#039;&#039;&#039; parameter:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$field5-&amp;gt;set_attributes(XMLDB_TYPE_TEXT, &#039;medium&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, we&#039;ll analyse the creation of keys. The first key specified in our example is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$key1 = new xmldb_key(&#039;primary&#039;);&lt;br /&gt;
$key1-&amp;gt;set_atrributes(XMLDB_KEY_PRIMARY, array(&#039;id&#039;), null, null);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the first line, we create an index named &amp;quot;primary&amp;quot;. It&#039;s important to note that, whereas field names specified in the previous code is important because it&#039;s going to be the name of the fields in the DB, the names specified here aren&#039;t important at all, mainly, because of [[XMLDB key and index naming]] specs, so the keys and indexes created will be automatically named following those rules. Anyway, in the XMLDB files we use to call &amp;quot;primary&amp;quot; to the primary key and to concatenate the name of the fields in the key/index separated by &amp;quot;-&amp;quot; as their official name. But here it isn&#039;t important.&lt;br /&gt;
&lt;br /&gt;
The second line completely defines the key. Attributes are:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Key Type:&#039;&#039;&#039; Can be one of these: XMLDB_KEY_PRIMARY, XMLDB_KEY_UNIQUE and XMLDB_KEY_FOREIGN that are the basic constraint types for our relational DB model. It&#039;s important to note that all the keys above will have one index created for them automatically so, when you define any Key, you haven&#039;t to define any index with the same fields at all. XMLDB will do it for you. More yet, for now, Foreign Keys won&#039;t be enforced (just the underlying indexes will be created) because before enabling them a lot of changes need to be performed within Moodle processes. But we want to have all those relations defined since the beginning of XMLDB. It will allow us to move quickly to a pure relational model.&lt;br /&gt;
# &#039;&#039;&#039;List of fields:&#039;&#039;&#039; An array containing the names of the fields that will be part of the key (and the underlying index).&lt;br /&gt;
# &#039;&#039;&#039;Reference table:&#039;&#039;&#039; Exclusively for Foreign Keys, the table where the fields defined in the previous parameter are pointing to.&lt;br /&gt;
# &#039;&#039;&#039;Reference fields:&#039;&#039;&#039; Exclusively for Foreign Keys, the list of fields in the reference table that must match with the list of fields in the own table. Please note that, those &amp;quot;reference fields&amp;quot; must be defined as primary or unique key in the reference table. Relational rules, you know. So avoid pointing to fields not satisfying this condition completely!&lt;br /&gt;
&lt;br /&gt;
With all this info, we know that we are going to create one primary key with the field &amp;quot;id&amp;quot;. Remember that XMLDB will set the correct name for that key.&lt;br /&gt;
&lt;br /&gt;
The second index in our example says:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$key2 = new xmldb_key(&#039;foreignkey1&#039;);&lt;br /&gt;
$key2-&amp;gt;set_attributes(XMLDB_KEY_FOREIGN, array(&#039;courseid&#039;), &#039;course&#039;, array(&#039;id&#039;)); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we are creating one more key, of type foreign key, with the field &amp;quot;course&amp;quot; pointing to the &amp;quot;id&amp;quot; field of the &amp;quot;course&amp;quot; table. Once more, the name isn&#039;t important at all.&lt;br /&gt;
&lt;br /&gt;
And finally, we arrive to the indexes. Don&#039;t forget that all the previously defined keys will generate one underlying index with the fields specified so, in this sections we only have to define some other combinations of fields that are used often by SQL statements. By providing the correct indexes we&#039;ll get big speed improvements. But they must be correct. It&#039;s absolutely wrong to &amp;quot;index everything&amp;quot; without knowing what is being done. Don&#039;t forget it! &lt;br /&gt;
&lt;br /&gt;
In our example we have this lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$index1 = new xmldb_index(&#039;type&#039;);&lt;br /&gt;
$index1-&amp;gt;set_attributes(XMLDB_INDEX_NOTUNIQUE, array(&#039;type&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the first line we create the new index object. Remember, that the name provided here isn&#039;t the name of the index in the RDBMS, [[XMLDB key and index naming]] will define it automatically.&lt;br /&gt;
&lt;br /&gt;
In the second line we specify:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Index Type:&#039;&#039;&#039; that can be XMLDB_INDEX_NOTUNIQUE (that is pretty equivalent to null, the default) or XMLDB_INDEX_UNIQUE depending if the list of fields defined in the previous parameter allows duplicate tuples or no.&lt;br /&gt;
# &#039;&#039;&#039;List of fields:&#039;&#039;&#039; An array containing the names of the fields that will be part of the index.&lt;br /&gt;
&lt;br /&gt;
So, in the example, we are creating one non unique index with the field called &amp;quot;type&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
With this we&#039;ve seen all the individual objects that conforms one table structure. Now we are going all these object to our new table so code says:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$table = new xmldb_table(&#039;my_first_xmldb_table&#039;);&lt;br /&gt;
$table-&amp;gt;addField($field1);&lt;br /&gt;
$table-&amp;gt;addField($field2);&lt;br /&gt;
$table-&amp;gt;addField($field3);&lt;br /&gt;
$table-&amp;gt;addField($field4);&lt;br /&gt;
$table-&amp;gt;addField($field5);&lt;br /&gt;
&lt;br /&gt;
$table-&amp;gt;addKey($key1);&lt;br /&gt;
$table-&amp;gt;addKey($key2);&lt;br /&gt;
&lt;br /&gt;
$table-&amp;gt;addIndex($index1);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this lines we are simply creating one new table, called &amp;quot;my_first_xmldb_table&amp;quot;, whose name must follow the [[XMLDB Defining one XML structure#Conventions|XMLDB naming conventions]] and the [[Database|coding guidelines]]. Then all the previously defined fields, keys and indexes are added to the table. Point.&lt;br /&gt;
&lt;br /&gt;
And the final line of our code is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$status = $dbman-&amp;gt;create_table($table);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this line, we&#039;ll create the DB table, with all the fields/keys/indexes specified with all the specs defined in the previous lines, with the correct prefix, proper object naming and particularities for each different RDBMS. This function call will return true/false, depending if the execution of the needed SQL commands has ended Ok or no.&lt;br /&gt;
&lt;br /&gt;
Wow, at last, we end!!&lt;br /&gt;
&lt;br /&gt;
==== Reduced (and recommended) example ====&lt;br /&gt;
&lt;br /&gt;
All the lines in the example above have been really good to explain how to create any XMLDBField, XMLDBKey, XMLDBIndex and XMLDBTable structures from PHP code but we must recognise that it&#039;s a bit awful in terms of readability and length. So, for the creation of tables, we can use the following code, that is a complete replacement for the previous one:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$table = new xmldb_table(&#039;my_first_xmldb_table&#039;);&lt;br /&gt;
 &lt;br /&gt;
$table-&amp;gt;add_field(&#039;id&#039;, XMLDB_TYPE_INTEGER, &#039;10&#039;, XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);&lt;br /&gt;
$table-&amp;gt;add_field(&#039;name&#039;, XMLDB_TYPE_CHAR, &#039;255&#039;, null, XMLDB_NOTNULL, null, null, null, &#039;default name&#039;);&lt;br /&gt;
$table-&amp;gt;add_field(&#039;course&#039;, XMLDB_TYPE_INTEGER, &#039;10&#039;, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);&lt;br /&gt;
$table-&amp;gt;add_field(&#039;type&#039;, XMLDB_TYPE_CHAR, &#039;20&#039;, null, XMLDB_NOTNULL, null, XMLDB_ENUM, array(&#039;type1&#039;, &#039;type2&#039;, &#039;type3&#039;), &#039;type1&#039;);&lt;br /&gt;
$table-&amp;gt;add_field(&#039;summary&#039;, XMLDB_TYPE_TEXT, &#039;medium&#039;, null, null, null, null, null, null);&lt;br /&gt;
 &lt;br /&gt;
$table-&amp;gt;add_key(&#039;primary&#039;, XMLDB_KEY_PRIMARY, array(&#039;id&#039;), null, null);&lt;br /&gt;
$table-&amp;gt;add_key(&#039;foreignkey1&#039;, XMLDB_KEY_FOREIGN, array(&#039;courseid&#039;), &#039;course&#039;, array(&#039;id&#039;)); &lt;br /&gt;
 &lt;br /&gt;
$table-&amp;gt;add_index(&#039;type&#039;, XMLDB_INDEX_NOTUNIQUE, array(&#039;type&#039;));&lt;br /&gt;
 &lt;br /&gt;
$status = $dbman-&amp;gt;create_table($table);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Better, isn&#039;t it? ;-)&lt;br /&gt;
&lt;br /&gt;
All the documentation in the previous complete example continues being 100% valid here. Just note that, when using this syntax, we have one more field, exactly &#039;&#039;&#039;the first one&#039;&#039;&#039;, that must be used to specify the name of the field/key/index being added. The rest is exactly the same!&lt;br /&gt;
&lt;br /&gt;
=== Integration with the XMLDB Editor ===&lt;br /&gt;
&lt;br /&gt;
Now you should know a bit more about how to create tables via PHP under the new XMLDB schema. It was really important to know about it because, in the process, you&#039;ve seen how to create fields, keys and indexes, plus some extra notions about the whole thing.&lt;br /&gt;
&lt;br /&gt;
Also, the creation of tables is, with difference, the most difficult operation (in terms of PHP generated) that you will find under all the new DDL functions. Other operations like rename, drop, alter will be really easier to use. Trying to help you a bit in the PHP generation, if you use the XMLDB Editor to design and create your DB structures, you will be able to obtain all the code needed to perform different DDL actions automatically. &lt;br /&gt;
&lt;br /&gt;
Just go to the table/field/key/index you are modifying and, after performing the desired changes, press the &amp;quot;PHP Code&amp;quot; link and it will show you one new page with all (practically) the options available. Just select the desired one and the required PHP will appear in seconds&lt;br /&gt;
&lt;br /&gt;
Such code can be directly used by your upgrade scripts (by modifying some minor bits here and there) improving the DB experience and reducing the risk of errors a lot.&lt;br /&gt;
&lt;br /&gt;
Also, it&#039;s highly recommended to use such utility to understand better how the PHP code works, like an online tutor. Enjoy it!&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[DDL functions|DDL functions]]: To get access to the updated documentation about the functions available to modify the DB objects.&lt;br /&gt;
* [[XMLDB Defining one XML structure|XML structure]]: To know a bit more about the internal XML structure used to describe all the DB objects using one neutral language.&lt;br /&gt;
* [[XMLDB List of DDL functions to create|List of functions to create]]: The list of functions to be created from scratch. Used to follow the progress and its status.&lt;br /&gt;
&lt;br /&gt;
[[Category:XMLDB]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Course_formats_2&amp;diff=36100</id>
		<title>Course formats 2</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Course_formats_2&amp;diff=36100"/>
		<updated>2012-11-07T02:29:18Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Backward compartibility */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Course formats refactoring&lt;br /&gt;
|state = FINAL DISCUSSION OF SPECIFICATION&lt;br /&gt;
|tracker = MDL-35218&lt;br /&gt;
|assignee = [[User:Marina Glancy|Marina Glancy]]&lt;br /&gt;
|discussion = http://moodle.org/mod/forum/discuss.php?d=212405&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.4}}&lt;br /&gt;
&lt;br /&gt;
We will refactor the course formats to support a proper object-oriented design and flexibility&lt;br /&gt;
==Database changes==&lt;br /&gt;
* change course.format from varchar(10) to varchar(21)&lt;br /&gt;
* add table course_format_options that will store format-specific options for the whole course and for specific secions (fields: id, courseid, format, sectionid[optional], name, value)&lt;br /&gt;
* remove existing fields in table course: numsections, hiddensections, coursedisplay&lt;br /&gt;
&lt;br /&gt;
==Object model==&lt;br /&gt;
&lt;br /&gt;
Course formats must be classes inheriting base &#039;&#039;&#039;class format_base&#039;&#039;&#039; instead of the current collection of callbacks. (Although some callbacks will be converted to methods)&lt;br /&gt;
&lt;br /&gt;
Method &#039;&#039;&#039;course_get_format($course)&#039;&#039;&#039; will return an instance of this class for the particular course. May be called with specific format but with empty course id (i.e. when course is being created)&lt;br /&gt;
&lt;br /&gt;
===Class format_base===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! method&lt;br /&gt;
! description&lt;br /&gt;
|-&lt;br /&gt;
| get_default_blocks()&lt;br /&gt;
| replaces config.php with $format[‘defaultblocks’]&lt;br /&gt;
|-&lt;br /&gt;
| course_format_options()&lt;br /&gt;
| definitions of the additional options that this course format uses for course&lt;br /&gt;
|-&lt;br /&gt;
| section_format_options()&lt;br /&gt;
| definitions of the additional options that this course format uses for section&lt;br /&gt;
|-&lt;br /&gt;
| edit_form_validation()&lt;br /&gt;
| performs additional validation for edit course form&lt;br /&gt;
|-&lt;br /&gt;
| page_set_course()&lt;br /&gt;
| is called from moodle_page::set_course() allowing course formats to run custom code at that point&lt;br /&gt;
|-&lt;br /&gt;
| page_set_cm()&lt;br /&gt;
| is called from moodle_page::set_cm()&lt;br /&gt;
|-&lt;br /&gt;
| output_course_header()&lt;br /&gt;
| displays the course-specific header. It will be included in all core themes, custom themes need to add support for it. Inside it should call function from renderer&lt;br /&gt;
|-&lt;br /&gt;
| output_course_footer()&lt;br /&gt;
| displays the course-specific footer&lt;br /&gt;
|-&lt;br /&gt;
| output_course_content_header()&lt;br /&gt;
| displays the course-specific header to be displayed in main content section (i.e. prev-next module navigation)&lt;br /&gt;
|-&lt;br /&gt;
| output_course_content_footer()&lt;br /&gt;
| displays the course-specific footer to be displayed in main content section (i.e. prev-next module navigation)&lt;br /&gt;
|-&lt;br /&gt;
| editsection_form()&lt;br /&gt;
| returns an instance of editsection_form (the format can overwrite this form)&lt;br /&gt;
|-&lt;br /&gt;
| is_section_current()&lt;br /&gt;
| replaces format_section_renderer_base::is_section_current()&lt;br /&gt;
|-&lt;br /&gt;
| get_section_name()&lt;br /&gt;
| is called from global function get_section_name()&lt;br /&gt;
|-&lt;br /&gt;
| uses_sections()&lt;br /&gt;
| is called from global function course_format_uses_sections()&lt;br /&gt;
|-&lt;br /&gt;
| supports_ajax()&lt;br /&gt;
| is called from global function course_format_ajax_support()&lt;br /&gt;
|-&lt;br /&gt;
| extend_course_navigation()&lt;br /&gt;
| replaces callback_FORMAT_load_content()&lt;br /&gt;
|-&lt;br /&gt;
| get_view_url()&lt;br /&gt;
| is called from course_get_url() and replaces callback_FORMAT_get_section_url()&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Backward compartibility==&lt;br /&gt;
&lt;br /&gt;
Define class format_legacy extends format_base that will call the existing callbacks and get blocks from config.php if format class is not defined (2.0-2.3 backward compartibility). Function course_get_format() will return instance of this class if the course format does not define its own class&lt;br /&gt;
&lt;br /&gt;
==Course sections hierarchy==&lt;br /&gt;
&lt;br /&gt;
Add support for nested course sections: &lt;br /&gt;
* avoid loops,&lt;br /&gt;
* store visibleold;&lt;br /&gt;
* Process moving, deleting sections;&lt;br /&gt;
* Make sure the hierarchy appears in navigation menu and breadcrumb;&lt;br /&gt;
* Make sure changing of availability rules for both parent and child sections works correctly together&lt;br /&gt;
&lt;br /&gt;
Core formats will NOT support hierarchy, we just want to make sure that non-plugin code can deal with it&lt;br /&gt;
&lt;br /&gt;
==Course add/edit form==&lt;br /&gt;
&lt;br /&gt;
* When course is created/edited, the options specific for the format are displayed in separate form section and it dynamically changes when format is changed (with support of non-js mode). Each course format defines which display options it needs. For the standard weeks/topics formatd they are [numsections, hiddensections, coursedisplay], for scrom and social there are no options&lt;br /&gt;
* Make Course format selector on create/edit course page looking similar to &amp;quot;Add activity&amp;quot; with help text for each format&lt;br /&gt;
&lt;br /&gt;
==Course sections management==&lt;br /&gt;
&lt;br /&gt;
* ? Add events_triggers in functions that create/delete/hide/move course sections&lt;br /&gt;
* Refactor class editsection_form so it is easier to overwrite and implements functions set_data() and get_data() instead of pre/post processing data in editsection.php&lt;br /&gt;
&lt;br /&gt;
==Add course formats as plugin to site administration==&lt;br /&gt;
&lt;br /&gt;
Course formats are plugins in the code, but they are not handled as plugins in the administration. Replace course default settings by this page. Move page to plugins.&lt;br /&gt;
&lt;br /&gt;
* option to enable/disable a course format&lt;br /&gt;
* each course format may have it&#039;s own configurable options such as default and/or sticky blocks, default settings, etc.&lt;br /&gt;
&lt;br /&gt;
==Other changes related to course formats==&lt;br /&gt;
&lt;br /&gt;
* Make sure backup/restore correctly works with sections hierarchy and new options. Also backup made in previous versions restores correctly in 2.4&lt;br /&gt;
* Make sure course format can influence navigation menu and breadcrumb. This probably will be done already by set_course/set_cm callbacks&lt;br /&gt;
* Course formats may want to extend course settings navigation. Implement additional callback or do it in set_course() ?&lt;br /&gt;
* Improvement: file_info (Server files repository) should list modules inside the course groupped by sections&lt;br /&gt;
* Describe in course formats dev documentation how formats can watch the events triggers&lt;br /&gt;
&lt;br /&gt;
==Changes to course-related functions in 2.4==&lt;br /&gt;
(see MDL-35339)&lt;br /&gt;
* add function get_module_types_names() to replace $modnames and $modnamesplural in get_all_mods()&lt;br /&gt;
* add course_modinfo::get_used_module_types() to replace $modnamesused in get_all_mods()&lt;br /&gt;
* deprecate function get_all_mods(), change it&#039;s usage&lt;br /&gt;
* deprecate function get_all_sections()&lt;br /&gt;
* deprecate add_mod_to_section() and replace it with course_add_cm_to_section() to fix the mess in arguments&lt;br /&gt;
&lt;br /&gt;
==Comments to the changes of the specification==&lt;br /&gt;
&lt;br /&gt;
During actual development the specification was changed a little.&lt;br /&gt;
* table course_sections will not have additional fields &#039;parent&#039; and &#039;visibilityold&#039;. If course format wants to support hierarchy, they should be defined as additional format options. There will be no performance change if we add those fields to the course_sections table because the information about all sections is cached anyway in course.sectioncache and should be accessed only using get_fast_modinfo()&lt;br /&gt;
* there is no need in trigger &#039;course_format_changed&#039; because there is already a trigger &#039;course_updated&#039; and before it the format_base::update_course_format_options() is called with the before-update value of $course object&lt;br /&gt;
* field course.format has the length 21 because if format wants to define it&#039;s own tables the table name should start with &#039;format_FORMATNAME&#039; and at the same time it&#039;s name should not be longer than 28 symbols. Therefore there is 21 char left for FORMATNAME&lt;br /&gt;
* function format_base::is_setup_completed() is not necessary because this functionality can be done in page_set_course() hook.&lt;br /&gt;
&lt;br /&gt;
==Wishlist==&lt;br /&gt;
&lt;br /&gt;
* Add field course_module.sortorder and deprecate course_section.sequence (not in 2.4!)&lt;br /&gt;
&lt;br /&gt;
==Class course (for discussion, NOT for 2.4)==&lt;br /&gt;
There are number of course-related functions in the core with random names. The argument name/property &#039;section&#039; is used for both {section}.id and {section}.section, sometimes even the same variable changes it&#039;s meaning. Some very important functions completely miss phpdocs.&lt;br /&gt;
&lt;br /&gt;
Besides it will be more understandable for developers of modules and other plugins to relate to course object and not to course format (for example to get section name, etc.)&lt;br /&gt;
&lt;br /&gt;
The proposal is:&lt;br /&gt;
* move code of all those functions into new class &#039;course&#039;&lt;br /&gt;
* leave the functions themselves in course/deprecatedlib.php with developer warning and call to corresponding function from course class&lt;br /&gt;
* update usage of those functions in the core&lt;br /&gt;
* add function get_course() or course_get_course() to retrieve the cached instance of the course, make sure cache is emptied inside rebuid_course_cache()&lt;br /&gt;
* try to replace multiple similar queries $DB-&amp;gt;get_record(&#039;course&#039;, array(&#039;id&#039;=&amp;gt;$courseid)) in the core code&lt;br /&gt;
&lt;br /&gt;
Source of course/lib.php: https://github.com/moodle/moodle/blob/master/course/lib.php&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Existing function&lt;br /&gt;
! New function&lt;br /&gt;
|-&lt;br /&gt;
| get_fast_modinfo()&lt;br /&gt;
| ? course::get_modinfo()&lt;br /&gt;
|-&lt;br /&gt;
| get_array_of_activities()&lt;br /&gt;
| course::get_array_of_activities() [used only by get_fast_modinfo()]&lt;br /&gt;
|-&lt;br /&gt;
| course_set_marker()&lt;br /&gt;
| course::set_marker()&lt;br /&gt;
|-&lt;br /&gt;
| set_section_visible&lt;br /&gt;
| course::set_section_visible()&lt;br /&gt;
|-&lt;br /&gt;
| add_course_module()&lt;br /&gt;
| course::add_cm_raw() This function only creates an entry in table {course_module}&lt;br /&gt;
|-&lt;br /&gt;
| course_add_cm_to_section()&lt;br /&gt;
| course::cm_add_to_section()&lt;br /&gt;
|-&lt;br /&gt;
| -&lt;br /&gt;
| course::add_cm() calls previous two functions and rebuild_course_cache()&lt;br /&gt;
|-&lt;br /&gt;
| get_course_section()&lt;br /&gt;
| course::get_or_create_section() will return section_info&lt;br /&gt;
|-&lt;br /&gt;
| -&lt;br /&gt;
| course::get_section() will return section_info, alias to get_fast_modinfo()-&amp;gt;get_section_info()&lt;br /&gt;
|-&lt;br /&gt;
| -&lt;br /&gt;
| course::get_sections() alias to get_fast_modinfo()-&amp;gt;get_section_info_all()&lt;br /&gt;
|-&lt;br /&gt;
| set_coursemodule_groupmode()&lt;br /&gt;
| course::set_cm_groupmode()&lt;br /&gt;
|-&lt;br /&gt;
| set_coursemodule_idnumber()&lt;br /&gt;
| course::set_cm_idnumber()&lt;br /&gt;
|-&lt;br /&gt;
| set_coursemodule_visible()&lt;br /&gt;
| course::set_cm_visible()&lt;br /&gt;
|-&lt;br /&gt;
| delete_course_module()&lt;br /&gt;
| course::delete_cm_raw()&lt;br /&gt;
|-&lt;br /&gt;
| delete_mod_from_section()&lt;br /&gt;
| course::delete_cm_from_section()&lt;br /&gt;
|-&lt;br /&gt;
| -&lt;br /&gt;
| course::delete_cm() will call both previous functions&lt;br /&gt;
|-&lt;br /&gt;
| move_section_to()&lt;br /&gt;
| course::move_section(), also call rebuild_course_cache()&lt;br /&gt;
|-&lt;br /&gt;
| reorder_sections()&lt;br /&gt;
| course::reorder_sections(), also call rebuild_course_cache()&lt;br /&gt;
|-&lt;br /&gt;
| moveto_module()&lt;br /&gt;
| course::move_cm()&lt;br /&gt;
|-&lt;br /&gt;
| course_format_name()&lt;br /&gt;
| course::get_formatted_name()&lt;br /&gt;
|-&lt;br /&gt;
| get_section_name()&lt;br /&gt;
| format_base::get_section_name() with aliases: course::get_section_name() and section_info::get_name() &lt;br /&gt;
|-&lt;br /&gt;
| can_delete_course()&lt;br /&gt;
| course::can_delete()&lt;br /&gt;
|-&lt;br /&gt;
| create_course()&lt;br /&gt;
| course::create() [static]&lt;br /&gt;
|-&lt;br /&gt;
| update_course()&lt;br /&gt;
| course::update()&lt;br /&gt;
|-&lt;br /&gt;
| course_ajax_enabled()&lt;br /&gt;
| course::is_ajax_enabled()&lt;br /&gt;
|-&lt;br /&gt;
| include_course_ajax()&lt;br /&gt;
| course::include_ajax()&lt;br /&gt;
|-&lt;br /&gt;
| course_get_url()&lt;br /&gt;
| course::get_view_url()&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Other course-related functions in course/lib.php&lt;br /&gt;
&lt;br /&gt;
* get_print_section_cm_text()&lt;br /&gt;
* print_section()&lt;br /&gt;
* print_section_add_menus()&lt;br /&gt;
* get_module_metadata()&lt;br /&gt;
* print_course&lt;br /&gt;
* make_editing_buttons()&lt;br /&gt;
* course_allowed_module()&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Behat_integration&amp;diff=36099</id>
		<title>Behat integration</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Behat_integration&amp;diff=36099"/>
		<updated>2012-11-07T02:28:35Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Objective */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
This page describes the internals of Behat and the integration with Moodle. For the functional description see https://docs.moodle.org/dev/Acceptance_testing.&lt;br /&gt;
&lt;br /&gt;
Behat is a framework for behaviour driven development (BDD) allows us to specify Moodle functionalities (aka features) as a human-readable list of steps and parse this steps to execute actions to simulate user interaction, it executes the actions against a headless browsers (without javascript support, only curl-kind petitions) or automation tools like Selenium, which interacts with browsers and allows Javascript events simulation.&lt;br /&gt;
&lt;br /&gt;
(This is not in standard Moodle yet: to play a bit with it now clone https://github.com/moodlehq/moodle-behat-features and follow the installation and usage instructions, you may need to hack a bit to pass all the tests, it depends on you Moodle site contens. Is an example of tool usage but not an example of good practices to create tests and specify fixtures)&lt;br /&gt;
&lt;br /&gt;
== Objective ==&lt;br /&gt;
The aim of this integration is to allow Moodle components to have its own set of features and steps definitions, this allows BDD in Moodle and allows us to execute periodically the whole set of tests to detect regressions and test the Moodle features in different environments (browsers, DBs engines, web servers...). &lt;br /&gt;
&lt;br /&gt;
== How Behat works ==&lt;br /&gt;
This section aims to explain the basics about BDD and Behat and a quick view of how Behat internally works from the CLI command execution to the results output.&lt;br /&gt;
&lt;br /&gt;
Some terms used:&lt;br /&gt;
* &#039;&#039;&#039;Features&#039;&#039;&#039;: Human-readable list of scenarios that describes a feature&lt;br /&gt;
&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;
* &#039;&#039;&#039;Scenario&#039;&#039;&#039;: Human-readable list of steps to describe an expected behaviour&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;
* &#039;&#039;&#039;Steps&#039;&#039;&#039;: Human-readable sentences that describes an action. There are 3 types of steps, &amp;quot;Given&amp;quot; describing the initial context, &amp;quot;When&amp;quot; the event that provokes a change and &amp;quot;Then&amp;quot; where the outcomes should be asserted.&lt;br /&gt;
  I click on the &amp;quot;Add user&amp;quot; button&lt;br /&gt;
* &#039;&#039;&#039;Steps definitions&#039;&#039;&#039;: PHP methods referenced by steps when matching it&#039;s regular expression. The @Given, @When and @Then tags are descriptive and they are not taken into account when matching steps with steps definitions. The regular expressions placeholders are returned to the PHP method as arguments so methods can use them to tell the browser which button (for example) they want to click.&lt;br /&gt;
  /**&lt;br /&gt;
   * @When /^I click on the &amp;quot;(.*)&amp;quot; button$/&lt;br /&gt;
   */&lt;br /&gt;
  public function i_click_on_the_button($button) {&lt;br /&gt;
    // Simulates the user interaction (see Mink description below for more info)&lt;br /&gt;
    $this-&amp;gt;getSession()-&amp;gt;getPage()-&amp;gt;pressButton($button);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
* &#039;&#039;&#039;Behat&#039;&#039;&#039;: PHP framework and CLI application that wraps the whole process of features files loading + features files parsing + execution of actions in the browser + results output (http://behat.org/)&lt;br /&gt;
* &#039;&#039;&#039;Gherkin&#039;&#039;&#039;: Human-readable language used to define features that can be parsed and translated into PHP methods. For more info, it&#039;s the same language used by Cucumber, the BDD Ruby framework (https://github.com/cucumber/cucumber/wiki/Gherkin)&lt;br /&gt;
* &#039;&#039;&#039;Context&#039;&#039;&#039;: In Behat scope a context is a PHP class that groups steps definitions (as methods)&lt;br /&gt;
* &#039;&#039;&#039;Mink&#039;&#039;&#039;: Is the component which interacts with browsers, simulating a real user interaction. It allows us to write PHP code (or use the available PHP methods) to send petitions to the different browsers APIs through a common interface or extend it to allow browser-specific actions. The supported browsers includes Selenium, Selenium2, Sahi... http://mink.behat.org/&lt;br /&gt;
* &#039;&#039;&#039;Selenium 2&#039;&#039;&#039;: Web browser automation tool, applications like Mink can communicate with it through a RESTful API (http://code.google.com/p/selenium/wiki/JsonWireProtocol) to execute actions simulating user interaction.&lt;br /&gt;
&lt;br /&gt;
All this components are written in PHP, open sourced and packaged in a single and extensible framework.&lt;br /&gt;
&lt;br /&gt;
=== Quick view of the whole process ===&lt;br /&gt;
&lt;br /&gt;
# Behat CLI execution&lt;br /&gt;
#* Behat application initialization and loading of arguments (features files to execute, output format...)&lt;br /&gt;
#* Reads the Behat config file (browser servers are specified here)&lt;br /&gt;
#* Extensions overrides management&lt;br /&gt;
#* Gherkin initialization&lt;br /&gt;
# Features files selection&lt;br /&gt;
#* According to the arguments Gherkin looks for .features files&lt;br /&gt;
#** It can use different features loaders (single file, a directory, the default directory...)&lt;br /&gt;
#** The framework can be extended to allow multiple folders loading&lt;br /&gt;
# Features parsing (Gherkin)&lt;br /&gt;
#* Loops through the loaded features files looking for scenarios&lt;br /&gt;
#* Gets the list of steps of each scenario&lt;br /&gt;
#* There are hooks at different levels (http://docs.behat.org/guides/3.hooks.html)&lt;br /&gt;
# Steps parsing (Gherkin)&lt;br /&gt;
#* Gherkin looks in the available steps definitions for a regular expression that matches the step text&lt;br /&gt;
# Step definition execution&lt;br /&gt;
#* The step definition code is executed&lt;br /&gt;
#* Steps definitions most of the time uses the Mink component to communicate with the browser API sending petitions like &amp;quot;click on that button&amp;quot; or &amp;quot;go to XXX page&amp;quot;&lt;br /&gt;
# Scenario outcomes&lt;br /&gt;
#* The scenario counts as failed if an exception is thrown when executing a step definition (for example trying to click a non-existing button)&lt;br /&gt;
#* The scenario counts as passed if no exception is thrown during it&#039;s steps execution&lt;br /&gt;
# Finishing CLI execution&lt;br /&gt;
#* A summary with all the scenario results is displayed&lt;br /&gt;
#* It accepts different output formats (like JUnitXML) to it&#039;s execution in continuous integration systems (http://docs.behat.org/guides/6.cli.html#format-options)&lt;br /&gt;
&lt;br /&gt;
== Moodle integration ==&lt;br /&gt;
&lt;br /&gt;
It follows the approach chosen with PHPUnit:&lt;br /&gt;
* Moodle components (subsystems and plugins) can have a tests/behat/ folder&lt;br /&gt;
* The behat project is not included in Moodle codebase and has to be installed separately (https://github.com/dmonllao/behat-moodle/blob/master/README.md)&lt;br /&gt;
* The scenarios are executed in a test environment (the same test environment used to execute PHPUnit tests)&lt;br /&gt;
* The scenarios specifies their own fixtures and it&#039;s execution is isolated from other scenarios and features&lt;br /&gt;
* Moodle lists the features files and steps definitions of it&#039;s components in a config.yml file, similar to the phpunit.xml manifest, that is read by Behat&lt;br /&gt;
&lt;br /&gt;
This tests can not be executed in production sites; before executing the tests the whole site switches to &amp;quot;test environment&amp;quot; and all the logged users are kicked out. This is required because Behat runs black box tests simulating user interaction, it accesses via URL to the site like an user does.&lt;br /&gt;
&lt;br /&gt;
=== Admin tool &amp;quot;Acceptance testing&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
There is an admin tool in admin/tool/behat/index.php (https://github.com/dmonllao/moodle/tree/MDL-35611_master/admin/tool/behat) to run and ease the creation of acceptance tests. All the features listed below can be executed through CLI and web although it&#039;s better to run the tests with CLI. You can:&lt;br /&gt;
# List the available steps (with an option to filter by keyword)&lt;br /&gt;
# Run the tests (with an option to filter by tag or to add extra behat command options) it also updates the config.yml file&lt;br /&gt;
* Also you can switch the real and test environments (uses $CFG-&amp;gt;phpunit_prefix and $CFG-&amp;gt;phpunit_dataroot instead of the regular $CFG-&amp;gt;prefix and $CFG-&amp;gt;dataroot) if you need to check something&lt;br /&gt;
&lt;br /&gt;
=== Behat extension ===&lt;br /&gt;
The Behat framework is extended to load features and steps definitions from multiple folders (subsystems and plugins).&lt;br /&gt;
&lt;br /&gt;
The basic Behat framework only allows one folder to look for features, we can set $CFG-&amp;gt;dirroot as a single features project, but Behat will have a huge amount of work to do every time is executed to find where the features classes are. The basic framework has been extended to read the Moodle config.yml file, and with it&#039;s data, to load the features and steps definitions files spread along different Moodle components. (https://github.com/dmonllao/moodle-behat-extension)&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Activity_modules&amp;diff=36098</id>
		<title>Activity modules</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Activity_modules&amp;diff=36098"/>
		<updated>2012-11-07T02:27:41Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* view.php */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Activity modules reside in the &#039;&#039;&#039;/mod&#039;&#039;&#039; directory.&lt;br /&gt;
&lt;br /&gt;
Each module is in a separate subdirectory and consists of a number of &#039;&#039;&#039;&#039;mandatory files&#039;&#039;&#039;&#039; and any other files the developer is going to use. The below image is an example of the certificate module&#039;s filestructure. Please note, any reference to &amp;lt;modname&amp;gt; in this documentation should be replaced by the name of your module.&lt;br /&gt;
&lt;br /&gt;
Below is an example of the file structure for the certificate plugin.&lt;br /&gt;
&lt;br /&gt;
[[File:Activities_file_structure_example.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Standard Files and their Functions ==&lt;br /&gt;
&lt;br /&gt;
There are several files that are crucial to Moodle. These files are used to install your module and then integrate it into the Moodle system. Each file has a particular function, some of the files are not necessary and are only created when wanting to use the functionality it offers. Below are the list of most commonly used files.&lt;br /&gt;
&lt;br /&gt;
=== Backup Folder ===&lt;br /&gt;
&lt;br /&gt;
This is the location to place the files that define how your module is going to behave when a course backup or restore is performed. You may have added a lot of information to the database for a specific module that you do not wish to enter again when restoring a course, this is where you define what should be saved during a backup and how to then restore this information from the Moodle backup.&lt;br /&gt;
&lt;br /&gt;
To read more on this please visit [[Backup_2.0_for_developers]] and [[Restore_2.0_for_developers]].&lt;br /&gt;
&lt;br /&gt;
=== DB Folder ===&lt;br /&gt;
&lt;br /&gt;
==== access.php ====&lt;br /&gt;
&lt;br /&gt;
This is where you define what capabilities your plugin will create. Note, if you add new capabilities to this file after your plugin has been installed you will need to increase the version number in your version.php file (discussed later) in order for them to be installed.&lt;br /&gt;
&lt;br /&gt;
An example of the file is below -&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$capabilities = array(&lt;br /&gt;
&lt;br /&gt;
    &#039;mod/certificate:addinstance&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;riskbitmask&#039; =&amp;gt; RISK_XSS,&lt;br /&gt;
        &#039;captype&#039; =&amp;gt; &#039;write&#039;,&lt;br /&gt;
        &#039;contextlevel&#039; =&amp;gt; CONTEXT_COURSE,&lt;br /&gt;
        &#039;archetypes&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;editingteacher&#039; =&amp;gt; CAP_ALLOW,&lt;br /&gt;
            &#039;manager&#039; =&amp;gt; CAP_ALLOW&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;clonepermissionsfrom&#039; =&amp;gt; &#039;moodle/course:manageactivities&#039;&lt;br /&gt;
    ),&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Moodle 2.3 a new capability called &#039;addinstance&#039; was added for all core modules. Moodle will look for this capability by searching for &#039;mod/&amp;lt;modname&amp;gt;:addinstance&#039; and if it is not created then it assumed that the user does have this capability with a debug message being displayed that the capability is missing. To avoid this situation it is good to add this to your module. For further information on what each attribute in that capabilities array means visit [[NEWMODULE_Adding_capabilities]].&lt;br /&gt;
&lt;br /&gt;
==== install.xml ====&lt;br /&gt;
&lt;br /&gt;
This file is used on installation of your module. It includes the database tables and fields that your module will be creating. If your module does not require any new tables then this file is not necessary. In order to create this XML file please view [[XMLDB_editor]]. Please note, in the XML file the table names are listed without the config.php prefix, this is automatically used when creating the tables and does not need to be specified.&lt;br /&gt;
&lt;br /&gt;
An example of this file is below -&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;&lt;br /&gt;
&amp;lt;XMLDB PATH=&amp;quot;mod/certificate/db&amp;quot; VERSION=&amp;quot;20120925&amp;quot; COMMENT=&amp;quot;XMLDB file for Moodle mod/certificate&amp;quot;&lt;br /&gt;
    xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;br /&gt;
    xsi:noNamespaceSchemaLocation=&amp;quot;../../../lib/xmldb/xmldb.xsd&amp;quot;&lt;br /&gt;
&amp;gt;&lt;br /&gt;
  &amp;lt;TABLES&amp;gt;&lt;br /&gt;
    &amp;lt;TABLE NAME=&amp;quot;certificate&amp;quot; COMMENT=&amp;quot;Defines certificates&amp;quot; NEXT=&amp;quot;certificate_issues&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;FIELDS&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;id&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; SEQUENCE=&amp;quot;true&amp;quot; ENUM=&amp;quot;false&amp;quot; NEXT=&amp;quot;course&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;course&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;id&amp;quot; NEXT=&amp;quot;name&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;name&amp;quot; TYPE=&amp;quot;char&amp;quot; LENGTH=&amp;quot;255&amp;quot; NOTNULL=&amp;quot;true&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;course&amp;quot; NEXT=&amp;quot;intro&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;intro&amp;quot; TYPE=&amp;quot;text&amp;quot; LENGTH=&amp;quot;small&amp;quot; NOTNULL=&amp;quot;false&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;name&amp;quot; NEXT=&amp;quot;introformat&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;introformat&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;4&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;intro&amp;quot; NEXT=&amp;quot;emailteachers&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;emailteachers&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;1&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;introformat&amp;quot; NEXT=&amp;quot;emailothers&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;emailothers&amp;quot; TYPE=&amp;quot;text&amp;quot; LENGTH=&amp;quot;small&amp;quot; NOTNULL=&amp;quot;false&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;emailteachers&amp;quot; NEXT=&amp;quot;savecert&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;savecert&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;1&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;emailothers&amp;quot; NEXT=&amp;quot;reportcert&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;reportcert&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;1&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;savecert&amp;quot; NEXT=&amp;quot;delivery&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;delivery&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;3&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;reportcert&amp;quot; NEXT=&amp;quot;requiredtime&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;requiredtime&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;delivery&amp;quot; NEXT=&amp;quot;type&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;type&amp;quot; TYPE=&amp;quot;char&amp;quot; LENGTH=&amp;quot;50&amp;quot; NOTNULL=&amp;quot;true&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;requiredtime&amp;quot; NEXT=&amp;quot;orientation&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;orientation&amp;quot; TYPE=&amp;quot;char&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;type&amp;quot; NEXT=&amp;quot;width&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;width&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;orientation&amp;quot; NEXT=&amp;quot;height&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;height&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;width&amp;quot; NEXT=&amp;quot;backgroundimage&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;backgroundimage&amp;quot; TYPE=&amp;quot;char&amp;quot; LENGTH=&amp;quot;255&amp;quot; NOTNULL=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;height&amp;quot; NEXT=&amp;quot;timecreated&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;timecreated&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;backgroundimage&amp;quot;  NEXT=&amp;quot;timemodified&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;timemodified&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;timecreated&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/FIELDS&amp;gt;&lt;br /&gt;
      &amp;lt;KEYS&amp;gt;&lt;br /&gt;
        &amp;lt;KEY NAME=&amp;quot;primary&amp;quot; TYPE=&amp;quot;primary&amp;quot; FIELDS=&amp;quot;id&amp;quot; COMMENT=&amp;quot;Primary key for certificate&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/KEYS&amp;gt;&lt;br /&gt;
    &amp;lt;/TABLE&amp;gt;&lt;br /&gt;
    &amp;lt;TABLE NAME=&amp;quot;certificate_issues&amp;quot; COMMENT=&amp;quot;Info about issued certificates&amp;quot; PREVIOUS=&amp;quot;certificate&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;FIELDS&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;id&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; SEQUENCE=&amp;quot;true&amp;quot; ENUM=&amp;quot;false&amp;quot; NEXT=&amp;quot;userid&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;userid&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;id&amp;quot; NEXT=&amp;quot;certificateid&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;certificateid&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;userid&amp;quot; NEXT=&amp;quot;timecreated&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FIELD NAME=&amp;quot;timecreated&amp;quot; TYPE=&amp;quot;int&amp;quot; LENGTH=&amp;quot;10&amp;quot; NOTNULL=&amp;quot;true&amp;quot; UNSIGNED=&amp;quot;true&amp;quot; DEFAULT=&amp;quot;0&amp;quot; SEQUENCE=&amp;quot;false&amp;quot; ENUM=&amp;quot;false&amp;quot; PREVIOUS=&amp;quot;certificateid&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/FIELDS&amp;gt;&lt;br /&gt;
      &amp;lt;KEYS&amp;gt;&lt;br /&gt;
        &amp;lt;KEY NAME=&amp;quot;primary&amp;quot; TYPE=&amp;quot;primary&amp;quot; FIELDS=&amp;quot;id&amp;quot; COMMENT=&amp;quot;Primary key for certificate_issues&amp;quot; NEXT=&amp;quot;certificate&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;KEY NAME=&amp;quot;certificate&amp;quot; TYPE=&amp;quot;foreign&amp;quot; FIELDS=&amp;quot;certificateid&amp;quot; REFTABLE=&amp;quot;certificate&amp;quot; REFFIELDS=&amp;quot;id&amp;quot; PREVIOUS=&amp;quot;primary&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/KEYS&amp;gt;&lt;br /&gt;
    &amp;lt;/TABLE&amp;gt;&lt;br /&gt;
  &amp;lt;/TABLES&amp;gt;&lt;br /&gt;
&amp;lt;/XMLDB&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example contains the structure of the table certificate and certificate_issues. Each table, field and key lists what element is before and after it (if there are any present). This is automatically created by the [[XMLDB_editor]]. Your module should always have a table with the same name as the module with the fields id, name and course as shown above.&lt;br /&gt;
&lt;br /&gt;
==== upgrade.php ====&lt;br /&gt;
&lt;br /&gt;
This file handles upgrading the module to match the latest version. After creating a module and using it extensively on your site (and others) you may want to extend the functionality of your module. Using the certificate example, a suggestion was made that a unique code could be generated for each certificate issued and displayed if a setting was selected. This requires two new database fields, one to store whether the user has chosen to display the code on the certificate and another to store the actual code. This is where the upgrade.php script becomes used. The install.xml file is only executed once, that is when your module is first installed, so adding these two extra columns to this file does not change the database structure for users who have already installed the module. So, to perform this upgrade you need to do three things.&lt;br /&gt;
&lt;br /&gt;
1. Add the new columns to the install.xml file so that any users who install the module after this point are given the new table structure.&amp;lt;br /&amp;gt;&lt;br /&gt;
2. Add the instructions for the upgrade.php files.&amp;lt;br /&amp;gt;&lt;br /&gt;
3. Update the version number in your version.php file.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this example we are only adding two new columns to the database, so we can use the [[XMLDB_editor]] to change the install.xml file AND create the upgrade path.&lt;br /&gt;
&lt;br /&gt;
An example of the upgrade.php file is as follows -&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function xmldb_certificate_upgrade($oldversion=0) {&lt;br /&gt;
    if ($oldversion &amp;lt; 2012091800) {&lt;br /&gt;
        // Add new fields to certificate table.&lt;br /&gt;
        $table = new xmldb_table(&#039;certificate&#039;);&lt;br /&gt;
        $field = new xmldb_field(&#039;showcode&#039;);&lt;br /&gt;
        $field-&amp;gt;set_attributes(XMLDB_TYPE_INTEGER, &#039;1&#039;, XMLDB_UNSIGNED, XMLDB_NOTNULL, null, &#039;0&#039;, &#039;savecert&#039;);&lt;br /&gt;
        if (!$dbman-&amp;gt;field_exists($table, $field)) {&lt;br /&gt;
            $dbman-&amp;gt;add_field($table, $field);&lt;br /&gt;
        }&lt;br /&gt;
        // Add new fields to certificate_issues table.&lt;br /&gt;
        $table = new xmldb_table(&#039;certificate_issues&#039;);&lt;br /&gt;
        $field = new xmldb_field(&#039;code&#039;);&lt;br /&gt;
        $field-&amp;gt;set_attributes(XMLDB_TYPE_CHAR, &#039;50&#039;, null, null, null, null, &#039;certificateid&#039;);&lt;br /&gt;
        if (!$dbman-&amp;gt;field_exists($table, $field)) {&lt;br /&gt;
            $dbman-&amp;gt;add_field($table, $field);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Certificate savepoint reached.&lt;br /&gt;
        upgrade_mod_savepoint(true, 2012091800, &#039;certificate&#039;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Whenever you change the version in your version.php module Moodle will look to see if anything needs to be done. The version that is stored in the database is passed to the xmldb_&amp;lt;modname&amp;gt;_upgrade function as the variable $oldversion in this case. In this example lets say that the initial version was 2012091600, since this is less than 2012091800 (the new value we put in the version.php file) we will execute the code in the if statement which will then update the version stored in the database meaning this if statement is never executed again. For more extensive details on this please see [[Upgrade_API]].&lt;br /&gt;
&lt;br /&gt;
=== Lang Folder ===&lt;br /&gt;
&lt;br /&gt;
This is where you store any strings you are going to use in your plugin. Each language has a specific folder that needs to be created in order for it to be used with your module. In this case we are going to use the English language (since unfortunately it is the only one I know). A folder called en is created in your lang folder that contains a file called &amp;lt;modname&amp;gt;.php that lists the translations of your string. For example, your module may have a setting called &#039;User preferences&#039;, rather than hard coding this term in your form, you use a placeholder that will then retrieve the appropriate string depending on the language being used on your Moodle site. There is one mandatory placeholder for modules called &#039;pluginname&#039; that Moodle will use when listing this module as an option to add to a course and other various pages. To keep with Moodle standards you should order your strings alphabetically by the placeholder name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;pluginname&#039;] = &#039;Certificate&#039;;&lt;br /&gt;
$string[&#039;userpreferences&#039;] = &#039;User preferences&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you wanted to add the French translation you would create a folder called fr and add the file &amp;lt;modname&amp;gt;.php to it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;pluginname&#039;] = &#039;Certificat&#039;;&lt;br /&gt;
$string[&#039;userpreferences&#039;] = &#039;Préférences d\&#039;utilisateur&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, when using the string &#039;User preferences&#039; we will use the Moodle function get_string instead, which will get the appropriate string depending on the language being used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;userpreferences&#039;, &#039;&amp;lt;modname&amp;gt;&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Pix folder ===&lt;br /&gt;
&lt;br /&gt;
Here you simply store the icon you wish Moodle to display next to the name of your module. The name of the file should be icon.gif and is displayed with the resolution of 64 * 64. Feel free to use this folder to store other pictures you may use.&lt;br /&gt;
&lt;br /&gt;
=== lib.php ===&lt;br /&gt;
&lt;br /&gt;
The list of functions that are possible to override in the lib.php are listed here [[NEWMODULE_Documentation#lib.php]]. The essential ones are listed below&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function certificate_add_instance($certificate);&lt;br /&gt;
function certificate_update_instance($certificate);&lt;br /&gt;
function certificate_delete_instance($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;modname&amp;gt;_add_instance function is passed the variables from the mod_form.php file (discussed later) as an object when you first create an activity and click submit. This is where you can take that data, do what you want with it and then insert it into the database if you wish. This is only called once when the module instance is first created, so this is where you should place the logic to add the activity.&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
The &amp;lt;modname&amp;gt;_update_instance function is passed the variables from the mod_form.php file as an object whenever you update an activity and click submit. The id of the instance you are editing is passed as the attribute instance and can be used to edit any existing values in the database for that instance.&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
The &amp;lt;modname&amp;gt;_delete_instance function is passed the id of your module which you can use to delete the records from any database tables associated with that id. For example, in the certificate module the id in the certificate table is passed, and then used to delete the certificate from the database, any issues of this certificate and any files associated with it on the filesystem.&lt;br /&gt;
&lt;br /&gt;
=== mod_form.php ===&lt;br /&gt;
&lt;br /&gt;
This file is used when adding/editing a module to a course. It contains the elements that will be displayed on the form responsible for creating/installing an instance of your module. The class in the file should be called mod_&amp;lt;modname&amp;gt;_mod_form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (!defined(&#039;MOODLE_INTERNAL&#039;)) {&lt;br /&gt;
    die(&#039;Direct access to this script is forbidden.&#039;);    ///  It must be included from a Moodle page&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
require_once($CFG-&amp;gt;dirroot.&#039;/course/moodleform_mod.php&#039;);&lt;br /&gt;
require_once($CFG-&amp;gt;dirroot.&#039;/mod/certificate/lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
class mod_certificate_mod_form extends moodleform_mod {&lt;br /&gt;
&lt;br /&gt;
    function definition() {&lt;br /&gt;
        global $CFG, $DB, $OUTPUT;&lt;br /&gt;
&lt;br /&gt;
        $mform =&amp;amp; $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;name&#039;, get_string(&#039;certificatename&#039;, &#039;certificate&#039;), array(&#039;size&#039;=&amp;gt;&#039;64&#039;));&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;name&#039;, PARAM_TEXT);&lt;br /&gt;
        $mform-&amp;gt;addRule(&#039;name&#039;, null, &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
&lt;br /&gt;
        $ynoptions = array(0 =&amp;gt; get_string(&#039;no&#039;),&lt;br /&gt;
                           1 =&amp;gt; get_string(&#039;yes&#039;));&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;select&#039;, &#039;usecode&#039;, get_string(&#039;usecode&#039;, &#039;certificate&#039;), $ynoptions);&lt;br /&gt;
        $mform-&amp;gt;setDefault(&#039;usecode&#039;, 0);&lt;br /&gt;
        $mform-&amp;gt;addHelpButton(&#039;usecode&#039;, &#039;usecode&#039;, &#039;certificate&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;standard_coursemodule_elements();&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example does not contain the full file, just enough to provide you with an idea. First we create a text element called &#039;name&#039; that is required, this is obviously the name of the instance. I then created another element that stores whether a user wishes to display the unique code issued to a user when they receive the certificate or not with a default value of 0 and a help button explaining what this setting does. The function standard_coursemodule_elements adds the elements common to all modules, such as the conditional fields. The add_action_buttons function adds the submit and cancel buttons to the form. This data will be passed to either &amp;lt;modname&amp;gt;_add_instance or &amp;lt;modname&amp;gt;_update_instance depending on whether you are adding a new instance or updating a current one. You can also add validation to this form, just like any other form in Moodle. For more information on how to create forms in Moodle see [[Form_API]].&lt;br /&gt;
&lt;br /&gt;
=== index.php ===&lt;br /&gt;
&lt;br /&gt;
This page is used by Moodle when listing all the instances of your module that are in a particular course with the course id being passed to this script. The beginning of the page should contain the following -&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require_once(&#039;../../config.php&#039;);&lt;br /&gt;
&lt;br /&gt;
$id = required_param(&#039;id&#039;, PARAM_INT);           // Course ID&lt;br /&gt;
&lt;br /&gt;
// Ensure that the course specified is valid&lt;br /&gt;
if (!$course = $DB-&amp;gt;get_record(&#039;course&#039;, array(&#039;id&#039;=&amp;gt; $id))) {&lt;br /&gt;
    print_error(&#039;Course ID is incorrect&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You are then free to display the list of instances as you wish.&lt;br /&gt;
&lt;br /&gt;
=== view.php ===&lt;br /&gt;
&lt;br /&gt;
When a course renders its page layout and activities it generates the links to view them using the view.php script, so the links will look like &amp;lt;wwwrootofyoursite&amp;gt;/mod/&amp;lt;modname&amp;gt;/view.php?id=4, where 4 is the course module id. For the certificate example the beginning of the view.php page looks like the following -&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require_once(&#039;../../config.php&#039;);&lt;br /&gt;
require_once(&#039;lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
$id = required_param(&#039;id&#039;, PARAM_INT);    // Course Module ID&lt;br /&gt;
&lt;br /&gt;
if (!$cm = get_coursemodule_from_id(&#039;certificate&#039;, $id)) {&lt;br /&gt;
    print_error(&#039;Course Module ID was incorrect&#039;);&lt;br /&gt;
}&lt;br /&gt;
if (!$course = $DB-&amp;gt;get_record(&#039;course&#039;, array(&#039;id&#039;=&amp;gt; $cm-&amp;gt;course))) {&lt;br /&gt;
    print_error(&#039;course is misconfigured&#039;);&lt;br /&gt;
}&lt;br /&gt;
if (!$certificate = $DB-&amp;gt;get_record(&#039;certificate&#039;, array(&#039;id&#039;=&amp;gt; $cm-&amp;gt;instance))) {&lt;br /&gt;
    print_error(&#039;course module is incorrect&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The course module id is passed to this script and set as $id, which is then used to retrieve the course data and the information from the certificate table which we can use later on as we wish.&lt;br /&gt;
&lt;br /&gt;
=== version.php ===&lt;br /&gt;
&lt;br /&gt;
The version.php file keeps track of the version of your module, and other attributes, such as what version of Moodle it requires. For a full list of the attributes please see [[version.php]].&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Git_repositories_for_contrib_modules&amp;diff=36044</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=36044"/>
		<updated>2012-11-06T01:38:17Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* 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 its 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;
&lt;br /&gt;
* [[:en:Git|Git]]&lt;br /&gt;
* [[Git tips]]&lt;br /&gt;
* [[User:Sam Hemelryk/My Moodle Git workflow]]&lt;br /&gt;
&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>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Page_API&amp;diff=36041</id>
		<title>Page API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Page_API&amp;diff=36041"/>
		<updated>2012-11-05T12:09:41Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* URL */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Page API is used to set up the current page, add JavaScript, and configure how things will be displayed to the user.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
The Page API is an integral part of any Moodle page. It allows the developer to set things up the way they envisage it. Through the Page API you can set things like the title, initial heading, where the user is for the navigation, and which layout you think the page should use.&lt;br /&gt;
&lt;br /&gt;
This document starts off with a simple example, and then proceeds to provide a more complete description of how to set up a page for display.&lt;br /&gt;
&lt;br /&gt;
==A simple example==&lt;br /&gt;
This example covers how to set up a basic page for use within an activity plugin and is undoubtedly the simplest example as much of the work is done behind the scenes for you.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// File: /mod/mymodulename/view.php&lt;br /&gt;
require_once(&#039;../../config.php&#039;);&lt;br /&gt;
$cmid = required_param(&#039;id&#039;, PARAM_INT);&lt;br /&gt;
$cm = get_coursemodule_from_id(&#039;mymodulename&#039;, $cmid, 0, false, MUST_EXIST);&lt;br /&gt;
$course = $DB-&amp;gt;get_record(&#039;course&#039;, array(&#039;id&#039; =&amp;gt; $cm-&amp;gt;course), &#039;*&#039;, MUST_EXIST);&lt;br /&gt;
&lt;br /&gt;
require_login($course, true, $cm);&lt;br /&gt;
$PAGE-&amp;gt;set_url(&#039;/mod/mymodulename/view.php&#039;, array(&#039;id&#039; =&amp;gt; $cm-&amp;gt;id));&lt;br /&gt;
$PAGE-&amp;gt;set_title(&#039;My modules page title&#039;);&lt;br /&gt;
$PAGE-&amp;gt;set_heading(&#039;My modules page heading&#039;);&lt;br /&gt;
&lt;br /&gt;
// The rest of your code goes below this.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
I&#039;m going to assume you know what the first four lines are doing, if not you are starting in the wrong place.&lt;br /&gt;
&lt;br /&gt;
So lets start at require_login and assume you already have the course and course module objects ready to use. When you call require_login part of the magic it does for you is set up the basic for the current page.&amp;lt;br /&amp;gt;&lt;br /&gt;
In the case of the example above because require_login is given a course and course module it is already setting up much of the page for you. It is giving the course and course module objects to the page, setting the context for the page to the course modules context, and setting the page layout to &#039;&#039;incourse&#039;&#039; so that you get the standard look of a course module.&lt;br /&gt;
&lt;br /&gt;
The set up that we are having to do is as follows:&lt;br /&gt;
# Set the URL for the page. This MUST be done.&lt;br /&gt;
# Set a title for the page. Most likely will be shown in the &amp;lt;title&amp;gt; tag.&lt;br /&gt;
# Set the heading for the page. Most likely used in the pages header.&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to mention that this has to be done before output starts. That means you must set up the page before the header is printed and before you instantiate any moodleform instances.&lt;br /&gt;
&lt;br /&gt;
And that is it, if you were to add a bit of simple output there you would get a page that already looks like other module pages you would have seen. Simple as.&lt;br /&gt;
&lt;br /&gt;
==$PAGE The Moodle page global==&lt;br /&gt;
For every page request Moodle sets up a couple of global structures that you will likely need. $DB the database object, and $CFG which stores configuration are two that you are likely already aware of. $PAGE is the focus of this article, it is a moodle_page instance that stores all of the information and is used by the output library $OUTPUT when displaying the page.&amp;lt;br /&amp;gt;&lt;br /&gt;
It&#039;s important to note the difference between $PAGE and $OUTPUT, $PAGE is for setting up the page and $OUTPUT is for displaying the page. $PAGE contains lots of logic and magic, $OUTPUT is purely about display and does little more than produce HTML.&lt;br /&gt;
&lt;br /&gt;
==Setting up the page==&lt;br /&gt;
When creating a page in Moodle there are a couple of things that you must set, and a couple of things that get set for you in many cases but not all of the time.&lt;br /&gt;
&lt;br /&gt;
===URL===&lt;br /&gt;
This is an absolute must, failing to set this will lead Moodle to display an error that it has not been set.&amp;lt;br /&amp;gt;&lt;br /&gt;
It can be set in the following manner:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/page/to/your/file.php&#039;, array(&#039;key&#039; =&amp;gt; &#039;value&#039;, &#039;id&#039; =&amp;gt; 3)));&lt;br /&gt;
$PAGE-&amp;gt;set_url(&#039;/page/to/your/file.php&#039;, array(&#039;key&#039; =&amp;gt; &#039;value&#039;, &#039;id&#039; =&amp;gt; 3));&lt;br /&gt;
$PAGE-&amp;gt;set_url(&#039;/page/to/your/file.php?key=value&amp;amp;id=3&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above code sets the page URL 3 times, and highlights the 3 different ways you can set the URL. Either of the first two methods are the preferred way as it provides 100% accuracy when processing the URL. Internally set_url() converts what ever you give it to a moodle_url object.&lt;br /&gt;
&lt;br /&gt;
The URL that you give to the page is going to be used by many Moodle core API&#039;s. Most importantly it is going to be used to create the navigation for your page so it&#039;s very important you set it accurately.&lt;br /&gt;
&lt;br /&gt;
===Context===&lt;br /&gt;
This is an absolute must as well, however in many cases it will be set for you magically by Moodle.&lt;br /&gt;
&lt;br /&gt;
In order to set the context for the page you must provide a context object, in Moodle 2.2 and greater this will look as follows:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Moodle 2.2 and greater&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_coursecat::instance($categoryid));&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_course::instance($courseid));&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_module::instance($moduleid));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0+, and Moodle 2.1+ the following is the equivalent code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Moodle 2.2 and greater&lt;br /&gt;
$PAGE-&amp;gt;set_context(get_system_context());&lt;br /&gt;
$PAGE-&amp;gt;set_context(get_context_instance(CONTEXT_COURSECAT, $categoryid));&lt;br /&gt;
$PAGE-&amp;gt;set_context(get_context_instance(CONTEXT_COURSE, $courseid));&lt;br /&gt;
$PAGE-&amp;gt;set_context(get_context_instance(CONTEXT_MODULE, $moduleid));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In both examples above setting different types of contexts has been illustrated however you should only ever call set_context() once with the context that is most appropriate to the page you are creating.&amp;lt;br /&amp;gt;&lt;br /&gt;
If it is a plugin then the context to use would be the context you are using for your capability checks.&lt;br /&gt;
&lt;br /&gt;
As mentioned above the other thing to be aware of is that in some circumstances this gets automatically set for you.&amp;lt;br /&amp;gt;&lt;br /&gt;
If your script calls require_login (and most scripts have to) and you are providing a course, or a module to your require login call then you will not need to call set_context().&amp;lt;br /&amp;gt;&lt;br /&gt;
This is because require_login handles it for you.&lt;br /&gt;
&lt;br /&gt;
If your script doesn&#039;t call require_login, or you don&#039;t call it with a course and/or module then you will need to manually set the context as shown.&lt;br /&gt;
&lt;br /&gt;
===Optional set up===&lt;br /&gt;
The following are optional extras you can set up against the PAGE object that you are likely to encounter throughout Moodle core, and are likely to want to use yourself.&lt;br /&gt;
====Page layout====&lt;br /&gt;
The following code sets the pages layout to the standard layout, the most generic layout in the arsenal.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;set_pagelayout(&#039;standard&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
When setting the page layout you should use the layout that is the closest match to the page you are creating. Layouts are used by themes to determine what is is shown on the page. The most prominent difference between layouts is the block regions they support. The default layout `&#039;&#039;base&#039;&#039;` for example doesn&#039;t normally have any block regions at all, where as normally `&#039;&#039;standard&#039;&#039;` has the most generic layout and several block regions.&lt;br /&gt;
&lt;br /&gt;
There are dozens of different layouts that can be, and are used throughout Moodle core that you can use within your code. For a full list of common layouts you are best too look at theme/base/config.php or refer to the list below.&lt;br /&gt;
&lt;br /&gt;
Note: It&#039;s important to know that the theme determines what layouts are available and how each looks. If you select a layout that the theme doesn&#039;t support then it will revert to the default layout while using that theme.&amp;lt;br /&amp;gt;Themes are also able to specify additional layouts, however its important to spot them and know that while they may work with one theme they are unlikely to work as you expect with other themes.&lt;br /&gt;
&lt;br /&gt;
====Base theme page layouts====&lt;br /&gt;
The following is a list of the layouts defined by the base theme. Theme designers are encouraged to make the base theme a parent of their custom theme so you can be sure that in 99% of cases these layouts will be available.&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Layout&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| base&lt;br /&gt;
| Most backwards compatible layout without the blocks. This is the layout used by default.&lt;br /&gt;
|-&lt;br /&gt;
| standard&lt;br /&gt;
| Standard layout with blocks, this is recommended for most pages with general information&lt;br /&gt;
|-&lt;br /&gt;
| course&lt;br /&gt;
| The course main page uses this layout.&lt;br /&gt;
|-&lt;br /&gt;
| coursecategory&lt;br /&gt;
| Category course listings.&lt;br /&gt;
|-&lt;br /&gt;
| incourse&lt;br /&gt;
| Used for areas within a course, typical for modules. Default page layout if $cm specified in require_login().&lt;br /&gt;
|-&lt;br /&gt;
| frontpage&lt;br /&gt;
| The site home page uses this.&lt;br /&gt;
|-&lt;br /&gt;
| admin&lt;br /&gt;
| Admin and settings pages as well as server administration scripts.&lt;br /&gt;
|-&lt;br /&gt;
| mydashboard&lt;br /&gt;
| The users dashboard.&lt;br /&gt;
|-&lt;br /&gt;
| mypublic&lt;br /&gt;
| A users public profile uses this layout.&lt;br /&gt;
|-&lt;br /&gt;
| login&lt;br /&gt;
| The login screen.&lt;br /&gt;
|-&lt;br /&gt;
| popup&lt;br /&gt;
| Pages that appear in popup windows, usually no navigation, blocks, or header.&lt;br /&gt;
|-&lt;br /&gt;
| frametop&lt;br /&gt;
| Used for the outermost content of a page constructed with frames. Usually no blocks and minimal footer.&lt;br /&gt;
|-&lt;br /&gt;
| embedded&lt;br /&gt;
| Embedded pages such as content for iframes/objects. Needs as much space as possible usually no blocks, header, or footer.&lt;br /&gt;
|-&lt;br /&gt;
| maintenance&lt;br /&gt;
| Used during upgrade, installation, and when maintenance mode is enabled.&lt;br /&gt;
|-&lt;br /&gt;
| print&lt;br /&gt;
| Gets used when printing a page. Normally just a simple header and no blocks.&lt;br /&gt;
|-&lt;br /&gt;
| redirect&lt;br /&gt;
| A special layout used during a redirect. Normally with content only.&lt;br /&gt;
|-&lt;br /&gt;
| report&lt;br /&gt;
| Used for reports within Moodle. Special layout designed to handle horizontal scrolling in a nice way.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Title====&lt;br /&gt;
Setting an appropriate title is certainly a must for any properly designed page. While it is optional it is highly recommended that you set the title.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;set_title(&#039;This is my title&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
When setting the title for the page you need to provide just the string you want to use for the title. It should be a basic string and contain no HTML. Any HTML will be stripped out as the title is used within the &amp;lt;title&amp;gt; tag in the HTML head.&lt;br /&gt;
&lt;br /&gt;
====Heading====&lt;br /&gt;
Like title it is highly recommended that you set a meaningful heading for the page, although it is optional.&amp;lt;br /&amp;gt;The heading is normally displayed at the top of the page before the rest of the content starts. However it is up to the layout defined by the theme as to where it is displayed. Not all layouts will display a heading but I encourage you to always set one even if you are using a layout that doesn&#039;t support headings. This way if you are using a theme that uses a heading on every page regardless of layout things still look consistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;set_heading(get_string(&#039;pluginname&#039;, &#039;local_myplugin&#039;), 3);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When setting a heading there is just one argument, the string to use for the heading. It should be a basic string and contain no HTML.&lt;br /&gt;
&lt;br /&gt;
=== Advanced set up ===&lt;br /&gt;
The following are advanced optional methods you can call to further set up your page. In most cases you will never need to use these.&lt;br /&gt;
&lt;br /&gt;
; set_activity_record : If you have called require_login with a course module, or you have manually set a course module on $PAGE then one other thing you may want to do is set the activity module record on $PAGE as well.&amp;lt;br /&amp;gt;This is best done when you have already fetched the activity record yourself in which case manually setting the activity record may reduce the number of queries for the page by 1.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_activity_record($activityrecord)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_blocks_editing_capability : Using this method you can set an additional capability that users must posses before being able to edit blocks on this page.&amp;lt;br /&amp;gt;By default &#039;moodle/site:manageblocks&#039; is used however there are sometimes reasons to use a different capability.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_blocks_editing_capability($strcapability)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_button : This allows you to set some HTML that will be shown in the navigation bar where the `Turn on editing` button normally lives.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_button($htmlstring)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_cacheable : By setting this to false the page will be sent with headers to prevent the client from caching the page. Defaults to true.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_cacheable(true/false)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_category_by_id : Allows you to set a category that this page is displaying. Calling this will force the $PAGE-&amp;gt;course to be set to the front page course.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_category_by_id($categoryid)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_cm : Like set page above, sometimes you need to manually set the course module for $PAGE. Again you must set the context to the context of the course module if you call this.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_cm($coursemodulerecord)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_course : This allows you to set the course the page belongs to. Normally when you call require_login the course you give it automatically gets sent to $PAGE for you.&amp;lt;br /&amp;gt;However if you don&#039;t want to require login for the course, but you need it in $PAGE then you can call set_course and provide it.&amp;lt;br /&amp;gt;Note that if you do this then you MUST use the context of the course when calling set_context().&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_course($courserecord)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_docs_path : Normally this gets automatically constructed for you, however in some circumstances you may want to manually set it.&amp;lt;br /&amp;gt;This allows you to have several pages that all point to the same docs page rather than requiring a docs page for each.&amp;lt;br /&amp;gt;The docs page link is normally shown by a theme in the footer.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_docs_path($strpath)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_focuscontrol : If you pass this method an element id when the page loads on the client focus will be shifted to the element with the corresponding id.&amp;lt;br /&amp;gt;Using this function is a REALLY bad idea in most situations because changing focus automatically in a browser is a nightmare for the vision impaired and those using screen readers.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_focuscontrol($controlid)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_headingmenu : This allows you to set some HTML that will be shown next to the pages main heading where the language select box normally lives.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_headingmenu($htmlstring)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_other_editing_capability : Can be used to set an additional capability that the user must posses before they can turn editing on for this page.&amp;lt;br /&amp;gt;This is useful if you can an editing more for your page that is more than just editing blocks.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_other_editing_capability($strcapability)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_pagetype : This gets automatically set up for by default to the path of your file e.g. mod/mymod/index.php will set up as mod-mymod-index.&amp;lt;br /&amp;gt;This is absolutely fine in 99% of cases however every now and again there is a reason to override it.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_pagetype($strpagetype)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_periodic_refresh_delay : If set a meta tag gets added to the page header causing it to refresh intermittently.&amp;lt;br /&amp;gt;This is rarely needed but can be useful if you need to automatically refresh the likes of a chat page, or news feed.&amp;lt;br /&amp;gt;Today it is not recommended to use this, but instead to create a means of getting additional content via AJAX.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_periodic_refresh_delay($intdelay)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_popup_notification_allowed : Allow or disallow popup notifications on this page. Things like messaging can cause messages to popup at the bottom of the screen sometimes.&amp;lt;br /&amp;gt;On some pages this functionality is not desired and can be stopped by calling this method and using false as the first argument.&amp;lt;br /&amp;gt;Popups are allowed by default.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_popup_notification_allowed(true/false)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; set_subpage : If context-&amp;gt;id and pagetype are not enough to uniquely identify this page and you need to include another string to make it more unique you can do it by calling this method setting a custom sub page type.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;set_subpage($strsubpage)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; add_body_class : Adds a CSS class to the body tag that will be printed by the Output API as part of the header.&amp;lt;br /&amp;gt;This is useful for adding classes to the body tag that describe the content of the page and may be required for styling the whole page, or for including indicator classes that may be useful to look for in JavaScript.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;add_body_class($strcssclass)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; add_body_classes : Adds an array of CSS classes to the body tag. Have a look at the above comment for &#039;&#039;&#039;add_body_class&#039;&#039;&#039; for more details.&lt;br /&gt;
&amp;lt;code php&amp;gt;$PAGE-&amp;gt;add_body_classes($arrayofclasses)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Getting information about the page==&lt;br /&gt;
As well of setting up the page you can of course get information back from it about the page it has been set up to display.&amp;lt;br /&amp;gt;&lt;br /&gt;
Anything you set against the page can be retrieved as can any information that was set magically for you by other methods.&lt;br /&gt;
&lt;br /&gt;
The following are the most interesting and likely useful things you can get back from the page.&lt;br /&gt;
&lt;br /&gt;
; activityrecord : The activityrecord will be the record from the database that relates to the cm that was set by require_login, or manually by your code.&amp;lt;br /&amp;gt;For example if you provided a $cm instance that related to a forum this will be a row from the forum table.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;activityrecord;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; blockmanager : This is the block manager responsible for loading the all of the blocks that will be shown on the page.&amp;lt;br /&amp;gt;For more information see the [[Blocks API]].&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;blockmanager;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; bodyid : The id that will be given to the body tag when the page is displayed.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;bodyid&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; categories : An array of all the categories the page course belongs to, starting with the immediately containing category.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;categories&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; category : The category that the page course belongs to.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;category&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; cm : The course module that has been set for the page.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;cm&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; course : The course that has been set for the page.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;course&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; devicetypeinuse : The device the user is using browse the page.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;devicetypeinuse&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; headerprinted : Is true if the page header has already been printed. &lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;headerprinted&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; heading : The page heading.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;heading&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; navbar : Gets a reference to the pages navigation bar so that you can interact with that. See the [[Navigation API]] for more information.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;navbar&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; navigation : Gets a reference to the navigation for the page. See the [[Navigation API]] for more information.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; requires : Gets the page requirements manager that handles any JavaScript and special CSS requirements for the page.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;requires&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; settingsnav : Gets the settings navigation for the page. See the [[Navigation API]] for more information.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;settingsnav&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; theme : Gets the theme that is being used for the page. Is a theme_config object.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;theme&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; title : Gets the title for the page.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;title&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; url : Gets the URL that was set for the page. Is a moodle_url object.&lt;br /&gt;
&amp;lt;code php&amp;gt;$var = $PAGE-&amp;gt;url&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FAQs==&lt;br /&gt;
&lt;br /&gt;
; I don&#039;t have any blocks on my page? : This has happened because you have not set a page layout that uses blocks OR you have set it after output has started. Once output has started you cannot change integral aspects of that page that are used for the initial output. Included is the page title, heading, url and layout.&lt;br /&gt;
&lt;br /&gt;
; I am getting a notice about not having set the page URL but I have set it? : As above you must set up the page before output starts, trying to do so will lead to notices and developer warnings about having things in the wrong order.&lt;br /&gt;
&lt;br /&gt;
; What starts output? : Output starts when either the script calls echo $OUTPUT-&amp;gt;header OR a moodleform is instantiated.&lt;br /&gt;
&lt;br /&gt;
==Related API&#039;s==&lt;br /&gt;
There are a couple of API&#039;s that are closely related to the Page API that you should be aware of as well.&lt;br /&gt;
&lt;br /&gt;
===Output API===&lt;br /&gt;
The output API is an immediate relation of the page API. The page API is about setting things up, whereas the output API is all about displaying things.&lt;br /&gt;
It&#039;s through the output API that content is actually produced, and much of the information you set up through the page is used to customise what is produced, and fill in the general blanks of any page (such as title and heading.&lt;br /&gt;
&lt;br /&gt;
See the [[Output API]] documentation for more information.&lt;br /&gt;
&lt;br /&gt;
===Page requirements API===&lt;br /&gt;
The page requirements API allows you the developer to include additional CSS, and JavaScript resources that should be included with the page, and to include JavaScript calls within the page through a variety of means.&lt;br /&gt;
Technically this API is part of the Output API mentioned above, however it deserves special mention. If you are going to be using any JavaScript or CSS within your page you will need to know about this.&lt;br /&gt;
&lt;br /&gt;
See the [[Output API]] documentation for more information on the page requirements API.&lt;br /&gt;
&lt;br /&gt;
===Navigation API===&lt;br /&gt;
The final API to mention is the navigation API. This again is integral to both the page and output API and is used to recognise the context of the content being displayed and ensure that the correct blocks and navigaiton structure are loaded for the context.&lt;br /&gt;
There is a good chance that you will encounter a need to customise the navigation early on in plugin page development and it&#039;s important to be aware of this important API.&lt;br /&gt;
&lt;br /&gt;
See the [[Navigation API]] documentation for more information on the page requirements API.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Core APIs]] : A list of all the core API&#039;s in Moodle.&lt;br /&gt;
* [[Output API]] : The Output API.&lt;br /&gt;
* [[Navigation API]] : The Navigation API.&lt;br /&gt;
* [http://moodle.org/mod/forum/view.php?id=55 General developer forum] : The place to ask question you may have about the Page API.&lt;br /&gt;
* MDL-30977 : The issue to see the Page API properly documented.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:API]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Navigation_API&amp;diff=36037</id>
		<title>Navigation API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Navigation_API&amp;diff=36037"/>
		<updated>2012-11-05T06:06:32Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: incorrect use of apostrophes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Overview==&lt;br /&gt;
The Navigation API allows for the manipulation of the navigation system used in Moodle.&lt;br /&gt;
&lt;br /&gt;
==What the navigation is==&lt;br /&gt;
It&#039;s very important to understand what the navigation is exactly within Moodle. One of the goals for Moodle 2.0 was to standardise navigation throughout Moodle and try to bring order to the structure of a Moodle site.  Navigation is available through the page object &#039;&#039;&#039;$PAGE&#039;&#039;&#039;, against which you set the heading for the page, the title, any JavaScript requirements, etc.  The navigation structure uses the information $PAGE contains to generate a navigation structure for the site.  The navigation or settings [[blocks]] are interpretations of the navigation structure Moodle creates.&lt;br /&gt;
&lt;br /&gt;
This navigation structure is available through three variables:&lt;br /&gt;
&lt;br /&gt;
; $PAGE-&amp;gt;navigation : This is the main navigation structure, it will contain items that will allow the user to browse to the other available pages.&lt;br /&gt;
; $PAGE-&amp;gt;settingsnav : This is the settings navigation structure contains items that will allow the user to edit settings.&lt;br /&gt;
; $PAGE-&amp;gt;navbar : The navbar is a special structure for page breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
==What the navigation is not==&lt;br /&gt;
The navigation is &#039;&#039;&#039;NOT&#039;&#039;&#039; the navigation block or the settings block!  These two blocks were created to display the navigation structure. The navigation block looks at &#039;&#039;$PAGE-&amp;gt;navigation&#039;&#039;, and the settings block looks at &#039;&#039;$PAGE-&amp;gt;settingsnav&#039;&#039;.  Both blocks interpret their data into an HTML structure and render it.&lt;br /&gt;
&lt;br /&gt;
# The navigation is a back-end structure that is built behind the scenes and has no immediate method of display.&lt;br /&gt;
# The navigation and settings blocks display the back-end navigation structure but add nothing to it at all.&lt;br /&gt;
::In the [[model-view-controller pattern]], $PAGE-&amp;gt;navigation, $PAGE-&amp;gt;settingsnav, and $PAGE-&amp;gt;navbar are the models, and the blocks are views.&lt;br /&gt;
&lt;br /&gt;
The navbar is just the path to the active navigation or settings item. The navbar is not displayed by a block; instead it is added into the theme&#039;s layout files and displayed by the core renderer. &lt;br /&gt;
&lt;br /&gt;
==How the navigation works==&lt;br /&gt;
&lt;br /&gt;
The main navigation structure can be accessed through &#039;&#039;&#039;$PAGE-&amp;gt;navigation&#039;&#039;&#039;.  The navigation and settings are contextual in that they will relate to the page that the user is viewing. This is determined by other $PAGE object properties:&lt;br /&gt;
* &#039;&#039;&#039;$PAGE-&amp;gt;context&#039;&#039;&#039; is a Moodle context that immediately outlines the nature of the page the user is viewing.&lt;br /&gt;
* &#039;&#039;&#039;$PAGE-&amp;gt;course&#039;&#039;&#039; is the course the user is viewing.  This is essential if the context is CONTEXT_COURSE or greater.  However, it is also useful in other contexts such as CONTEXT_USER.&lt;br /&gt;
* &#039;&#039;&#039;$PAGE-&amp;gt;cm&#039;&#039;&#039; is the course module instance.  This is essential if the context is CONTEXT_MODULE or greater.&lt;br /&gt;
* &#039;&#039;&#039;$PAGE-&amp;gt;url&#039;&#039;&#039; is used to match the active navigation item. &lt;br /&gt;
&lt;br /&gt;
Nearly every page sets $PAGE-&amp;gt;url through a call to &#039;&#039;&#039;$PAGE-&amp;gt;set_url&#039;&#039;&#039; however not many explicitly set the context, course, or cm. When you call &#039;&#039;&#039;require_login&#039;&#039;&#039; with a course or cm it automatically calls the following:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($cm) {&lt;br /&gt;
    $PAGE-&amp;gt;set_cm($cm, $course); // sets up global $COURSE&lt;br /&gt;
} else {&lt;br /&gt;
    $PAGE-&amp;gt;set_course($course);// sets up global $COURSE&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A page will only be required to explicitly set a context, course, or cm under one of these conditions:&lt;br /&gt;
# &#039;&#039;&#039;require_login&#039;&#039;&#039; is NOT being called correctly&lt;br /&gt;
# The page is using CONTEXT_SYSTEM, CONTEXT_COURSECAT, or CONTEXT_USER (call &#039;&#039;&#039;$PAGE-&amp;gt;set_context&#039;&#039;&#039; ).&lt;br /&gt;
# The page is using a course or cm but it is also using one of the above contexts (call &#039;&#039;&#039;$PAGE-&amp;gt;set_course&#039;&#039;&#039; or &#039;&#039;&#039;$PAGE-&amp;gt;set_cm&#039;&#039;&#039; ).&lt;br /&gt;
&lt;br /&gt;
The navigation structure cannot be generated before the $PAGE object is configured. It is only generated when it is first used, either when something tries to access the structure or when code tries to add to it.  The navigation is initialised in a specific order:&lt;br /&gt;
# Main navigation structure&lt;br /&gt;
# Settings navigation&lt;br /&gt;
# Navbar (does not need to be generated because of its simple contents and rendering)&lt;br /&gt;
&lt;br /&gt;
==Extending the navigation==&lt;br /&gt;
&lt;br /&gt;
===Code extension===&lt;br /&gt;
This method of extending is when the code arbitrarily extends the navigation during its execution. Extending the navigation through this means allows you to extend the navigation anywhere easily, however it will only be shown on pages where your extending code gets called (you should probably put it in a function within lib.php).&lt;br /&gt;
&lt;br /&gt;
These examples are taken from the [http://moodle.org/mod/forum/discuss.php?d=152391 General Developer Forum: Moodle 2 - how to set up breadcrumbs for a module page]. It has further information that is well worth reading.&lt;br /&gt;
====Navigation====&lt;br /&gt;
This is extending the main navigation structure.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$previewnode = $PAGE-&amp;gt;navigation-&amp;gt;add(get_string(&#039;preview&#039;), new moodle_url(&#039;/a/link/if/you/want/one.php&#039;), navigation_node::TYPE_CONTAINER);&lt;br /&gt;
$thingnode = $previewnode-&amp;gt;add(get_string(&#039;name of thing&#039;), new moodle_url(&#039;/a/link/if/you/want/one.php&#039;));&lt;br /&gt;
$thingnode-&amp;gt;make_active();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The above lines of code adds a preview node to the bottom of the navigation and then adds a thingnode to the previewnode (adding a leaf to our tree).&lt;br /&gt;
The final line of code makes the thingnode active so that the navbar finds it however if the URL you give it is the same as the url you set for the page it will automatically be marked active and you won&#039;t need this call.&lt;br /&gt;
&lt;br /&gt;
Next extending the navigation for the course.&lt;br /&gt;
For this you will need to know the course id and have called require_login($courseorid); so that the navigation is loaded for the course.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$coursenode = $PAGE-&amp;gt;navigation-&amp;gt;find($courseid, navigation_node::TYPE_COURSE);&lt;br /&gt;
$thingnode = $coursenode-&amp;gt;add(get_string(&#039;Name of thing&#039;), new moodle_url(&#039;/a/link/if/you/want/one.php&#039;));&lt;br /&gt;
$thingnode-&amp;gt;make_active();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The new bit of code here really is the first line which simply finds the course node, to do this we give it the course id and the node type in this case TYPE_COURSE.&lt;br /&gt;
What we are doing here is relying on the navigation to generate the navigation up to the course and then just adding to the course.&lt;br /&gt;
====Settings navigation====&lt;br /&gt;
Adding to the settings navigation is very similar to navigation&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$settingnode = $PAGE-&amp;gt;settingsnav-&amp;gt;add(get_string(&#039;setting&#039;), new moodle_url(&#039;/a/link/if/you/want/one.php&#039;), navigation_node::TYPE_CONTAINER);&lt;br /&gt;
$thingnode = $settingnode-&amp;gt;add(get_string(&#039;Name of thing&#039;), new moodle_url(&#039;/a/link/if/you/want/one.php&#039;));&lt;br /&gt;
$thingnode-&amp;gt;make_active();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
====Navbar====&lt;br /&gt;
This is extending the settings navigation.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;navbar-&amp;gt;ignore_active();&lt;br /&gt;
$PAGE-&amp;gt;navbar-&amp;gt;add(get_string(&#039;preview&#039;), new moodle_url(&#039;/a/link/if/you/want/one.php&#039;));&lt;br /&gt;
$PAGE-&amp;gt;navbar-&amp;gt;add(get_string(&#039;name of thing&#039;), new moodle_url(&#039;/a/link/if/you/want/one.php&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The above code tells the navbar to ignore what ever the active page was and just use what you add, at which point we add two items as shown.&lt;br /&gt;
===Plugin Callbacks===&lt;br /&gt;
These are specific functions that the navigation looks for and calls if they exist for the plugin, presently only three plugin types can extend the navigation through these call-backs.&lt;br /&gt;
====Module====&lt;br /&gt;
Modules have two call-back methods, first to extend the navigation, and second to extend the settings. These call-backs get called when ever the user is viewing a page within the module and should only extend the navigation for the module.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function {modulename}_extend_navigation(${modulename}node, $course, $module, $cm)&lt;br /&gt;
function {modulename}_extend_settings_navigation($settings, ${modulename}node)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Course Formats====&lt;br /&gt;
Course formats are able to completely redefine the way in which navigation is generated for a course, as well as this they also have several methods to ensure the navigation is generated correctly.&lt;br /&gt;
====Course Reports====&lt;br /&gt;
By default reports don&#039;t add themselves or anything else to the navigation however there is a call-back that can be implemented to allow them to do so.&lt;br /&gt;
&lt;br /&gt;
==FAQ&#039;s and troubleshooting==&lt;br /&gt;
&#039;&#039;&#039;Q.&#039;&#039;&#039; My page is on the navigation but it doesn&#039;t find it?&lt;br /&gt;
&lt;br /&gt;
The first thing to do here is check the URL you are setting for the page. It should match the URL your page has within the navigation. If it doesn&#039;t you have two options, first change your &#039;&#039;&#039;$PAGE-&amp;gt;set_url&#039;&#039;&#039; call, or second override the URL the navigation is using to find the active node as shown below:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
navigation_node::override_active_url(new moodle_url(&#039;/your/url/here.php&#039;, array(&#039;param&#039;=&amp;gt;&#039;value&#039;)));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Core APIs]]&lt;br /&gt;
&lt;br /&gt;
[[Category:API]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Moodle_architecture&amp;diff=36036</id>
		<title>Moodle architecture</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Moodle_architecture&amp;diff=36036"/>
		<updated>2012-11-05T05:44:19Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Minor typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The aim of this document is to quickly give an overview of how the Moodle works at a technical level. Hopefully this will be helpful to Administrators and Developers. However, to actually do Moodle development you will have to study the code in much more detail than is provided here. See the [[Developer_documentation|developer documentation]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==What is Moodle?==&lt;br /&gt;
&lt;br /&gt;
Moodle is a Learning Management System, Course Management System, or Virtual Learning Environment, depending on which term you prefer. Its goal is to give teachers and students the tools they need to teach and learn. Moodle comes from a background of [[:en:Pedagogy|Social Constructionist pedagogy]], however, it can be used to support any style of teaching and learning.&lt;br /&gt;
&lt;br /&gt;
There are other types of software systems that are important for educational institutions, for example ePortfolios, Student Information Systems and Content repositories. Generally, Moodle does not try to re-invent these areas of functionality. Instead, tries to be the best LMS possible, and then interoperate gracefully with other systems that provide the other areas of functionality. It is, however, perfectly possible to use Moodle as a stand-alone system, without integrating it with anything else.&lt;br /&gt;
&lt;br /&gt;
Moodle is a web application written in PHP. Moodle is open source. Copyright is owned by individual contributors, not assigned to a single entity, although the company Moodle Pty Ltd in Perth Australia, owned by Moodle&#039;s founder Martin Dougiamas, manages the project.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview of a Moodle installation==&lt;br /&gt;
&lt;br /&gt;
A Moodle installation comprises the Moodle code executing in a PHP-capable web server; a database managed by MySQL, PostgreSQL, Microsoft SQL Server, or Oracle; and a file store for uploaded and generated files (the moodledata folder).&lt;br /&gt;
&lt;br /&gt;
All three parts can run on a single server; or they can be separated with many load-balanced web-servers, a database cluster, and a file-server; or anywhere between those extremes.&lt;br /&gt;
&lt;br /&gt;
Moodle is designed to be simple to install on any server that meets these basic requirements. Moodle self-installs once the code has been copied to the web server and a blank database created (see [[:en:Installing Moodle]]). Similarly, Moodle can always self-upgrade from one version to the next (see [[:en:Upgrading]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Moodle as a modular system==&lt;br /&gt;
&lt;br /&gt;
Like many successful open source systems, Moodle is structured as an application core, surrounded by numerous plugins to provide specific functionality. Moodle is designed to be highly extensible and customizable without modifying the core libraries, as doing so would create problems when upgrading Moodle to a newer version. So when customizing or extending your own Moodle install, always do so through the plugin architecture. &lt;br /&gt;
&lt;br /&gt;
Plugins in Moodle are of specific types. That is, an authentication plugin and an activity module will communicate with Moodle core using different APIs, tailored to the type of functionality the plugin provides. Functionality common to all plugins (installation, upgrade, permissions, configuration, ...) are, however, handled consistently across all plugin types.&lt;br /&gt;
&lt;br /&gt;
The standard Moodle distribution includes Moodle core and a number of plugins of each type, so that a new Moodle installation can immediately be used to start teaching and learning. After installation a Moodle site can be adapted for a particular purpose by changing the default configuration option, and by installing and removing plugins. Most plugins that have been shared publicly are listed in the [http://moodle.org/plugins/index.php Moodle Plugins Directory].&lt;br /&gt;
&lt;br /&gt;
Physically, a Moodle plugin is just a folder of PHP scripts (and CSS, JavaScript, etc. if necessary). Moodle core communicates with the plugin by looking for particular entry points, often defined in the file &amp;lt;tt&amp;gt;lib.php&amp;lt;/tt&amp;gt; within the plugin.&lt;br /&gt;
&lt;br /&gt;
==An overview of Moodle core==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Apologies, this section is not complete yet. If you have the knowledge and time, please add to it.--[[User:Tim Hunt|Tim Hunt]] 18:28, 8 December 2010 (UTC)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Moodle core provides all the infrastructure necessary to build an LMS. It implements the key concepts that all the different plugins will need to work with. These include&lt;br /&gt;
&lt;br /&gt;
===Courses and activities===&lt;br /&gt;
&lt;br /&gt;
A Moodle course is a sequence of activities and resources grouped into sections. Courses themselves are organised into a hierarchical set of categories within a Moodle site.&lt;br /&gt;
&lt;br /&gt;
===Users===&lt;br /&gt;
&lt;br /&gt;
Users, profile, my moodle ...&lt;br /&gt;
&lt;br /&gt;
===Groups and cohorts===&lt;br /&gt;
&lt;br /&gt;
===Enrolments and access control===&lt;br /&gt;
&lt;br /&gt;
Contexts, roles, capabilities, and permissions, ...&lt;br /&gt;
&lt;br /&gt;
===Activity and course completion===&lt;br /&gt;
&lt;br /&gt;
===Navigation, settings and configuration===&lt;br /&gt;
&lt;br /&gt;
===Forms library===&lt;br /&gt;
&lt;br /&gt;
===JavaScript library===&lt;br /&gt;
&lt;br /&gt;
Moodle has adopted the [http://developer.yahoo.com/yui/3/ Yahoo User Interface library]. There is also a nice system for loading the additional JavaScript files required by each page.&lt;br /&gt;
&lt;br /&gt;
===Installation and upgrade===&lt;br /&gt;
&lt;br /&gt;
===Logs and statistics===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==The most important plugin types==&lt;br /&gt;
&lt;br /&gt;
===Activities and resources===&lt;br /&gt;
&lt;br /&gt;
Activities and resources are the most basic individual components that make up a course and are the main tools for teaching and learning. Some examples of resources are: pages, links and IMS content packages. Examples of activities include: forums, wikis, quizzes, and assignments.&lt;br /&gt;
&lt;br /&gt;
Activities are by far the largest type of plugin in terms of amount of code. A forum or wiki system could be a software project in its own right. Both activities and resources are installed in the &amp;lt;tt&amp;gt;mod&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
===Blocks===&lt;br /&gt;
&lt;br /&gt;
Blocks are small bits of interface functionality that can be added to (normally the sides of) pages. Many blocks provide additional views of data stored and modified elsewhere. Blocks live in the &amp;lt;tt&amp;gt;blocks&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
===Themes===&lt;br /&gt;
&lt;br /&gt;
The overall visual style of a Moodle site, a particular course, or all courses in a category, can be changed by selecting a different theme at these different levels. Themes are the standard way of making aesthetic changes to your Moodle application, decoupling presentation from content and functionality.&lt;br /&gt;
&lt;br /&gt;
Themes live in the &amp;lt;tt&amp;gt;theme&amp;lt;/tt&amp;gt; folder, and published themes are listed in the [http://moodle.org/mod/data/view.php?id=6552 Themes database].&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
&lt;br /&gt;
Moodle is internationalised. That is, you can get language packs for many different languages. Language packs are normally downloaded and installed via the Moodle administration screens, but they can also be downloaded manually from [http://download.moodle.org/langpack/ Moodle.org: language packs].&lt;br /&gt;
&lt;br /&gt;
Administrators can also manually change any of the standard user interface strings if the terms used in the installed language pack are not appropriate.&lt;br /&gt;
&lt;br /&gt;
===Course formats===&lt;br /&gt;
&lt;br /&gt;
control how the structure of the course, a sequence of activities grouped into sections, is presented to the users. Course formats live in the &amp;lt;tt&amp;gt;course/format&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
===Authentication plugins===&lt;br /&gt;
&lt;br /&gt;
control how users log in. Moodle can manage usernames and passwords itself, or use those stored in LDAP or another database. Alternatively, Moodle can use a number of single-sign-on schemes. Authentication plugins live in the &amp;lt;tt&amp;gt;auth&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
===Enrolment plugins===&lt;br /&gt;
&lt;br /&gt;
control which users are enrolled in which courses. Again this can be by synchronising with another system, perhaps a student information system, or it can be tracked internally by Moodle. Enrolment plugins live in the &amp;lt;tt&amp;gt;enrol&amp;lt;/tt&amp;gt; folder. (Moodle was created by an Australian, so enrol is the correct spelling ;-).)&lt;br /&gt;
&lt;br /&gt;
===Repository plugins===&lt;br /&gt;
&lt;br /&gt;
Ways for users to get content (files) into Moodle, either by uploading from their hard drive, or by getting the file from another location on the Internet, perhaps Drop Box, Google Docs, or Flickr. Repository plugins live in the &amp;lt;tt&amp;gt;repository&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
===Others===&lt;br /&gt;
&lt;br /&gt;
There are [[Developer_documentation#Make_a_new_plugin|many more types of plugins]] (more than 30 at the last count). These include text filters, question types, gradebook reports, admin reports, course reports, plagiarism detection services and web service protocols.&lt;br /&gt;
&lt;br /&gt;
For a definitive, up-to-date list, look at the value returned by the &amp;lt;tt&amp;gt;get_plugin_types()&amp;lt;/tt&amp;gt; function, which is defined in &amp;lt;tt&amp;gt;lib/moodlelib.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==How Moodle code is organised==&lt;br /&gt;
&lt;br /&gt;
Moodle mostly follows a [http://martinfowler.com/eaaCatalog/transactionScript.html transaction script] approach. That is, suppose you are looking a Forum. The URL will be &amp;lt;tt&amp;gt;.../mod/forum/view.php?id=1234&amp;lt;/tt&amp;gt;. and &amp;lt;tt&amp;gt;mod/forum/view.php&amp;lt;/tt&amp;gt; the PHP script that generates that page. One could argue that transaction script is not an appropriate pattern for an application as complex as Moodle. However, it is a very natural architecture for a PHP application, and Moodle is an aggregate of many different plugins, rather than a single complex application.&lt;br /&gt;
&lt;br /&gt;
Behind that basic transaction script approach, a lot of the core functionality has be refactored out into libraries (mostly in the &amp;lt;tt&amp;gt;lib&amp;lt;/tt&amp;gt; folder). This provides elements of a [http://martinfowler.com/eaaCatalog/domainModel.html domain model]. The Moodle project started before PHP could handle object-oriented code, however, so don&#039;t expect an object-oriented domain model except in some of the more recent parts of the Moodle code.&lt;br /&gt;
&lt;br /&gt;
There are two layers used to separate presentation from the business logic. The outer layer is the theme (see above) which controls the more visual aspects of the Moodle interface. Then there are renderer classes which generate the HTML to be output from the data supplied by the transaction scripts and the domain model. Unfortunately, neither PHP, nor the Moodle architecture, enforces a clear separation of the UI layer. It is possible for sloppy developers to make a mess, and this has happened in the past. The code in the standard Moodle distribution is gradually being cleaned up.&lt;br /&gt;
&lt;br /&gt;
==The Moodle database==&lt;br /&gt;
&lt;br /&gt;
The Moodle database comprises many tables (more than 250) because the whole database is an aggregate of the core tables and the tables belonging to each plugin. Fortunately, this large structure is understandable, because the tables for one particular plugin typically only link to each other and a few core tables. See [[Database_schema_introduction|Database schema introduction]] for more information.&lt;br /&gt;
&lt;br /&gt;
The Moodle database structure is defined in &amp;lt;tt&amp;gt;install.xml&amp;lt;/tt&amp;gt; files inside the &amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; folder in each plugin. For example &amp;lt;tt&amp;gt;mod/forum/db/install.xml&amp;lt;/tt&amp;gt; contains the database definition for the forum module. &amp;lt;tt&amp;gt;lib/db/install.xml&amp;lt;/tt&amp;gt; defines the tables used by Moodle core. The &amp;lt;tt&amp;gt;install.xml&amp;lt;/tt&amp;gt; files contain comments that should explain the purpose of each table and column. These comments can be turned into human-readable documentation by going to Site administration -&amp;gt; Development -&amp;gt; XMLDB editor in your Moodle installation, and clicking on the [Doc] link.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[:en:Administrator documentation]]&lt;br /&gt;
* [[Developer_documentation|Developer documentation]]&lt;br /&gt;
* [[Database_schema_introduction|Database schema introduction]]&lt;br /&gt;
* [http://moodle.org/mod/data/view.php?id=6009 Modules and plugins] and [http://moodle.org/mod/data/view.php?id=6552 themes] databases&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=64465 Forum thread with a vigorous argument about whether an architecture document is a good idea, which eventually lead to this page being rewritten]&lt;br /&gt;
* [http://www.aosabook.org/en/moodle.html The chapter of The Architecture of Open Source Applications about Moodle]&lt;br /&gt;
&lt;br /&gt;
[[Category:Coding guidelines]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Nicolas_Connault&amp;diff=20139</id>
		<title>User:Nicolas Connault</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Nicolas_Connault&amp;diff=20139"/>
		<updated>2010-07-11T11:21:46Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Removing all content from page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Nicolas_Connault&amp;diff=20138</id>
		<title>User:Nicolas Connault</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Nicolas_Connault&amp;diff=20138"/>
		<updated>2009-10-26T22:31:42Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Contact Details */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Contact Details==&lt;br /&gt;
 ICQ: 826611&lt;br /&gt;
 Yahoo : nicolasconnault&lt;br /&gt;
 AIM: NikoZeta&lt;br /&gt;
 Skype: nicolasconnault&lt;br /&gt;
 Email: nicolasconnault@gmail.com&lt;br /&gt;
 Cell: 0435423748&lt;br /&gt;
&lt;br /&gt;
==First steps towards Moodle==&lt;br /&gt;
Born in 1978 in France, I have 4 brothers and 1 sister. At age 19, I spent 2 years on a mission in England for the [http://www.mormon.org Church of Jesus Christ of Latter-Day Saints]. There I learned much about human nature and saw much misery and suffering, mostly caused by dysfunctional relationships between people, especially in families. I decided I would study psychology and become a family counselor, to help alleviate the hurt I saw all around me.&lt;br /&gt;
&lt;br /&gt;
I returned home to France in 2000, and shortly thereafter met my wife Anne-Marie while chatting on ICQ. We discovered we had similar interests, beliefs and values, and within 2 weeks (!?) I proposed to her over the Internet. I hadn&#039;t yet seen a picture of her. To me, physical appearance was not a concern.&lt;br /&gt;
&lt;br /&gt;
She came over to France 6 (long!) months later, and we were married in France (civilly) and in England (for eternity in the Temple). A few months later we moved to England for a job which did not work out as expected, following which we moved to Western Australia in 2001.&lt;br /&gt;
&lt;br /&gt;
==University and Self-teaching==&lt;br /&gt;
There I started taking interest in Web Development as a hobby (I had already built a couple of static websites as a youth), and used the many resources available online to teach myself LAMP/WAMP development. When I started my psychology degree (externally at Edith Cowan University), I chose a minor in professional computing, which included Object-Oriented programming, Database design and Web Development, but these added little to the knowledge I had already gained through learning online. The vibrant PHP community was the greatest source of learning for me.&lt;br /&gt;
&lt;br /&gt;
A few years ago I undertook my first serious open source project with two other friends, Christopher Vance and Vickie Comrie (both from the USA). The project was a PHP auction framework, along the lines of Ebay but much more simple, which could be implemented by small companies wanting an auction system. It was a voluntary project for a fairly large american charity. This project hasn&#039;t been maintained for years, and I would probably cringe if I looked at the source code now, but the skills I learned while working on it have proven invaluable.&lt;br /&gt;
&lt;br /&gt;
The code for the auction project attracted the attention of a small web development company, Triangle Solutions, who contracted me immediately on a part-time basis, later to become full-time. During the 1.5 years I worked for Triangle, I contributed to many different projects, but the most important was PHP Support Tickets, an application originally written in horrible spaghetti PHP which I was asked to upgrade. My decision was to rewrite it using Object-Oriented principles, of which I knew enough to improve the application at the time. Unfortunately I did not have the time resources available to devote enough time on this project, which greatly hindered its development. I still think it has great potential, but I would certainly refactor it if I had the opportunity to work on it again.&lt;br /&gt;
&lt;br /&gt;
==Beginnings at Moodle HQ==&lt;br /&gt;
In December 2006, I applied for the developer position at Moodle HQ in Perth, and was hired on a full-time basis. I started out doing mainly bug-squashing for version 1.8, but was also made responsible for unit testing, an area which had been greatly neglected until then. As soon as 1.8 was stable and released, I started working on the new gradebook internals for 1.9, together with Yu Zhang, Martin Dougiamas and Petr Skoda.&lt;br /&gt;
&lt;br /&gt;
==Other programming interests==&lt;br /&gt;
=== Ruby ===&lt;br /&gt;
One last note on development: I have recently taken great interest in the Ruby language, although that interest started much earlier than the current Ruby on Rails craze. I find the language extremely elegant, intuitive and fun to write in. It enables me to be much more creative than with PHP or Java. I also enjoy the Rails framework, although it certainly isn&#039;t as easy to learn as some people would have you believe. I am currently working on a real estate application (sales and rentals) written on that platform. You can see the front-end for one of my clients at [http://www.glenmarrealty.com.au Glenmar Realty].&lt;br /&gt;
&lt;br /&gt;
=== Test-Driven Development (TDD) ===&lt;br /&gt;
Since my early days with Object-Oriented languages, I have been a strong proponent of unit testing, and a keen supporter of the Test-Driven Development approach. Unfortunately it is not always easy to adopt such methodology when working on a project like Moodle, with much legacy and non-OO code. The approach was successfully used when developing the new gradebook for Moodle 1.9, although it could have used better. &lt;br /&gt;
&lt;br /&gt;
In the last 6 months I have given 3 presentations on the subject of Unit Testing. One was at the [http://elearning.lse.ac.uk/blogs/clt/?p=251 2007 UK Moodle Moot], and the other two were given at two different branches of the BCS, [http://nottmderby.bcs.org/events08-feb.htm Derby-Nottingham] and [http://www.herts.bcs.org/past.htm Hertfordshire]. [http://moodlemoot.org/file.php/4/moddata/assignment/1/438/presentation.ppt My slides] are available at these sites, but there are minor differences between them, since I try to improve my presentation each time.&lt;br /&gt;
&lt;br /&gt;
==Psychology==&lt;br /&gt;
On the topic of psychology: during my external studies, I became more and more disillusioned with the traditional counseling practice. I felt that, although the intentions of most professional psychologists were noble, their skills were often exercised within an ideological framework of manufactured needs. This meant that often, their contributions to the well-being of the human race were more imagined than real.&lt;br /&gt;
&lt;br /&gt;
My interests diverged towards community development, group processes, interpersonal relationships, child development and the learning process. I became aware that people tended to get better emotionally when they had a strong support network, and that non-professional assistance in the form of support groups was often more effective than prohibitory and extensive therapy sessions.&lt;br /&gt;
&lt;br /&gt;
I also became more and more disenchanted with the educational practices of the universities in Australia. They seemed to be based on ancient traditions and to ignore the very principles they were teaching. I especially abhorred the lecture medium of teaching, which was one reason why I chose to study externally. However I did a few units on campus and was glad to see some tutors trying to change their delivery methods to something more informal and participative. Overall I was extremely disappointed in the appalling quality of external teaching. It was extremely difficult to connect with other students, and you only got help if you persistently sought for it. Feedback was as rare (and precious) as gold. BlackBoard was inadequate in many ways, and most tutors didn&#039;t even use it. The drop-out rates from external courses was so great that it was very difficult to extract that figure from university staff. I wouldn&#039;t be surprised if only around 10% of all external students across all Australian universities actually stuck to their entire course, not to mention those who stay but fail.&lt;br /&gt;
&lt;br /&gt;
My project for the honours degree (which I have delayed for next year) was to compare the teaching methods of a German university with those of Edith Cowan University, and to compare the measurable results. The project for my doctorate was to develop an online, open source software suite that would enable external students to connect with each other as a vibrant community, in order to facilitate self-motivated learning and participation. This was a major factor in me accepting the job with Moodle, which represented exactly what I had in mind.&lt;br /&gt;
&lt;br /&gt;
Well, this is a rather lengthy description, but those that have read all of it now know a lot about me, and will be better able to understand the person behind the posts and the code.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15971</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15971"/>
		<updated>2009-10-22T23:44:22Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* External blogs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
Before 2.0 there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this could lead to serious performance and usability issues on large sites, and was turning the blog into something more akin to a forum. It was thus decided to make all blogs public. (See MDL-19676)&lt;br /&gt;
&lt;br /&gt;
This simplification means that sites using the old BLOG_GROUP_LEVEL and BLOG_COURSE_LEVEL and values of $CFG-&amp;gt;bloglevel have to perform an special upgrade which copies all the site&#039;s blog entries into a special &amp;quot;blog-type&amp;quot; forum in each course in which the blog entry&#039;s author is enrolled, then disables blogging at site level.&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:view&lt;br /&gt;
*moodle/blog:viewdrafts&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with an activity module.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: MDL-14408&lt;br /&gt;
&lt;br /&gt;
Capabilities: &lt;br /&gt;
*moodle/blog:associatecourse&lt;br /&gt;
*moodle/blog:associatemodule&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can specify external blogs which publish an RSS/ATOM feed. This feed is then checked periodically through a cron task, and entries are copied into the user&#039;s blog in Moodle, as read-only entries.&lt;br /&gt;
&lt;br /&gt;
This synchronisation checks if external entries have been modified or deleted since the last sync, and updates the Moodle entries accordingly.&lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;Filter tags&amp;quot; and &amp;quot;Automatic tags&amp;quot;:&lt;br /&gt;
*Filter tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog. An external entry will be selected if it has at least one of the filter tags. (Tags are called &amp;quot;categories&amp;quot; in RSS feeds)&lt;br /&gt;
*Automatic tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: MDL-19683&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:manageexternal&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19686&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:search&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19687&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19744&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-8776&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19688&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
===Upgrade from &amp;quot;Users can only see blogs for people who share a course&amp;quot;===&lt;br /&gt;
&lt;br /&gt;
This mode was always a slightly quirky and uncomfortable hack.  It was mostly useful for sites with very distinct courses and users, such as those where each course consists of the employees from a separate company, and My Moodle was used to hide the companies from each other.  However, for the general case, the main problem is that you see ALL blog entries from those people, not just ones that are about your courses.  &lt;br /&gt;
&lt;br /&gt;
This mode has no direct equivalent in Moodle 2.0 because we have resolved not to emulate &amp;quot;course-like&amp;quot; modes within a system that is at system level, and outside the courses.  This helps understanding of the blog system, simplifies the implementation and improves performance.  &lt;br /&gt;
&lt;br /&gt;
However some people may be using that mode so we do need to provide an upgrade path for them.&lt;br /&gt;
&lt;br /&gt;
Informationally, the idea of just seeing all the blogs from people who are in your course is the same as a course forum. &lt;br /&gt;
&lt;br /&gt;
So a possible upgrade would be to do this:&lt;br /&gt;
 If the mode is set to &amp;quot;Users can only see blogs for people who share a course&amp;quot; then&lt;br /&gt;
   Set the new mode to &amp;quot;Users can only see their own blog&amp;quot;&lt;br /&gt;
   Foreach user who has a blog &lt;br /&gt;
     Foreach course that user is enrolled in &lt;br /&gt;
        If a &amp;quot;Course blogs (recovered)&amp;quot; forum doesn&#039;t exist yet in the course (section 0) then create one&lt;br /&gt;
        Foreach blog entry that the user created &lt;br /&gt;
           Create a new discussion from the blog post with the same dates, attachments etc&lt;br /&gt;
&lt;br /&gt;
This way no data is lost.  The teachers can choose to delete the whole forum if they choose to.  The original blog entries should not be touched.&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| failedlastsync&lt;br /&gt;
| int(1)&lt;br /&gt;
|&lt;br /&gt;
| Whether or not the last synchronisation failed&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15970</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15970"/>
		<updated>2009-10-22T08:56:15Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* blog_external */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
Before 2.0 there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this could lead to serious performance and usability issues on large sites, and was turning the blog into something more akin to a forum. It was thus decided to make all blogs public. (See MDL-19676)&lt;br /&gt;
&lt;br /&gt;
This simplification means that sites using the old BLOG_GROUP_LEVEL and BLOG_COURSE_LEVEL and values of $CFG-&amp;gt;bloglevel have to perform an special upgrade which copies all the site&#039;s blog entries into a special &amp;quot;blog-type&amp;quot; forum in each course in which the blog entry&#039;s author is enrolled, then disables blogging at site level.&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:view&lt;br /&gt;
*moodle/blog:viewdrafts&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with an activity module.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: MDL-14408&lt;br /&gt;
&lt;br /&gt;
Capabilities: &lt;br /&gt;
*moodle/blog:associatecourse&lt;br /&gt;
*moodle/blog:associatemodule&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: MDL-19683&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:manageexternal&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19686&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:search&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19687&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19744&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-8776&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19688&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
===Upgrade from &amp;quot;Users can only see blogs for people who share a course&amp;quot;===&lt;br /&gt;
&lt;br /&gt;
This mode was always a slightly quirky and uncomfortable hack.  It was mostly useful for sites with very distinct courses and users, such as those where each course consists of the employees from a separate company, and My Moodle was used to hide the companies from each other.  However, for the general case, the main problem is that you see ALL blog entries from those people, not just ones that are about your courses.  &lt;br /&gt;
&lt;br /&gt;
This mode has no direct equivalent in Moodle 2.0 because we have resolved not to emulate &amp;quot;course-like&amp;quot; modes within a system that is at system level, and outside the courses.  This helps understanding of the blog system, simplifies the implementation and improves performance.  &lt;br /&gt;
&lt;br /&gt;
However some people may be using that mode so we do need to provide an upgrade path for them.&lt;br /&gt;
&lt;br /&gt;
Informationally, the idea of just seeing all the blogs from people who are in your course is the same as a course forum. &lt;br /&gt;
&lt;br /&gt;
So a possible upgrade would be to do this:&lt;br /&gt;
 If the mode is set to &amp;quot;Users can only see blogs for people who share a course&amp;quot; then&lt;br /&gt;
   Set the new mode to &amp;quot;Users can only see their own blog&amp;quot;&lt;br /&gt;
   Foreach user who has a blog &lt;br /&gt;
     Foreach course that user is enrolled in &lt;br /&gt;
        If a &amp;quot;Course blogs (recovered)&amp;quot; forum doesn&#039;t exist yet in the course (section 0) then create one&lt;br /&gt;
        Foreach blog entry that the user created &lt;br /&gt;
           Create a new discussion from the blog post with the same dates, attachments etc&lt;br /&gt;
&lt;br /&gt;
This way no data is lost.  The teachers can choose to delete the whole forum if they choose to.  The original blog entries should not be touched.&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| failedlastsync&lt;br /&gt;
| int(1)&lt;br /&gt;
|&lt;br /&gt;
| Whether or not the last synchronisation failed&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15969</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15969"/>
		<updated>2009-10-22T08:52:04Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* blog_external */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
Before 2.0 there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this could lead to serious performance and usability issues on large sites, and was turning the blog into something more akin to a forum. It was thus decided to make all blogs public. (See MDL-19676)&lt;br /&gt;
&lt;br /&gt;
This simplification means that sites using the old BLOG_GROUP_LEVEL and BLOG_COURSE_LEVEL and values of $CFG-&amp;gt;bloglevel have to perform an special upgrade which copies all the site&#039;s blog entries into a special &amp;quot;blog-type&amp;quot; forum in each course in which the blog entry&#039;s author is enrolled, then disables blogging at site level.&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:view&lt;br /&gt;
*moodle/blog:viewdrafts&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with an activity module.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: MDL-14408&lt;br /&gt;
&lt;br /&gt;
Capabilities: &lt;br /&gt;
*moodle/blog:associatecourse&lt;br /&gt;
*moodle/blog:associatemodule&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: MDL-19683&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:manageexternal&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19686&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:search&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19687&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19744&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-8776&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19688&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
===Upgrade from &amp;quot;Users can only see blogs for people who share a course&amp;quot;===&lt;br /&gt;
&lt;br /&gt;
This mode was always a slightly quirky and uncomfortable hack.  It was mostly useful for sites with very distinct courses and users, such as those where each course consists of the employees from a separate company, and My Moodle was used to hide the companies from each other.  However, for the general case, the main problem is that you see ALL blog entries from those people, not just ones that are about your courses.  &lt;br /&gt;
&lt;br /&gt;
This mode has no direct equivalent in Moodle 2.0 because we have resolved not to emulate &amp;quot;course-like&amp;quot; modes within a system that is at system level, and outside the courses.  This helps understanding of the blog system, simplifies the implementation and improves performance.  &lt;br /&gt;
&lt;br /&gt;
However some people may be using that mode so we do need to provide an upgrade path for them.&lt;br /&gt;
&lt;br /&gt;
Informationally, the idea of just seeing all the blogs from people who are in your course is the same as a course forum. &lt;br /&gt;
&lt;br /&gt;
So a possible upgrade would be to do this:&lt;br /&gt;
 If the mode is set to &amp;quot;Users can only see blogs for people who share a course&amp;quot; then&lt;br /&gt;
   Set the new mode to &amp;quot;Users can only see their own blog&amp;quot;&lt;br /&gt;
   Foreach user who has a blog &lt;br /&gt;
     Foreach course that user is enrolled in &lt;br /&gt;
        If a &amp;quot;Course blogs (recovered)&amp;quot; forum doesn&#039;t exist yet in the course (section 0) then create one&lt;br /&gt;
        Foreach blog entry that the user created &lt;br /&gt;
           Create a new discussion from the blog post with the same dates, attachments etc&lt;br /&gt;
&lt;br /&gt;
This way no data is lost.  The teachers can choose to delete the whole forum if they choose to.  The original blog entries should not be touched.&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| validationresult&lt;br /&gt;
| int(1)&lt;br /&gt;
|&lt;br /&gt;
| A cached boolean of the last Feed validation test&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15968</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15968"/>
		<updated>2009-10-15T03:02:29Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Who can view blog entries? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
Before 2.0 there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this could lead to serious performance and usability issues on large sites, and was turning the blog into something more akin to a forum. It was thus decided to make all blogs public. (See MDL-19676)&lt;br /&gt;
&lt;br /&gt;
This simplification means that sites using the old BLOG_GROUP_LEVEL and BLOG_COURSE_LEVEL and values of $CFG-&amp;gt;bloglevel have to perform an special upgrade which copies all the site&#039;s blog entries into a special &amp;quot;blog-type&amp;quot; forum in each course in which the blog entry&#039;s author is enrolled, then disables blogging at site level.&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:view&lt;br /&gt;
*moodle/blog:viewdrafts&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with an activity module.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: MDL-14408&lt;br /&gt;
&lt;br /&gt;
Capabilities: &lt;br /&gt;
*moodle/blog:associatecourse&lt;br /&gt;
*moodle/blog:associatemodule&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: MDL-19683&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:manageexternal&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19686&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:search&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19687&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19744&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-8776&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19688&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
===Upgrade from &amp;quot;Users can only see blogs for people who share a course&amp;quot;===&lt;br /&gt;
&lt;br /&gt;
This mode was always a slightly quirky and uncomfortable hack.  It was mostly useful for sites with very distinct courses and users, such as those where each course consists of the employees from a separate company, and My Moodle was used to hide the companies from each other.  However, for the general case, the main problem is that you see ALL blog entries from those people, not just ones that are about your courses.  &lt;br /&gt;
&lt;br /&gt;
This mode has no direct equivalent in Moodle 2.0 because we have resolved not to emulate &amp;quot;course-like&amp;quot; modes within a system that is at system level, and outside the courses.  This helps understanding of the blog system, simplifies the implementation and improves performance.  &lt;br /&gt;
&lt;br /&gt;
However some people may be using that mode so we do need to provide an upgrade path for them.&lt;br /&gt;
&lt;br /&gt;
Informationally, the idea of just seeing all the blogs from people who are in your course is the same as a course forum. &lt;br /&gt;
&lt;br /&gt;
So a possible upgrade would be to do this:&lt;br /&gt;
 If the mode is set to &amp;quot;Users can only see blogs for people who share a course&amp;quot; then&lt;br /&gt;
   Set the new mode to &amp;quot;Users can only see their own blog&amp;quot;&lt;br /&gt;
   Foreach user who has a blog &lt;br /&gt;
     Foreach course that user is enrolled in &lt;br /&gt;
        If a &amp;quot;Course blogs (recovered)&amp;quot; forum doesn&#039;t exist yet in the course (section 0) then create one&lt;br /&gt;
        Foreach blog entry that the user created &lt;br /&gt;
           Create a new discussion from the blog post with the same dates, attachments etc&lt;br /&gt;
&lt;br /&gt;
This way no data is lost.  The teachers can choose to delete the whole forum if they choose to.  The original blog entries should not be touched.&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15967</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15967"/>
		<updated>2009-10-15T02:45:30Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Associations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See MDL-19676)&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:view&lt;br /&gt;
*moodle/blog:viewdrafts&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with an activity module.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: MDL-14408&lt;br /&gt;
&lt;br /&gt;
Capabilities: &lt;br /&gt;
*moodle/blog:associatecourse&lt;br /&gt;
*moodle/blog:associatemodule&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: MDL-19683&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:manageexternal&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19686&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:search&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19687&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19744&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-8776&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19688&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
===Upgrade from &amp;quot;Users can only see blogs for people who share a course&amp;quot;===&lt;br /&gt;
&lt;br /&gt;
This mode was always a slightly quirky and uncomfortable hack.  It was mostly useful for sites with very distinct courses and users, such as those where each course consists of the employees from a separate company, and My Moodle was used to hide the companies from each other.  However, for the general case, the main problem is that you see ALL blog entries from those people, not just ones that are about your courses.  &lt;br /&gt;
&lt;br /&gt;
This mode has no direct equivalent in Moodle 2.0 because we have resolved not to emulate &amp;quot;course-like&amp;quot; modes within a system that is at system level, and outside the courses.  This helps understanding of the blog system, simplifies the implementation and improves performance.  &lt;br /&gt;
&lt;br /&gt;
However some people may be using that mode so we do need to provide an upgrade path for them.&lt;br /&gt;
&lt;br /&gt;
Informationally, the idea of just seeing all the blogs from people who are in your course is the same as a course forum. &lt;br /&gt;
&lt;br /&gt;
So a possible upgrade would be to do this:&lt;br /&gt;
 If the mode is set to &amp;quot;Users can only see blogs for people who share a course&amp;quot; then&lt;br /&gt;
   Set the new mode to &amp;quot;Users can only see their own blog&amp;quot;&lt;br /&gt;
   Foreach user who has a blog &lt;br /&gt;
     Foreach course that user is enrolled in &lt;br /&gt;
        If a &amp;quot;Course blogs (recovered)&amp;quot; forum doesn&#039;t exist yet in the course (section 0) then create one&lt;br /&gt;
        Foreach blog entry that the user created &lt;br /&gt;
           Create a new discussion from the blog post with the same dates, attachments etc&lt;br /&gt;
&lt;br /&gt;
This way no data is lost.  The teachers can choose to delete the whole forum if they choose to.  The original blog entries should not be touched.&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15965</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15965"/>
		<updated>2009-09-30T07:12:00Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Search blog entries */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See MDL-19676)&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:view&lt;br /&gt;
*moodle/blog:viewdrafts&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: MDL-14408&lt;br /&gt;
&lt;br /&gt;
Capabilities: &lt;br /&gt;
*moodle/blog:associatecourse&lt;br /&gt;
*moodle/blog:associatemodule&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: MDL-19683&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:manageexternal&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19686&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:search&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19687&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19744&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-8776&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19688&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15964</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15964"/>
		<updated>2009-09-30T07:00:56Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Who can view blog entries? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See MDL-19676)&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:view&lt;br /&gt;
*moodle/blog:viewdrafts&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: MDL-14408&lt;br /&gt;
&lt;br /&gt;
Capabilities: &lt;br /&gt;
*moodle/blog:associatecourse&lt;br /&gt;
*moodle/blog:associatemodule&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: MDL-19683&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:manageexternal&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19686&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19687&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19744&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-8776&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19688&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15963</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15963"/>
		<updated>2009-09-30T07:00:03Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* External blogs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See MDL-19676)&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: MDL-14408&lt;br /&gt;
&lt;br /&gt;
Capabilities: &lt;br /&gt;
*moodle/blog:associatecourse&lt;br /&gt;
*moodle/blog:associatemodule&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: MDL-19683&lt;br /&gt;
&lt;br /&gt;
Capabilities:&lt;br /&gt;
*moodle/blog:manageexternal&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19686&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19687&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19744&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-8776&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19688&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15962</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15962"/>
		<updated>2009-09-30T06:59:11Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Associations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See MDL-19676)&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: MDL-14408&lt;br /&gt;
&lt;br /&gt;
Capabilities: &lt;br /&gt;
*moodle/blog:associatecourse&lt;br /&gt;
*moodle/blog:associatemodule&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: MDL-19683&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19686&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19687&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19744&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-8776&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19688&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Using_the_File_API_in_Moodle_forms&amp;diff=13030</id>
		<title>Using the File API in Moodle forms</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Using_the_File_API_in_Moodle_forms&amp;diff=13030"/>
		<updated>2009-09-25T08:48:49Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Add section on gotchas&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
This document shows you exactly how to use Moodle forms to get files from users in a standard and secure way.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 all files are stored in a central database accessible via the [[File API|File API]], and every file is associated with a &amp;quot;file area&amp;quot; in Moodle, such as a particular module.&lt;br /&gt;
&lt;br /&gt;
A common use case is to provide a form (using Moodle&#039;s [[lib/formslib.php|Forms API]]) which allows users to upload or import files as attachments or media embedded into HTML.&lt;br /&gt;
&lt;br /&gt;
Normally this works like this:&lt;br /&gt;
# User starts creation or re-edits an existing item in Moodle (eg forum post, resource, glossary entry etc)&lt;br /&gt;
# User presses some sort of button to browse for new files to attach or embed&lt;br /&gt;
# User sees our &amp;quot;Choose file...&amp;quot; dialog, which contains one or more repository instances. &lt;br /&gt;
# User chooses a file, the [[Repository API|Repository API]] takes care of copying the file into a &amp;quot;draft file area&amp;quot; within Moodle&lt;br /&gt;
# File appears in the text or as an attachment in the form.&lt;br /&gt;
# When the user hits save, the [[File API|File API]] is invoked to move the file from the draft file area into a permanent file area associated with that data &lt;br /&gt;
&lt;br /&gt;
This document shows you exactly how to use Moodle forms to interact with users in a standard and secure way.&lt;br /&gt;
&lt;br /&gt;
If you just want to write code to manipulate Moodle files internally (without user input) then see [[Using_the_File_API]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Form elements== &lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 there are three file-related form elements for interacting with users:&lt;br /&gt;
&lt;br /&gt;
# filemanager - the way to attach one or more files as a set&lt;br /&gt;
# editor - the way to specify a textarea with a HTML editor, and all the handling of images and movies within that HTML&lt;br /&gt;
# filepicker - a way to specify one file for the case when you want to process the file and throw it away &lt;br /&gt;
&lt;br /&gt;
In Moodle 1.9 there were two other types which are now &#039;&#039;&#039;deprecated&#039;&#039;&#039; (they work, but please do not use these anymore)&lt;br /&gt;
# file - used to just allow a normal file upload from the desktop only.&lt;br /&gt;
# htmleditor - this old method of embedding a HTML editor in a textarea is not able to support repositories etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===filepicker===&lt;br /&gt;
&lt;br /&gt;
File picker (&#039;&#039;filepicker&#039;&#039;) is a direct replacement of the older &#039;&#039;file&#039;&#039; formslib element. &lt;br /&gt;
&lt;br /&gt;
It is intended for situations when you want the user to upload &#039;&#039;&#039;one&#039;&#039;&#039; file so you can process it and delete it, such as when you are importing data from a CSV file.&lt;br /&gt;
&lt;br /&gt;
==== Using the filepicker element ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filepicker&#039;, &#039;userfile&#039;, get_string(&#039;file&#039;), null, array(&#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;filetypes&#039; =&amp;gt; &#039;*&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Obtain the chosen file ====&lt;br /&gt;
&lt;br /&gt;
The API for getting file contents is exactly the same as for &#039;&#039;file&#039;&#039; element.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$content = $mform-&amp;gt;get_file_content(&#039;userfile&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== filemanager ===&lt;br /&gt;
&lt;br /&gt;
The File Manager element improves on file picker by allowing you to manage more than one file.  It is expected that the files will be stored permanently for future use (such as forum and glossary attachments).&lt;br /&gt;
&lt;br /&gt;
==== Add file manager element ====&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filemanager&#039;, &#039;attachments&#039;, get_string(&#039;attachment&#039;, &#039;moodle&#039;), null,&lt;br /&gt;
                    array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50, &#039;filetypes&#039; =&amp;gt; array(&#039;document&#039;) ));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here are the fields for filemanager:&lt;br /&gt;
&lt;br /&gt;
;&#039;filemanager&#039;:This is a filemanager element :)&lt;br /&gt;
;elementname:The unique name of the element in the form&lt;br /&gt;
;elementlabel:The label string that users see &lt;br /&gt;
;attributes:(leave it as null)&lt;br /&gt;
;options: an array of further options for the filepicker (see below)&lt;br /&gt;
&lt;br /&gt;
The options array can contain:&lt;br /&gt;
&lt;br /&gt;
;subdirs:(Default 0) Are subdirectories allowed?  (true or false)&lt;br /&gt;
;maxbytes:(Default 0) Restricts the total size of all the files.&lt;br /&gt;
;maxfiles:(Default -1) Restricts the total number of files.&lt;br /&gt;
;filetypes:(Default *) You can specify what file types are accepted by filemanager.  All current file types are listed in this file: [http://cvs.moodle.org/moodle/lib/file/file_types.mm moodle/lib/file/file_types.mm].  This is a [http://freemind.sourceforge.net/wiki/index.php/Main_Page freemind] file: if it is edited the changes will be immediately reflected in Moodle.  Example usage:  &#039;&#039;&#039;array(&#039;audio&#039;, &#039;video&#039;, &#039;documents&#039;)&#039;&#039;&#039;, you can include file extensions as well, for example: &#039;&#039;&#039;array(&#039;*.txt&#039;, &#039;*.jpg&#039;, &#039;audio&#039;)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Load existing files into draft area ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (empty($entry-&amp;gt;id)) {&lt;br /&gt;
    $entry = new object();&lt;br /&gt;
    $entry-&amp;gt;id = null;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$draftitemid = file_get_submitted_draft_itemid(&#039;attachments&#039;);&lt;br /&gt;
file_prepare_draft_area($draftitemid, $context-&amp;gt;id, &#039;glossary_attachment&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50));&lt;br /&gt;
$entry-&amp;gt;attachments = $draftitemid;&lt;br /&gt;
&lt;br /&gt;
$mform-&amp;gt;set_data($entry);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Store updated set of files ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
    // ... store or update $entry&lt;br /&gt;
    file_save_draft_area_files($data-&amp;gt;attachments, $context-&amp;gt;id, &#039;glossary_attachment&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===editor===&lt;br /&gt;
There are two way for using of editor element in code, the first one is easier but expects some standardized fields. The second method is more low level.&lt;br /&gt;
&lt;br /&gt;
====Simple use====&lt;br /&gt;
# name database fields: &#039;&#039;textfield&#039;&#039;, &#039;&#039;textfieldformat&#039;&#039; (and &#039;&#039;textfieldtrust&#039;&#039; if required)&lt;br /&gt;
# create options array &amp;lt;code php&amp;gt;$textfieldoptions = array(&#039;trusttext&#039;=&amp;gt;true, &#039;subdirs&#039;=&amp;gt;true, &#039;maxfiles&#039;=&amp;gt;$maxfiles, &#039;maxbytes&#039;=&amp;gt;$maxbytes);&amp;lt;/code&amp;gt;&lt;br /&gt;
# add editor &#039;&#039;textfield_editor&#039;&#039; to moodle form, pass options through custom data in form constructor, set $data-&amp;gt;id to null if data not exist yet &amp;lt;code php&amp;gt;$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;textfield_editor&#039;, get_string(&#039;fieldname&#039;, &#039;somemodule&#039;), null, $textfieldoptions);&amp;lt;/code&amp;gt;&lt;br /&gt;
# prepare data &amp;lt;code php&amp;gt;$data = file_prepare_standard_editor($data, &#039;textfield&#039;, $textfieldoptions, $context, &#039;somemodule_somearea&#039;, $data-&amp;gt;id);&amp;lt;/code&amp;gt;&lt;br /&gt;
# get submitted data and after inserting/updating of data &amp;lt;code php&amp;gt;$data = file_postupdate_standard_editor($data, &#039;textfield&#039;, $textfieldoptions, $context, &#039;somemodule_somearea&#039;, $data-&amp;gt;id);&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Real world examples are in mod/glossary/edit.php and mod/glossary/comment.php&lt;br /&gt;
&lt;br /&gt;
====Low level use====&lt;br /&gt;
&lt;br /&gt;
When using editor element you  need to preprocess and postprocess the data:&lt;br /&gt;
# detect if form was already submitted (usually means draft is area already exists) - &#039;&#039;file_get_submitted_draft_itemid()&#039;&#039;&lt;br /&gt;
# prepare draft file area, temporary storage of all files attached to the text - &#039;&#039;file_prepare_draft_area()&#039;&#039;&lt;br /&gt;
# convert encoded relative links to absolute links - &#039;&#039;file_prepare_draft_area()&#039;&#039;&lt;br /&gt;
# create form and set current data&lt;br /&gt;
# after submission the changed files must be merged back into original area - &#039;&#039;file_save_draft_area_files()&#039;&#039;&lt;br /&gt;
# absolute links have to be replaced by relative links - &#039;&#039;file_save_draft_area_files()&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====Replace old htmleditor with editor=====&lt;br /&gt;
&lt;br /&gt;
The file picker has been integrated with with TinyMCE to make the editor element. This new element should support all types on editors and should be able to switch them on-the-fly. Instances of the old htmleditor element in your forms should be replaced by the new editor element, this may need adding of new format and trusttext columns. For example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;entry&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), null,&lt;br /&gt;
        array(&#039;maxfiles&#039; =&amp;gt; EDITOR_UNLIMITED_FILES, &#039;filearea&#039; =&amp;gt; &#039;glossary_entry&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The editor element can take following options: maxfiles, maxbytes, filearea, subdirs and changeformat. Please note that the embedded files is optional feature and is not expected be used everywhere.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the editor element now includes text format option. You should no longer use the separate format element type.&lt;br /&gt;
&lt;br /&gt;
=====Prepare current data - text and files=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (empty($entry-&amp;gt;id)) {&lt;br /&gt;
  $entry = new object();&lt;br /&gt;
  $entry-&amp;gt;id = null;&lt;br /&gt;
  $entry-&amp;gt;definition = &#039;&#039;;&lt;br /&gt;
  $entry-&amp;gt;format = FORMAT_HTML;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$draftid_editor = file_get_submitted_draft_itemid(&#039;entry&#039;);&lt;br /&gt;
$currenttext = file_prepare_draft_area($draftid_editor, $context-&amp;gt;id, &#039;glossary_entry&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039;=&amp;gt;true), $entry-&amp;gt;definition);&lt;br /&gt;
$entry-&amp;gt;entry = array(&#039;text&#039;=&amp;gt;$currenttext, &#039;format&#039;=&amp;gt;$entry-&amp;gt;format, &#039;itemid&#039;=&amp;gt;$draftid_editor);&lt;br /&gt;
&lt;br /&gt;
$mform-&amp;gt;set_data($entry);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there are multiple files, they will share the same itemid.&lt;br /&gt;
&lt;br /&gt;
=====Obtain text, format and save draft files=====&lt;br /&gt;
&lt;br /&gt;
To retrieve editor content, you need to use following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($fromform = $mform-&amp;gt;get_data()) {&lt;br /&gt;
    // content of editor&lt;br /&gt;
    $messagetext = $fromform-&amp;gt;entry[&#039;text&#039;];&lt;br /&gt;
    // format of content&lt;br /&gt;
    $messageformat  = $fromform-&amp;gt;entry[&#039;format&#039;];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a user selects a file using the file picker, the file is initially stored in a draft file area, and a URL is inserted into the HTML in the editor that lets the person editing the content (but no one else) see the file.&lt;br /&gt;
&lt;br /&gt;
When the user submits the form, we then need to save the draft files to the correct place in permanent storage. (Just like you have to call $DB-&amp;gt;update_record(&#039;tablename&#039;, $data); to have the other parts of the form submission stored correctly.)&lt;br /&gt;
&lt;br /&gt;
The save_files_from_draft_area function and replace absolute links with internal relative links do:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$messagetext = file_save_draft_area_files($draftid_editor, $context-&amp;gt;id, &#039;glossary_entry&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039;=&amp;gt;true), $messagetext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
; $context-&amp;gt;id, &#039;proper_file_area&#039; and $entry-&amp;gt;id : correspond to the contextid, filearea and itemid columns in the [[File_API#Table:_files|files table]].&lt;br /&gt;
; $messagetext : this is the message text. As the files are saved to the real file area, the URLs in this content are rewritten.&lt;br /&gt;
&lt;br /&gt;
All URLs in content that point to files managed to the File API are converted to a form that starts &#039;@@PLUGINFILE@@/&#039; before the content is stored in the database. That is what we mean by rewriting.&lt;br /&gt;
&lt;br /&gt;
== File serving==&lt;br /&gt;
&lt;br /&gt;
=== Convert internal relative links to absolute links ===&lt;br /&gt;
&lt;br /&gt;
Before text content is displayed to the user, any URLs in the &#039;@@PLUGINFILE@@/&#039; form in the content need to be rewritten to the real URL where the user can access the files. &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$messagetext = file_rewrite_pluginfile_urls($messagetext, &#039;pluginfile.php&#039;,&lt;br /&gt;
        &amp;quot;$context-&amp;gt;id/proper_file_area/$itemid/&amp;quot;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
; $messagetext : is the content containing the @@PLUGINFILE@@ URLs from the database.&lt;br /&gt;
; &#039;pluginfile.php&#039; : there are a number of different scripts that can serve files with different permissions checks. You need to specify which one to use.&lt;br /&gt;
; &amp;quot;$context-&amp;gt;id/proper_file_area/$itemid/&amp;quot; : uniquely identifies the file area, as before.&lt;br /&gt;
&lt;br /&gt;
=== Implement file serving access control ===&lt;br /&gt;
&lt;br /&gt;
Attachments and embedded images should have the same access control like the text itself, in majority of cases these files are served using pluginfile.php. Access control is defined in &#039;&#039;module/lib.php&#039;&#039; file in function &#039;&#039;module_pluginfile()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File browsing support ==&lt;br /&gt;
Only owner of each file area is allowed to use low level File API function to access files, other parts of Moodle should use file browsing API.&lt;br /&gt;
&lt;br /&gt;
Activities may specify browsing support in own module/lib.php file by implementing functions module_get_file_areas() and module_get_file_info().&lt;br /&gt;
&lt;br /&gt;
== Upgrading your code ==&lt;br /&gt;
Here I will attempt to describe some simple steps you can take to upgrade your file-handling form elements from pre-2.0 code to 2.0. We will use the example of glossary, since it has been used above.&lt;br /&gt;
&lt;br /&gt;
=== Preparing your options ===&lt;br /&gt;
Unless you are happy with the defaults, you will need to define an array of options for each file-handling form element. You could define it at different places, but it&#039;s best to put it in one place and make the array(s) available to other files if they need it. In the majority of cases, this will be in a file like edit.php&lt;br /&gt;
&lt;br /&gt;
Previous code in mod/glossary/edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform =&amp;amp; new mod_glossary_entry_form(null, compact(&#039;cm&#039;, &#039;glossary&#039;, &#039;hook&#039;, &#039;mode&#039;, &#039;e&#039;, &#039;context&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$maxbytes = $course-&amp;gt;maxbytes; // Could also use $CFG-&amp;gt;maxbytes if you are not coding within a course context&lt;br /&gt;
$definitionoptions = array(&#039;subdirs&#039;=&amp;gt;false, &#039;maxfiles&#039;=&amp;gt;99, &#039;maxbytes&#039;=&amp;gt;$maxbytes, &#039;trusttext&#039;=&amp;gt;true, &#039;context&#039;=&amp;gt;$context);&lt;br /&gt;
$attachmentoptions = array(&#039;subdirs&#039;=&amp;gt;false, &#039;maxfiles&#039;=&amp;gt;99, &#039;maxbytes&#039;=&amp;gt;$maxbytes);&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note that the data being passed to the form constructor have changed also, but this is not part of the file API changes, I just include them to avoid confusion.&lt;br /&gt;
&lt;br /&gt;
These options are for the htmleditor (definition field) and the filemanager (attachment field). They are used by a file called edit_form.php.&lt;br /&gt;
&lt;br /&gt;
=== Element preparation ===&lt;br /&gt;
Before we look at this, however, we need to &amp;quot;prepare&amp;quot; the elements so that they can correctly display existing embedded images and attached files when you are editing a record instead of just creating one. So, let&#039;s take the code we&#039;ve got so far in edit.php and add to it:&lt;br /&gt;
&lt;br /&gt;
Currently upgraded code in edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code with element preparation:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$entry = file_prepare_standard_editor($entry, &#039;definition&#039;, $definitionoptions, $context, &#039;glossary_entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_prepare_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;glossary_attachment&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Things to note:&lt;br /&gt;
* $entry in this case is simply a stdClass object which may either represent a new glossary entry or an existing one.&lt;br /&gt;
* $entry-&amp;gt;id must be the unique identifier for the current object. If we are creating a new entry, it will be null, but in all cases it must be defined.&lt;br /&gt;
* These two functions (file_prepare_standard_editor and file_prepare_standard_filemanager) are shortcuts functions that take care of some of the tedious setting up for you, but they make a couple of assumptions:&lt;br /&gt;
*# You &#039;&#039;&#039;must&#039;&#039;&#039; name the form element as {element}_editor or {element}_filemanager (see next section)&lt;br /&gt;
*# You &#039;&#039;&#039;must&#039;&#039;&#039; have at least the following fields in the database: {element} and {element}summary, as described earlier in this documentation&lt;br /&gt;
&lt;br /&gt;
We can now look at the upgrades needed in the form definition file.&lt;br /&gt;
&lt;br /&gt;
=== Form definition ===&lt;br /&gt;
Previous code in mod/glossary/edit_form.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;htmleditor&#039;, &#039;definition&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), array(&#039;rows&#039;=&amp;gt;20));&lt;br /&gt;
$mform-&amp;gt;setType(&#039;definition&#039;, PARAM_RAW);&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;definition&#039;, null, &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;definition&#039;, array(&#039;writing&#039;, &#039;richtext&#039;), false, &#039;editorhelpbutton&#039;);&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;format&#039;);&lt;br /&gt;
// a bit further...&lt;br /&gt;
$this-&amp;gt;set_upload_manager(new upload_manager(&#039;attachment&#039;, true, false, $COURSE, false, 0, true, true, false));&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;attachment&#039;, array(&#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), &#039;glossary&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$definitionoptions = $this-&amp;gt;_customdata[&#039;definitionoptions&#039;];&lt;br /&gt;
$attachmentoptions = $this-&amp;gt;_customdata[&#039;attachmentoptions&#039;];&lt;br /&gt;
// a bit further...&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;definition_editor&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), null, $definitionoptions);&lt;br /&gt;
$mform-&amp;gt;setType(&#039;definition_editor&#039;, PARAM_RAW);&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;definition_editor&#039;, get_string(&#039;required&#039;), &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
// a bit further...&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filemanager&#039;, &#039;attachment_filemanager&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), null, $attachmentoptions);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;attachment_filemanager&#039;, array(&#039;attachment2&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), &#039;glossary&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the following:&lt;br /&gt;
* The format element and the help button are no longer required for the HTML editor element&lt;br /&gt;
* The name of the form element needs to be changed by adding &#039;_editor&#039; or &#039;_manager&#039; to the original name. This is a naming convention that is used by a couple of functions we will look at shortly&lt;br /&gt;
&lt;br /&gt;
=== Handling submitted data ===&lt;br /&gt;
The final step is to handle the submitted data properly, i.e. retrieve the files and save them to disk, associating them with the record we have just created (a glossary entry in our example). This happens in edit.php:&lt;br /&gt;
&lt;br /&gt;
Previous code in edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Section that updates an entry:&lt;br /&gt;
$todb-&amp;gt;id = $e;&lt;br /&gt;
$dir = glossary_file_area_name($todb);&lt;br /&gt;
if ($mform-&amp;gt;save_files($dir) and $newfilename = $mform-&amp;gt;get_new_filename()) {&lt;br /&gt;
    $todb-&amp;gt;attachment = $newfilename;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Section that adds an entry:&lt;br /&gt;
if ($todb-&amp;gt;id = insert_record(&amp;quot;glossary_entries&amp;quot;, $todb)) {&lt;br /&gt;
    $e = $todb-&amp;gt;id;&lt;br /&gt;
    $dir = glossary_file_area_name($todb);&lt;br /&gt;
    if ($mform-&amp;gt;save_files($dir) and $newfilename = $mform-&amp;gt;get_new_filename()) {&lt;br /&gt;
        set_field(&amp;quot;glossary_entries&amp;quot;, &amp;quot;attachment&amp;quot;, $newfilename, &amp;quot;id&amp;quot;, $todb-&amp;gt;id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// $todb was renamed to $entry, and the code was refactored &lt;br /&gt;
// so that the file-handling code is only used once for either an add or an update action.&lt;br /&gt;
// If an entry is being added, $DB-&amp;gt;insert() has already been called, so we have a valid $entry-&amp;gt;id&lt;br /&gt;
$entry = file_postupdate_standard_editor($entry, &#039;definition&#039;, $definitionoptions, $context, &#039;glossary_entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_postupdate_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;glossary_attachment&#039;, $entry-&amp;gt;id);&lt;br /&gt;
// store the updated value values&lt;br /&gt;
$DB-&amp;gt;update_record(&#039;glossary_entries&#039;, $entry);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Things to note:&lt;br /&gt;
* If you are adding a new record, you will still need to call update_record after calling the file_postupdate* functions&lt;br /&gt;
&lt;br /&gt;
=== Gotchas ===&lt;br /&gt;
A few things to keep in mind:&lt;br /&gt;
* Make sure that you instantiate the moodle form before any call to $OUTPUT-&amp;gt;header()&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[File API]]&lt;br /&gt;
* [[Using the file API]]&lt;br /&gt;
* [[Repository API]]&lt;br /&gt;
* [[Portfolio API]]&lt;br /&gt;
* MDL-14589 - File API Meta issue&lt;br /&gt;
&lt;br /&gt;
{{CategoryDeveloper}}&lt;br /&gt;
[[Category:Files]]&lt;br /&gt;
[[Category:Repositories]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Using_the_File_API_in_Moodle_forms&amp;diff=13029</id>
		<title>Using the File API in Moodle forms</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Using_the_File_API_in_Moodle_forms&amp;diff=13029"/>
		<updated>2009-09-25T08:35:10Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Preparing your options */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
This document shows you exactly how to use Moodle forms to get files from users in a standard and secure way.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 all files are stored in a central database accessible via the [[File API|File API]], and every file is associated with a &amp;quot;file area&amp;quot; in Moodle, such as a particular module.&lt;br /&gt;
&lt;br /&gt;
A common use case is to provide a form (using Moodle&#039;s [[lib/formslib.php|Forms API]]) which allows users to upload or import files as attachments or media embedded into HTML.&lt;br /&gt;
&lt;br /&gt;
Normally this works like this:&lt;br /&gt;
# User starts creation or re-edits an existing item in Moodle (eg forum post, resource, glossary entry etc)&lt;br /&gt;
# User presses some sort of button to browse for new files to attach or embed&lt;br /&gt;
# User sees our &amp;quot;Choose file...&amp;quot; dialog, which contains one or more repository instances. &lt;br /&gt;
# User chooses a file, the [[Repository API|Repository API]] takes care of copying the file into a &amp;quot;draft file area&amp;quot; within Moodle&lt;br /&gt;
# File appears in the text or as an attachment in the form.&lt;br /&gt;
# When the user hits save, the [[File API|File API]] is invoked to move the file from the draft file area into a permanent file area associated with that data &lt;br /&gt;
&lt;br /&gt;
This document shows you exactly how to use Moodle forms to interact with users in a standard and secure way.&lt;br /&gt;
&lt;br /&gt;
If you just want to write code to manipulate Moodle files internally (without user input) then see [[Using_the_File_API]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Form elements== &lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 there are three file-related form elements for interacting with users:&lt;br /&gt;
&lt;br /&gt;
# filemanager - the way to attach one or more files as a set&lt;br /&gt;
# editor - the way to specify a textarea with a HTML editor, and all the handling of images and movies within that HTML&lt;br /&gt;
# filepicker - a way to specify one file for the case when you want to process the file and throw it away &lt;br /&gt;
&lt;br /&gt;
In Moodle 1.9 there were two other types which are now &#039;&#039;&#039;deprecated&#039;&#039;&#039; (they work, but please do not use these anymore)&lt;br /&gt;
# file - used to just allow a normal file upload from the desktop only.&lt;br /&gt;
# htmleditor - this old method of embedding a HTML editor in a textarea is not able to support repositories etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===filepicker===&lt;br /&gt;
&lt;br /&gt;
File picker (&#039;&#039;filepicker&#039;&#039;) is a direct replacement of the older &#039;&#039;file&#039;&#039; formslib element. &lt;br /&gt;
&lt;br /&gt;
It is intended for situations when you want the user to upload &#039;&#039;&#039;one&#039;&#039;&#039; file so you can process it and delete it, such as when you are importing data from a CSV file.&lt;br /&gt;
&lt;br /&gt;
==== Using the filepicker element ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filepicker&#039;, &#039;userfile&#039;, get_string(&#039;file&#039;), null, array(&#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;filetypes&#039; =&amp;gt; &#039;*&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Obtain the chosen file ====&lt;br /&gt;
&lt;br /&gt;
The API for getting file contents is exactly the same as for &#039;&#039;file&#039;&#039; element.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$content = $mform-&amp;gt;get_file_content(&#039;userfile&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== filemanager ===&lt;br /&gt;
&lt;br /&gt;
The File Manager element improves on file picker by allowing you to manage more than one file.  It is expected that the files will be stored permanently for future use (such as forum and glossary attachments).&lt;br /&gt;
&lt;br /&gt;
==== Add file manager element ====&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filemanager&#039;, &#039;attachments&#039;, get_string(&#039;attachment&#039;, &#039;moodle&#039;), null,&lt;br /&gt;
                    array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50, &#039;filetypes&#039; =&amp;gt; array(&#039;document&#039;) ));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here are the fields for filemanager:&lt;br /&gt;
&lt;br /&gt;
;&#039;filemanager&#039;:This is a filemanager element :)&lt;br /&gt;
;elementname:The unique name of the element in the form&lt;br /&gt;
;elementlabel:The label string that users see &lt;br /&gt;
;attributes:(leave it as null)&lt;br /&gt;
;options: an array of further options for the filepicker (see below)&lt;br /&gt;
&lt;br /&gt;
The options array can contain:&lt;br /&gt;
&lt;br /&gt;
;subdirs:(Default 0) Are subdirectories allowed?  (true or false)&lt;br /&gt;
;maxbytes:(Default 0) Restricts the total size of all the files.&lt;br /&gt;
;maxfiles:(Default -1) Restricts the total number of files.&lt;br /&gt;
;filetypes:(Default *) You can specify what file types are accepted by filemanager.  All current file types are listed in this file: [http://cvs.moodle.org/moodle/lib/file/file_types.mm moodle/lib/file/file_types.mm].  This is a [http://freemind.sourceforge.net/wiki/index.php/Main_Page freemind] file: if it is edited the changes will be immediately reflected in Moodle.  Example usage:  &#039;&#039;&#039;array(&#039;audio&#039;, &#039;video&#039;, &#039;documents&#039;)&#039;&#039;&#039;, you can include file extensions as well, for example: &#039;&#039;&#039;array(&#039;*.txt&#039;, &#039;*.jpg&#039;, &#039;audio&#039;)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Load existing files into draft area ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (empty($entry-&amp;gt;id)) {&lt;br /&gt;
    $entry = new object();&lt;br /&gt;
    $entry-&amp;gt;id = null;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$draftitemid = file_get_submitted_draft_itemid(&#039;attachments&#039;);&lt;br /&gt;
file_prepare_draft_area($draftitemid, $context-&amp;gt;id, &#039;glossary_attachment&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50));&lt;br /&gt;
$entry-&amp;gt;attachments = $draftitemid;&lt;br /&gt;
&lt;br /&gt;
$mform-&amp;gt;set_data($entry);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Store updated set of files ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
    // ... store or update $entry&lt;br /&gt;
    file_save_draft_area_files($data-&amp;gt;attachments, $context-&amp;gt;id, &#039;glossary_attachment&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===editor===&lt;br /&gt;
There are two way for using of editor element in code, the first one is easier but expects some standardized fields. The second method is more low level.&lt;br /&gt;
&lt;br /&gt;
====Simple use====&lt;br /&gt;
# name database fields: &#039;&#039;textfield&#039;&#039;, &#039;&#039;textfieldformat&#039;&#039; (and &#039;&#039;textfieldtrust&#039;&#039; if required)&lt;br /&gt;
# create options array &amp;lt;code php&amp;gt;$textfieldoptions = array(&#039;trusttext&#039;=&amp;gt;true, &#039;subdirs&#039;=&amp;gt;true, &#039;maxfiles&#039;=&amp;gt;$maxfiles, &#039;maxbytes&#039;=&amp;gt;$maxbytes);&amp;lt;/code&amp;gt;&lt;br /&gt;
# add editor &#039;&#039;textfield_editor&#039;&#039; to moodle form, pass options through custom data in form constructor, set $data-&amp;gt;id to null if data not exist yet &amp;lt;code php&amp;gt;$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;textfield_editor&#039;, get_string(&#039;fieldname&#039;, &#039;somemodule&#039;), null, $textfieldoptions);&amp;lt;/code&amp;gt;&lt;br /&gt;
# prepare data &amp;lt;code php&amp;gt;$data = file_prepare_standard_editor($data, &#039;textfield&#039;, $textfieldoptions, $context, &#039;somemodule_somearea&#039;, $data-&amp;gt;id);&amp;lt;/code&amp;gt;&lt;br /&gt;
# get submitted data and after inserting/updating of data &amp;lt;code php&amp;gt;$data = file_postupdate_standard_editor($data, &#039;textfield&#039;, $textfieldoptions, $context, &#039;somemodule_somearea&#039;, $data-&amp;gt;id);&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Real world examples are in mod/glossary/edit.php and mod/glossary/comment.php&lt;br /&gt;
&lt;br /&gt;
====Low level use====&lt;br /&gt;
&lt;br /&gt;
When using editor element you  need to preprocess and postprocess the data:&lt;br /&gt;
# detect if form was already submitted (usually means draft is area already exists) - &#039;&#039;file_get_submitted_draft_itemid()&#039;&#039;&lt;br /&gt;
# prepare draft file area, temporary storage of all files attached to the text - &#039;&#039;file_prepare_draft_area()&#039;&#039;&lt;br /&gt;
# convert encoded relative links to absolute links - &#039;&#039;file_prepare_draft_area()&#039;&#039;&lt;br /&gt;
# create form and set current data&lt;br /&gt;
# after submission the changed files must be merged back into original area - &#039;&#039;file_save_draft_area_files()&#039;&#039;&lt;br /&gt;
# absolute links have to be replaced by relative links - &#039;&#039;file_save_draft_area_files()&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====Replace old htmleditor with editor=====&lt;br /&gt;
&lt;br /&gt;
The file picker has been integrated with with TinyMCE to make the editor element. This new element should support all types on editors and should be able to switch them on-the-fly. Instances of the old htmleditor element in your forms should be replaced by the new editor element, this may need adding of new format and trusttext columns. For example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;entry&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), null,&lt;br /&gt;
        array(&#039;maxfiles&#039; =&amp;gt; EDITOR_UNLIMITED_FILES, &#039;filearea&#039; =&amp;gt; &#039;glossary_entry&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The editor element can take following options: maxfiles, maxbytes, filearea, subdirs and changeformat. Please note that the embedded files is optional feature and is not expected be used everywhere.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the editor element now includes text format option. You should no longer use the separate format element type.&lt;br /&gt;
&lt;br /&gt;
=====Prepare current data - text and files=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (empty($entry-&amp;gt;id)) {&lt;br /&gt;
  $entry = new object();&lt;br /&gt;
  $entry-&amp;gt;id = null;&lt;br /&gt;
  $entry-&amp;gt;definition = &#039;&#039;;&lt;br /&gt;
  $entry-&amp;gt;format = FORMAT_HTML;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$draftid_editor = file_get_submitted_draft_itemid(&#039;entry&#039;);&lt;br /&gt;
$currenttext = file_prepare_draft_area($draftid_editor, $context-&amp;gt;id, &#039;glossary_entry&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039;=&amp;gt;true), $entry-&amp;gt;definition);&lt;br /&gt;
$entry-&amp;gt;entry = array(&#039;text&#039;=&amp;gt;$currenttext, &#039;format&#039;=&amp;gt;$entry-&amp;gt;format, &#039;itemid&#039;=&amp;gt;$draftid_editor);&lt;br /&gt;
&lt;br /&gt;
$mform-&amp;gt;set_data($entry);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there are multiple files, they will share the same itemid.&lt;br /&gt;
&lt;br /&gt;
=====Obtain text, format and save draft files=====&lt;br /&gt;
&lt;br /&gt;
To retrieve editor content, you need to use following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($fromform = $mform-&amp;gt;get_data()) {&lt;br /&gt;
    // content of editor&lt;br /&gt;
    $messagetext = $fromform-&amp;gt;entry[&#039;text&#039;];&lt;br /&gt;
    // format of content&lt;br /&gt;
    $messageformat  = $fromform-&amp;gt;entry[&#039;format&#039;];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a user selects a file using the file picker, the file is initially stored in a draft file area, and a URL is inserted into the HTML in the editor that lets the person editing the content (but no one else) see the file.&lt;br /&gt;
&lt;br /&gt;
When the user submits the form, we then need to save the draft files to the correct place in permanent storage. (Just like you have to call $DB-&amp;gt;update_record(&#039;tablename&#039;, $data); to have the other parts of the form submission stored correctly.)&lt;br /&gt;
&lt;br /&gt;
The save_files_from_draft_area function and replace absolute links with internal relative links do:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$messagetext = file_save_draft_area_files($draftid_editor, $context-&amp;gt;id, &#039;glossary_entry&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039;=&amp;gt;true), $messagetext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
; $context-&amp;gt;id, &#039;proper_file_area&#039; and $entry-&amp;gt;id : correspond to the contextid, filearea and itemid columns in the [[File_API#Table:_files|files table]].&lt;br /&gt;
; $messagetext : this is the message text. As the files are saved to the real file area, the URLs in this content are rewritten.&lt;br /&gt;
&lt;br /&gt;
All URLs in content that point to files managed to the File API are converted to a form that starts &#039;@@PLUGINFILE@@/&#039; before the content is stored in the database. That is what we mean by rewriting.&lt;br /&gt;
&lt;br /&gt;
== File serving==&lt;br /&gt;
&lt;br /&gt;
=== Convert internal relative links to absolute links ===&lt;br /&gt;
&lt;br /&gt;
Before text content is displayed to the user, any URLs in the &#039;@@PLUGINFILE@@/&#039; form in the content need to be rewritten to the real URL where the user can access the files. &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$messagetext = file_rewrite_pluginfile_urls($messagetext, &#039;pluginfile.php&#039;,&lt;br /&gt;
        &amp;quot;$context-&amp;gt;id/proper_file_area/$itemid/&amp;quot;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
; $messagetext : is the content containing the @@PLUGINFILE@@ URLs from the database.&lt;br /&gt;
; &#039;pluginfile.php&#039; : there are a number of different scripts that can serve files with different permissions checks. You need to specify which one to use.&lt;br /&gt;
; &amp;quot;$context-&amp;gt;id/proper_file_area/$itemid/&amp;quot; : uniquely identifies the file area, as before.&lt;br /&gt;
&lt;br /&gt;
=== Implement file serving access control ===&lt;br /&gt;
&lt;br /&gt;
Attachments and embedded images should have the same access control like the text itself, in majority of cases these files are served using pluginfile.php. Access control is defined in &#039;&#039;module/lib.php&#039;&#039; file in function &#039;&#039;module_pluginfile()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File browsing support ==&lt;br /&gt;
Only owner of each file area is allowed to use low level File API function to access files, other parts of Moodle should use file browsing API.&lt;br /&gt;
&lt;br /&gt;
Activities may specify browsing support in own module/lib.php file by implementing functions module_get_file_areas() and module_get_file_info().&lt;br /&gt;
&lt;br /&gt;
== Upgrading your code ==&lt;br /&gt;
Here I will attempt to describe some simple steps you can take to upgrade your file-handling form elements from pre-2.0 code to 2.0. We will use the example of glossary, since it has been used above.&lt;br /&gt;
&lt;br /&gt;
=== Preparing your options ===&lt;br /&gt;
Unless you are happy with the defaults, you will need to define an array of options for each file-handling form element. You could define it at different places, but it&#039;s best to put it in one place and make the array(s) available to other files if they need it. In the majority of cases, this will be in a file like edit.php&lt;br /&gt;
&lt;br /&gt;
Previous code in mod/glossary/edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform =&amp;amp; new mod_glossary_entry_form(null, compact(&#039;cm&#039;, &#039;glossary&#039;, &#039;hook&#039;, &#039;mode&#039;, &#039;e&#039;, &#039;context&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$maxbytes = $course-&amp;gt;maxbytes; // Could also use $CFG-&amp;gt;maxbytes if you are not coding within a course context&lt;br /&gt;
$definitionoptions = array(&#039;subdirs&#039;=&amp;gt;false, &#039;maxfiles&#039;=&amp;gt;99, &#039;maxbytes&#039;=&amp;gt;$maxbytes, &#039;trusttext&#039;=&amp;gt;true, &#039;context&#039;=&amp;gt;$context);&lt;br /&gt;
$attachmentoptions = array(&#039;subdirs&#039;=&amp;gt;false, &#039;maxfiles&#039;=&amp;gt;99, &#039;maxbytes&#039;=&amp;gt;$maxbytes);&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note that the data being passed to the form constructor have changed also, but this is not part of the file API changes, I just include them to avoid confusion.&lt;br /&gt;
&lt;br /&gt;
These options are for the htmleditor (definition field) and the filemanager (attachment field). They are used by a file called edit_form.php.&lt;br /&gt;
&lt;br /&gt;
=== Element preparation ===&lt;br /&gt;
Before we look at this, however, we need to &amp;quot;prepare&amp;quot; the elements so that they can correctly display existing embedded images and attached files when you are editing a record instead of just creating one. So, let&#039;s take the code we&#039;ve got so far in edit.php and add to it:&lt;br /&gt;
&lt;br /&gt;
Currently upgraded code in edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code with element preparation:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$entry = file_prepare_standard_editor($entry, &#039;definition&#039;, $definitionoptions, $context, &#039;glossary_entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_prepare_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;glossary_attachment&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Things to note:&lt;br /&gt;
* $entry in this case is simply a stdClass object which may either represent a new glossary entry or an existing one.&lt;br /&gt;
* $entry-&amp;gt;id must be the unique identifier for the current object. If we are creating a new entry, it will be null, but in all cases it must be defined.&lt;br /&gt;
* These two functions (file_prepare_standard_editor and file_prepare_standard_filemanager) are shortcuts functions that take care of some of the tedious setting up for you, but they make a couple of assumptions:&lt;br /&gt;
*# You &#039;&#039;&#039;must&#039;&#039;&#039; name the form element as {element}_editor or {element}_filemanager (see next section)&lt;br /&gt;
*# You &#039;&#039;&#039;must&#039;&#039;&#039; have at least the following fields in the database: {element} and {element}summary, as described earlier in this documentation&lt;br /&gt;
&lt;br /&gt;
We can now look at the upgrades needed in the form definition file.&lt;br /&gt;
&lt;br /&gt;
=== Form definition ===&lt;br /&gt;
Previous code in mod/glossary/edit_form.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;htmleditor&#039;, &#039;definition&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), array(&#039;rows&#039;=&amp;gt;20));&lt;br /&gt;
$mform-&amp;gt;setType(&#039;definition&#039;, PARAM_RAW);&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;definition&#039;, null, &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;definition&#039;, array(&#039;writing&#039;, &#039;richtext&#039;), false, &#039;editorhelpbutton&#039;);&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;format&#039;);&lt;br /&gt;
// a bit further...&lt;br /&gt;
$this-&amp;gt;set_upload_manager(new upload_manager(&#039;attachment&#039;, true, false, $COURSE, false, 0, true, true, false));&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;attachment&#039;, array(&#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), &#039;glossary&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$definitionoptions = $this-&amp;gt;_customdata[&#039;definitionoptions&#039;];&lt;br /&gt;
$attachmentoptions = $this-&amp;gt;_customdata[&#039;attachmentoptions&#039;];&lt;br /&gt;
// a bit further...&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;definition_editor&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), null, $definitionoptions);&lt;br /&gt;
$mform-&amp;gt;setType(&#039;definition_editor&#039;, PARAM_RAW);&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;definition_editor&#039;, get_string(&#039;required&#039;), &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
// a bit further...&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filemanager&#039;, &#039;attachment_filemanager&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), null, $attachmentoptions);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;attachment_filemanager&#039;, array(&#039;attachment2&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), &#039;glossary&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the following:&lt;br /&gt;
* The format element and the help button are no longer required for the HTML editor element&lt;br /&gt;
* The name of the form element needs to be changed by adding &#039;_editor&#039; or &#039;_manager&#039; to the original name. This is a naming convention that is used by a couple of functions we will look at shortly&lt;br /&gt;
&lt;br /&gt;
=== Handling submitted data ===&lt;br /&gt;
The final step is to handle the submitted data properly, i.e. retrieve the files and save them to disk, associating them with the record we have just created (a glossary entry in our example). This happens in edit.php:&lt;br /&gt;
&lt;br /&gt;
Previous code in edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Section that updates an entry:&lt;br /&gt;
$todb-&amp;gt;id = $e;&lt;br /&gt;
$dir = glossary_file_area_name($todb);&lt;br /&gt;
if ($mform-&amp;gt;save_files($dir) and $newfilename = $mform-&amp;gt;get_new_filename()) {&lt;br /&gt;
    $todb-&amp;gt;attachment = $newfilename;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Section that adds an entry:&lt;br /&gt;
if ($todb-&amp;gt;id = insert_record(&amp;quot;glossary_entries&amp;quot;, $todb)) {&lt;br /&gt;
    $e = $todb-&amp;gt;id;&lt;br /&gt;
    $dir = glossary_file_area_name($todb);&lt;br /&gt;
    if ($mform-&amp;gt;save_files($dir) and $newfilename = $mform-&amp;gt;get_new_filename()) {&lt;br /&gt;
        set_field(&amp;quot;glossary_entries&amp;quot;, &amp;quot;attachment&amp;quot;, $newfilename, &amp;quot;id&amp;quot;, $todb-&amp;gt;id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// $todb was renamed to $entry, and the code was refactored &lt;br /&gt;
// so that the file-handling code is only used once for either an add or an update action.&lt;br /&gt;
// If an entry is being added, $DB-&amp;gt;insert() has already been called, so we have a valid $entry-&amp;gt;id&lt;br /&gt;
$entry = file_postupdate_standard_editor($entry, &#039;definition&#039;, $definitionoptions, $context, &#039;glossary_entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_postupdate_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;glossary_attachment&#039;, $entry-&amp;gt;id);&lt;br /&gt;
// store the updated value values&lt;br /&gt;
$DB-&amp;gt;update_record(&#039;glossary_entries&#039;, $entry);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Things to note:&lt;br /&gt;
* If you are adding a new record, you will still need to call update_record after calling the file_postupdate* functions&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[File API]]&lt;br /&gt;
* [[Using the file API]]&lt;br /&gt;
* [[Repository API]]&lt;br /&gt;
* [[Portfolio API]]&lt;br /&gt;
* MDL-14589 - File API Meta issue&lt;br /&gt;
&lt;br /&gt;
{{CategoryDeveloper}}&lt;br /&gt;
[[Category:Files]]&lt;br /&gt;
[[Category:Repositories]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Using_the_File_API_in_Moodle_forms&amp;diff=13028</id>
		<title>Using the File API in Moodle forms</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Using_the_File_API_in_Moodle_forms&amp;diff=13028"/>
		<updated>2009-09-25T07:42:02Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Submitted data handling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
This document shows you exactly how to use Moodle forms to get files from users in a standard and secure way.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 all files are stored in a central database accessible via the [[File API|File API]], and every file is associated with a &amp;quot;file area&amp;quot; in Moodle, such as a particular module.&lt;br /&gt;
&lt;br /&gt;
A common use case is to provide a form (using Moodle&#039;s [[lib/formslib.php|Forms API]]) which allows users to upload or import files as attachments or media embedded into HTML.&lt;br /&gt;
&lt;br /&gt;
Normally this works like this:&lt;br /&gt;
# User starts creation or re-edits an existing item in Moodle (eg forum post, resource, glossary entry etc)&lt;br /&gt;
# User presses some sort of button to browse for new files to attach or embed&lt;br /&gt;
# User sees our &amp;quot;Choose file...&amp;quot; dialog, which contains one or more repository instances. &lt;br /&gt;
# User chooses a file, the [[Repository API|Repository API]] takes care of copying the file into a &amp;quot;draft file area&amp;quot; within Moodle&lt;br /&gt;
# File appears in the text or as an attachment in the form.&lt;br /&gt;
# When the user hits save, the [[File API|File API]] is invoked to move the file from the draft file area into a permanent file area associated with that data &lt;br /&gt;
&lt;br /&gt;
This document shows you exactly how to use Moodle forms to interact with users in a standard and secure way.&lt;br /&gt;
&lt;br /&gt;
If you just want to write code to manipulate Moodle files internally (without user input) then see [[Using_the_File_API]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Form elements== &lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 there are three file-related form elements for interacting with users:&lt;br /&gt;
&lt;br /&gt;
# filemanager - the way to attach one or more files as a set&lt;br /&gt;
# editor - the way to specify a textarea with a HTML editor, and all the handling of images and movies within that HTML&lt;br /&gt;
# filepicker - a way to specify one file for the case when you want to process the file and throw it away &lt;br /&gt;
&lt;br /&gt;
In Moodle 1.9 there were two other types which are now &#039;&#039;&#039;deprecated&#039;&#039;&#039; (they work, but please do not use these anymore)&lt;br /&gt;
# file - used to just allow a normal file upload from the desktop only.&lt;br /&gt;
# htmleditor - this old method of embedding a HTML editor in a textarea is not able to support repositories etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===filepicker===&lt;br /&gt;
&lt;br /&gt;
File picker (&#039;&#039;filepicker&#039;&#039;) is a direct replacement of the older &#039;&#039;file&#039;&#039; formslib element. &lt;br /&gt;
&lt;br /&gt;
It is intended for situations when you want the user to upload &#039;&#039;&#039;one&#039;&#039;&#039; file so you can process it and delete it, such as when you are importing data from a CSV file.&lt;br /&gt;
&lt;br /&gt;
==== Using the filepicker element ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filepicker&#039;, &#039;userfile&#039;, get_string(&#039;file&#039;), null, array(&#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;filetypes&#039; =&amp;gt; &#039;*&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Obtain the chosen file ====&lt;br /&gt;
&lt;br /&gt;
The API for getting file contents is exactly the same as for &#039;&#039;file&#039;&#039; element.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$content = $mform-&amp;gt;get_file_content(&#039;userfile&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== filemanager ===&lt;br /&gt;
&lt;br /&gt;
The File Manager element improves on file picker by allowing you to manage more than one file.  It is expected that the files will be stored permanently for future use (such as forum and glossary attachments).&lt;br /&gt;
&lt;br /&gt;
==== Add file manager element ====&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filemanager&#039;, &#039;attachments&#039;, get_string(&#039;attachment&#039;, &#039;moodle&#039;), null,&lt;br /&gt;
                    array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50, &#039;filetypes&#039; =&amp;gt; array(&#039;document&#039;) ));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here are the fields for filemanager:&lt;br /&gt;
&lt;br /&gt;
;&#039;filemanager&#039;:This is a filemanager element :)&lt;br /&gt;
;elementname:The unique name of the element in the form&lt;br /&gt;
;elementlabel:The label string that users see &lt;br /&gt;
;attributes:(leave it as null)&lt;br /&gt;
;options: an array of further options for the filepicker (see below)&lt;br /&gt;
&lt;br /&gt;
The options array can contain:&lt;br /&gt;
&lt;br /&gt;
;subdirs:(Default 0) Are subdirectories allowed?  (true or false)&lt;br /&gt;
;maxbytes:(Default 0) Restricts the total size of all the files.&lt;br /&gt;
;maxfiles:(Default -1) Restricts the total number of files.&lt;br /&gt;
;filetypes:(Default *) You can specify what file types are accepted by filemanager.  All current file types are listed in this file: [http://cvs.moodle.org/moodle/lib/file/file_types.mm moodle/lib/file/file_types.mm].  This is a [http://freemind.sourceforge.net/wiki/index.php/Main_Page freemind] file: if it is edited the changes will be immediately reflected in Moodle.  Example usage:  &#039;&#039;&#039;array(&#039;audio&#039;, &#039;video&#039;, &#039;documents&#039;)&#039;&#039;&#039;, you can include file extensions as well, for example: &#039;&#039;&#039;array(&#039;*.txt&#039;, &#039;*.jpg&#039;, &#039;audio&#039;)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Load existing files into draft area ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (empty($entry-&amp;gt;id)) {&lt;br /&gt;
    $entry = new object();&lt;br /&gt;
    $entry-&amp;gt;id = null;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$draftitemid = file_get_submitted_draft_itemid(&#039;attachments&#039;);&lt;br /&gt;
file_prepare_draft_area($draftitemid, $context-&amp;gt;id, &#039;glossary_attachment&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50));&lt;br /&gt;
$entry-&amp;gt;attachments = $draftitemid;&lt;br /&gt;
&lt;br /&gt;
$mform-&amp;gt;set_data($entry);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Store updated set of files ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
    // ... store or update $entry&lt;br /&gt;
    file_save_draft_area_files($data-&amp;gt;attachments, $context-&amp;gt;id, &#039;glossary_attachment&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===editor===&lt;br /&gt;
There are two way for using of editor element in code, the first one is easier but expects some standardized fields. The second method is more low level.&lt;br /&gt;
&lt;br /&gt;
====Simple use====&lt;br /&gt;
# name database fields: &#039;&#039;textfield&#039;&#039;, &#039;&#039;textfieldformat&#039;&#039; (and &#039;&#039;textfieldtrust&#039;&#039; if required)&lt;br /&gt;
# create options array &amp;lt;code php&amp;gt;$textfieldoptions = array(&#039;trusttext&#039;=&amp;gt;true, &#039;subdirs&#039;=&amp;gt;true, &#039;maxfiles&#039;=&amp;gt;$maxfiles, &#039;maxbytes&#039;=&amp;gt;$maxbytes);&amp;lt;/code&amp;gt;&lt;br /&gt;
# add editor &#039;&#039;textfield_editor&#039;&#039; to moodle form, pass options through custom data in form constructor, set $data-&amp;gt;id to null if data not exist yet &amp;lt;code php&amp;gt;$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;textfield_editor&#039;, get_string(&#039;fieldname&#039;, &#039;somemodule&#039;), null, $textfieldoptions);&amp;lt;/code&amp;gt;&lt;br /&gt;
# prepare data &amp;lt;code php&amp;gt;$data = file_prepare_standard_editor($data, &#039;textfield&#039;, $textfieldoptions, $context, &#039;somemodule_somearea&#039;, $data-&amp;gt;id);&amp;lt;/code&amp;gt;&lt;br /&gt;
# get submitted data and after inserting/updating of data &amp;lt;code php&amp;gt;$data = file_postupdate_standard_editor($data, &#039;textfield&#039;, $textfieldoptions, $context, &#039;somemodule_somearea&#039;, $data-&amp;gt;id);&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Real world examples are in mod/glossary/edit.php and mod/glossary/comment.php&lt;br /&gt;
&lt;br /&gt;
====Low level use====&lt;br /&gt;
&lt;br /&gt;
When using editor element you  need to preprocess and postprocess the data:&lt;br /&gt;
# detect if form was already submitted (usually means draft is area already exists) - &#039;&#039;file_get_submitted_draft_itemid()&#039;&#039;&lt;br /&gt;
# prepare draft file area, temporary storage of all files attached to the text - &#039;&#039;file_prepare_draft_area()&#039;&#039;&lt;br /&gt;
# convert encoded relative links to absolute links - &#039;&#039;file_prepare_draft_area()&#039;&#039;&lt;br /&gt;
# create form and set current data&lt;br /&gt;
# after submission the changed files must be merged back into original area - &#039;&#039;file_save_draft_area_files()&#039;&#039;&lt;br /&gt;
# absolute links have to be replaced by relative links - &#039;&#039;file_save_draft_area_files()&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====Replace old htmleditor with editor=====&lt;br /&gt;
&lt;br /&gt;
The file picker has been integrated with with TinyMCE to make the editor element. This new element should support all types on editors and should be able to switch them on-the-fly. Instances of the old htmleditor element in your forms should be replaced by the new editor element, this may need adding of new format and trusttext columns. For example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;entry&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), null,&lt;br /&gt;
        array(&#039;maxfiles&#039; =&amp;gt; EDITOR_UNLIMITED_FILES, &#039;filearea&#039; =&amp;gt; &#039;glossary_entry&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The editor element can take following options: maxfiles, maxbytes, filearea, subdirs and changeformat. Please note that the embedded files is optional feature and is not expected be used everywhere.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the editor element now includes text format option. You should no longer use the separate format element type.&lt;br /&gt;
&lt;br /&gt;
=====Prepare current data - text and files=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (empty($entry-&amp;gt;id)) {&lt;br /&gt;
  $entry = new object();&lt;br /&gt;
  $entry-&amp;gt;id = null;&lt;br /&gt;
  $entry-&amp;gt;definition = &#039;&#039;;&lt;br /&gt;
  $entry-&amp;gt;format = FORMAT_HTML;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$draftid_editor = file_get_submitted_draft_itemid(&#039;entry&#039;);&lt;br /&gt;
$currenttext = file_prepare_draft_area($draftid_editor, $context-&amp;gt;id, &#039;glossary_entry&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039;=&amp;gt;true), $entry-&amp;gt;definition);&lt;br /&gt;
$entry-&amp;gt;entry = array(&#039;text&#039;=&amp;gt;$currenttext, &#039;format&#039;=&amp;gt;$entry-&amp;gt;format, &#039;itemid&#039;=&amp;gt;$draftid_editor);&lt;br /&gt;
&lt;br /&gt;
$mform-&amp;gt;set_data($entry);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there are multiple files, they will share the same itemid.&lt;br /&gt;
&lt;br /&gt;
=====Obtain text, format and save draft files=====&lt;br /&gt;
&lt;br /&gt;
To retrieve editor content, you need to use following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($fromform = $mform-&amp;gt;get_data()) {&lt;br /&gt;
    // content of editor&lt;br /&gt;
    $messagetext = $fromform-&amp;gt;entry[&#039;text&#039;];&lt;br /&gt;
    // format of content&lt;br /&gt;
    $messageformat  = $fromform-&amp;gt;entry[&#039;format&#039;];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a user selects a file using the file picker, the file is initially stored in a draft file area, and a URL is inserted into the HTML in the editor that lets the person editing the content (but no one else) see the file.&lt;br /&gt;
&lt;br /&gt;
When the user submits the form, we then need to save the draft files to the correct place in permanent storage. (Just like you have to call $DB-&amp;gt;update_record(&#039;tablename&#039;, $data); to have the other parts of the form submission stored correctly.)&lt;br /&gt;
&lt;br /&gt;
The save_files_from_draft_area function and replace absolute links with internal relative links do:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$messagetext = file_save_draft_area_files($draftid_editor, $context-&amp;gt;id, &#039;glossary_entry&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039;=&amp;gt;true), $messagetext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
; $context-&amp;gt;id, &#039;proper_file_area&#039; and $entry-&amp;gt;id : correspond to the contextid, filearea and itemid columns in the [[File_API#Table:_files|files table]].&lt;br /&gt;
; $messagetext : this is the message text. As the files are saved to the real file area, the URLs in this content are rewritten.&lt;br /&gt;
&lt;br /&gt;
All URLs in content that point to files managed to the File API are converted to a form that starts &#039;@@PLUGINFILE@@/&#039; before the content is stored in the database. That is what we mean by rewriting.&lt;br /&gt;
&lt;br /&gt;
== File serving==&lt;br /&gt;
&lt;br /&gt;
=== Convert internal relative links to absolute links ===&lt;br /&gt;
&lt;br /&gt;
Before text content is displayed to the user, any URLs in the &#039;@@PLUGINFILE@@/&#039; form in the content need to be rewritten to the real URL where the user can access the files. &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$messagetext = file_rewrite_pluginfile_urls($messagetext, &#039;pluginfile.php&#039;,&lt;br /&gt;
        &amp;quot;$context-&amp;gt;id/proper_file_area/$itemid/&amp;quot;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
; $messagetext : is the content containing the @@PLUGINFILE@@ URLs from the database.&lt;br /&gt;
; &#039;pluginfile.php&#039; : there are a number of different scripts that can serve files with different permissions checks. You need to specify which one to use.&lt;br /&gt;
; &amp;quot;$context-&amp;gt;id/proper_file_area/$itemid/&amp;quot; : uniquely identifies the file area, as before.&lt;br /&gt;
&lt;br /&gt;
=== Implement file serving access control ===&lt;br /&gt;
&lt;br /&gt;
Attachments and embedded images should have the same access control like the text itself, in majority of cases these files are served using pluginfile.php. Access control is defined in &#039;&#039;module/lib.php&#039;&#039; file in function &#039;&#039;module_pluginfile()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File browsing support ==&lt;br /&gt;
Only owner of each file area is allowed to use low level File API function to access files, other parts of Moodle should use file browsing API.&lt;br /&gt;
&lt;br /&gt;
Activities may specify browsing support in own module/lib.php file by implementing functions module_get_file_areas() and module_get_file_info().&lt;br /&gt;
&lt;br /&gt;
== Upgrading your code ==&lt;br /&gt;
Here I will attempt to describe some simple steps you can take to upgrade your file-handling form elements from pre-2.0 code to 2.0. We will use the example of glossary, since it has been used above.&lt;br /&gt;
&lt;br /&gt;
=== Preparing your options ===&lt;br /&gt;
Unless you are happy with the defaults, you will need to define an array of options for each file-handling form element. You could define it at different places, but it&#039;s best to put it in one place and make the array(s) available to other files if they need it. In the majority of cases, this will be in a file like edit.php&lt;br /&gt;
&lt;br /&gt;
Previous code in mod/glossary/edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform =&amp;amp; new mod_glossary_entry_form(null, compact(&#039;cm&#039;, &#039;glossary&#039;, &#039;hook&#039;, &#039;mode&#039;, &#039;e&#039;, &#039;context&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$maxbytes = $course-&amp;gt;maxbytes; // Could also use $CFG-&amp;gt;maxbytes if you are not coding within a course context&lt;br /&gt;
$definitionoptions = array(&#039;trusttext&#039;=&amp;gt;true, &#039;subdirs&#039;=&amp;gt;false, &#039;maxfiles&#039;=&amp;gt;99, &#039;maxbytes&#039;=&amp;gt;$maxbytes, &#039;trusttext&#039;=&amp;gt;true, &#039;context&#039;=&amp;gt;$context);&lt;br /&gt;
$attachmentoptions = array(&#039;subdirs&#039;=&amp;gt;false, &#039;maxfiles&#039;=&amp;gt;99, &#039;maxbytes&#039;=&amp;gt;$maxbytes);&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note that the data being passed to the form constructor have changed also, but this is not part of the file API changes, I just include them to avoid confusion.&lt;br /&gt;
&lt;br /&gt;
These options are for the htmleditor (definition field) and the filemanager (attachment field). They are used by a file called edit_form.php. &lt;br /&gt;
&lt;br /&gt;
=== Element preparation ===&lt;br /&gt;
Before we look at this, however, we need to &amp;quot;prepare&amp;quot; the elements so that they can correctly display existing embedded images and attached files when you are editing a record instead of just creating one. So, let&#039;s take the code we&#039;ve got so far in edit.php and add to it:&lt;br /&gt;
&lt;br /&gt;
Currently upgraded code in edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code with element preparation:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$entry = file_prepare_standard_editor($entry, &#039;definition&#039;, $definitionoptions, $context, &#039;glossary_entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_prepare_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;glossary_attachment&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Things to note:&lt;br /&gt;
* $entry in this case is simply a stdClass object which may either represent a new glossary entry or an existing one.&lt;br /&gt;
* $entry-&amp;gt;id must be the unique identifier for the current object. If we are creating a new entry, it will be null, but in all cases it must be defined.&lt;br /&gt;
* These two functions (file_prepare_standard_editor and file_prepare_standard_filemanager) are shortcuts functions that take care of some of the tedious setting up for you, but they make a couple of assumptions:&lt;br /&gt;
*# You &#039;&#039;&#039;must&#039;&#039;&#039; name the form element as {element}_editor or {element}_filemanager (see next section)&lt;br /&gt;
*# You &#039;&#039;&#039;must&#039;&#039;&#039; have at least the following fields in the database: {element} and {element}summary, as described earlier in this documentation&lt;br /&gt;
&lt;br /&gt;
We can now look at the upgrades needed in the form definition file.&lt;br /&gt;
&lt;br /&gt;
=== Form definition ===&lt;br /&gt;
Previous code in mod/glossary/edit_form.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;htmleditor&#039;, &#039;definition&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), array(&#039;rows&#039;=&amp;gt;20));&lt;br /&gt;
$mform-&amp;gt;setType(&#039;definition&#039;, PARAM_RAW);&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;definition&#039;, null, &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;definition&#039;, array(&#039;writing&#039;, &#039;richtext&#039;), false, &#039;editorhelpbutton&#039;);&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;format&#039;);&lt;br /&gt;
// a bit further...&lt;br /&gt;
$this-&amp;gt;set_upload_manager(new upload_manager(&#039;attachment&#039;, true, false, $COURSE, false, 0, true, true, false));&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;attachment&#039;, array(&#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), &#039;glossary&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$definitionoptions = $this-&amp;gt;_customdata[&#039;definitionoptions&#039;];&lt;br /&gt;
$attachmentoptions = $this-&amp;gt;_customdata[&#039;attachmentoptions&#039;];&lt;br /&gt;
// a bit further...&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;definition_editor&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), null, $definitionoptions);&lt;br /&gt;
$mform-&amp;gt;setType(&#039;definition_editor&#039;, PARAM_RAW);&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;definition_editor&#039;, get_string(&#039;required&#039;), &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
// a bit further...&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filemanager&#039;, &#039;attachment_filemanager&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), null, $attachmentoptions);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;attachment_filemanager&#039;, array(&#039;attachment2&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), &#039;glossary&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the following:&lt;br /&gt;
* The format element and the help button are no longer required for the HTML editor element&lt;br /&gt;
* The name of the form element needs to be changed by adding &#039;_editor&#039; or &#039;_manager&#039; to the original name. This is a naming convention that is used by a couple of functions we will look at shortly&lt;br /&gt;
&lt;br /&gt;
=== Handling submitted data ===&lt;br /&gt;
The final step is to handle the submitted data properly, i.e. retrieve the files and save them to disk, associating them with the record we have just created (a glossary entry in our example). This happens in edit.php:&lt;br /&gt;
&lt;br /&gt;
Previous code in edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Section that updates an entry:&lt;br /&gt;
$todb-&amp;gt;id = $e;&lt;br /&gt;
$dir = glossary_file_area_name($todb);&lt;br /&gt;
if ($mform-&amp;gt;save_files($dir) and $newfilename = $mform-&amp;gt;get_new_filename()) {&lt;br /&gt;
    $todb-&amp;gt;attachment = $newfilename;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Section that adds an entry:&lt;br /&gt;
if ($todb-&amp;gt;id = insert_record(&amp;quot;glossary_entries&amp;quot;, $todb)) {&lt;br /&gt;
    $e = $todb-&amp;gt;id;&lt;br /&gt;
    $dir = glossary_file_area_name($todb);&lt;br /&gt;
    if ($mform-&amp;gt;save_files($dir) and $newfilename = $mform-&amp;gt;get_new_filename()) {&lt;br /&gt;
        set_field(&amp;quot;glossary_entries&amp;quot;, &amp;quot;attachment&amp;quot;, $newfilename, &amp;quot;id&amp;quot;, $todb-&amp;gt;id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// $todb was renamed to $entry, and the code was refactored &lt;br /&gt;
// so that the file-handling code is only used once for either an add or an update action.&lt;br /&gt;
// If an entry is being added, $DB-&amp;gt;insert() has already been called, so we have a valid $entry-&amp;gt;id&lt;br /&gt;
$entry = file_postupdate_standard_editor($entry, &#039;definition&#039;, $definitionoptions, $context, &#039;glossary_entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_postupdate_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;glossary_attachment&#039;, $entry-&amp;gt;id);&lt;br /&gt;
// store the updated value values&lt;br /&gt;
$DB-&amp;gt;update_record(&#039;glossary_entries&#039;, $entry);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Things to note:&lt;br /&gt;
* If you are adding a new record, you will still need to call update_record after calling the file_postupdate* functions&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[File API]]&lt;br /&gt;
* [[Using the file API]]&lt;br /&gt;
* [[Repository API]]&lt;br /&gt;
* [[Portfolio API]]&lt;br /&gt;
* MDL-14589 - File API Meta issue&lt;br /&gt;
&lt;br /&gt;
{{CategoryDeveloper}}&lt;br /&gt;
[[Category:Files]]&lt;br /&gt;
[[Category:Repositories]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Using_the_File_API_in_Moodle_forms&amp;diff=13027</id>
		<title>Using the File API in Moodle forms</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Using_the_File_API_in_Moodle_forms&amp;diff=13027"/>
		<updated>2009-09-25T07:38:14Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Element preparation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
This document shows you exactly how to use Moodle forms to get files from users in a standard and secure way.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 all files are stored in a central database accessible via the [[File API|File API]], and every file is associated with a &amp;quot;file area&amp;quot; in Moodle, such as a particular module.&lt;br /&gt;
&lt;br /&gt;
A common use case is to provide a form (using Moodle&#039;s [[lib/formslib.php|Forms API]]) which allows users to upload or import files as attachments or media embedded into HTML.&lt;br /&gt;
&lt;br /&gt;
Normally this works like this:&lt;br /&gt;
# User starts creation or re-edits an existing item in Moodle (eg forum post, resource, glossary entry etc)&lt;br /&gt;
# User presses some sort of button to browse for new files to attach or embed&lt;br /&gt;
# User sees our &amp;quot;Choose file...&amp;quot; dialog, which contains one or more repository instances. &lt;br /&gt;
# User chooses a file, the [[Repository API|Repository API]] takes care of copying the file into a &amp;quot;draft file area&amp;quot; within Moodle&lt;br /&gt;
# File appears in the text or as an attachment in the form.&lt;br /&gt;
# When the user hits save, the [[File API|File API]] is invoked to move the file from the draft file area into a permanent file area associated with that data &lt;br /&gt;
&lt;br /&gt;
This document shows you exactly how to use Moodle forms to interact with users in a standard and secure way.&lt;br /&gt;
&lt;br /&gt;
If you just want to write code to manipulate Moodle files internally (without user input) then see [[Using_the_File_API]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Form elements== &lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 there are three file-related form elements for interacting with users:&lt;br /&gt;
&lt;br /&gt;
# filemanager - the way to attach one or more files as a set&lt;br /&gt;
# editor - the way to specify a textarea with a HTML editor, and all the handling of images and movies within that HTML&lt;br /&gt;
# filepicker - a way to specify one file for the case when you want to process the file and throw it away &lt;br /&gt;
&lt;br /&gt;
In Moodle 1.9 there were two other types which are now &#039;&#039;&#039;deprecated&#039;&#039;&#039; (they work, but please do not use these anymore)&lt;br /&gt;
# file - used to just allow a normal file upload from the desktop only.&lt;br /&gt;
# htmleditor - this old method of embedding a HTML editor in a textarea is not able to support repositories etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===filepicker===&lt;br /&gt;
&lt;br /&gt;
File picker (&#039;&#039;filepicker&#039;&#039;) is a direct replacement of the older &#039;&#039;file&#039;&#039; formslib element. &lt;br /&gt;
&lt;br /&gt;
It is intended for situations when you want the user to upload &#039;&#039;&#039;one&#039;&#039;&#039; file so you can process it and delete it, such as when you are importing data from a CSV file.&lt;br /&gt;
&lt;br /&gt;
==== Using the filepicker element ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filepicker&#039;, &#039;userfile&#039;, get_string(&#039;file&#039;), null, array(&#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;filetypes&#039; =&amp;gt; &#039;*&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Obtain the chosen file ====&lt;br /&gt;
&lt;br /&gt;
The API for getting file contents is exactly the same as for &#039;&#039;file&#039;&#039; element.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$content = $mform-&amp;gt;get_file_content(&#039;userfile&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== filemanager ===&lt;br /&gt;
&lt;br /&gt;
The File Manager element improves on file picker by allowing you to manage more than one file.  It is expected that the files will be stored permanently for future use (such as forum and glossary attachments).&lt;br /&gt;
&lt;br /&gt;
==== Add file manager element ====&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filemanager&#039;, &#039;attachments&#039;, get_string(&#039;attachment&#039;, &#039;moodle&#039;), null,&lt;br /&gt;
                    array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50, &#039;filetypes&#039; =&amp;gt; array(&#039;document&#039;) ));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here are the fields for filemanager:&lt;br /&gt;
&lt;br /&gt;
;&#039;filemanager&#039;:This is a filemanager element :)&lt;br /&gt;
;elementname:The unique name of the element in the form&lt;br /&gt;
;elementlabel:The label string that users see &lt;br /&gt;
;attributes:(leave it as null)&lt;br /&gt;
;options: an array of further options for the filepicker (see below)&lt;br /&gt;
&lt;br /&gt;
The options array can contain:&lt;br /&gt;
&lt;br /&gt;
;subdirs:(Default 0) Are subdirectories allowed?  (true or false)&lt;br /&gt;
;maxbytes:(Default 0) Restricts the total size of all the files.&lt;br /&gt;
;maxfiles:(Default -1) Restricts the total number of files.&lt;br /&gt;
;filetypes:(Default *) You can specify what file types are accepted by filemanager.  All current file types are listed in this file: [http://cvs.moodle.org/moodle/lib/file/file_types.mm moodle/lib/file/file_types.mm].  This is a [http://freemind.sourceforge.net/wiki/index.php/Main_Page freemind] file: if it is edited the changes will be immediately reflected in Moodle.  Example usage:  &#039;&#039;&#039;array(&#039;audio&#039;, &#039;video&#039;, &#039;documents&#039;)&#039;&#039;&#039;, you can include file extensions as well, for example: &#039;&#039;&#039;array(&#039;*.txt&#039;, &#039;*.jpg&#039;, &#039;audio&#039;)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Load existing files into draft area ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (empty($entry-&amp;gt;id)) {&lt;br /&gt;
    $entry = new object();&lt;br /&gt;
    $entry-&amp;gt;id = null;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$draftitemid = file_get_submitted_draft_itemid(&#039;attachments&#039;);&lt;br /&gt;
file_prepare_draft_area($draftitemid, $context-&amp;gt;id, &#039;glossary_attachment&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50));&lt;br /&gt;
$entry-&amp;gt;attachments = $draftitemid;&lt;br /&gt;
&lt;br /&gt;
$mform-&amp;gt;set_data($entry);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Store updated set of files ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
    // ... store or update $entry&lt;br /&gt;
    file_save_draft_area_files($data-&amp;gt;attachments, $context-&amp;gt;id, &#039;glossary_attachment&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===editor===&lt;br /&gt;
There are two way for using of editor element in code, the first one is easier but expects some standardized fields. The second method is more low level.&lt;br /&gt;
&lt;br /&gt;
====Simple use====&lt;br /&gt;
# name database fields: &#039;&#039;textfield&#039;&#039;, &#039;&#039;textfieldformat&#039;&#039; (and &#039;&#039;textfieldtrust&#039;&#039; if required)&lt;br /&gt;
# create options array &amp;lt;code php&amp;gt;$textfieldoptions = array(&#039;trusttext&#039;=&amp;gt;true, &#039;subdirs&#039;=&amp;gt;true, &#039;maxfiles&#039;=&amp;gt;$maxfiles, &#039;maxbytes&#039;=&amp;gt;$maxbytes);&amp;lt;/code&amp;gt;&lt;br /&gt;
# add editor &#039;&#039;textfield_editor&#039;&#039; to moodle form, pass options through custom data in form constructor, set $data-&amp;gt;id to null if data not exist yet &amp;lt;code php&amp;gt;$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;textfield_editor&#039;, get_string(&#039;fieldname&#039;, &#039;somemodule&#039;), null, $textfieldoptions);&amp;lt;/code&amp;gt;&lt;br /&gt;
# prepare data &amp;lt;code php&amp;gt;$data = file_prepare_standard_editor($data, &#039;textfield&#039;, $textfieldoptions, $context, &#039;somemodule_somearea&#039;, $data-&amp;gt;id);&amp;lt;/code&amp;gt;&lt;br /&gt;
# get submitted data and after inserting/updating of data &amp;lt;code php&amp;gt;$data = file_postupdate_standard_editor($data, &#039;textfield&#039;, $textfieldoptions, $context, &#039;somemodule_somearea&#039;, $data-&amp;gt;id);&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Real world examples are in mod/glossary/edit.php and mod/glossary/comment.php&lt;br /&gt;
&lt;br /&gt;
====Low level use====&lt;br /&gt;
&lt;br /&gt;
When using editor element you  need to preprocess and postprocess the data:&lt;br /&gt;
# detect if form was already submitted (usually means draft is area already exists) - &#039;&#039;file_get_submitted_draft_itemid()&#039;&#039;&lt;br /&gt;
# prepare draft file area, temporary storage of all files attached to the text - &#039;&#039;file_prepare_draft_area()&#039;&#039;&lt;br /&gt;
# convert encoded relative links to absolute links - &#039;&#039;file_prepare_draft_area()&#039;&#039;&lt;br /&gt;
# create form and set current data&lt;br /&gt;
# after submission the changed files must be merged back into original area - &#039;&#039;file_save_draft_area_files()&#039;&#039;&lt;br /&gt;
# absolute links have to be replaced by relative links - &#039;&#039;file_save_draft_area_files()&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====Replace old htmleditor with editor=====&lt;br /&gt;
&lt;br /&gt;
The file picker has been integrated with with TinyMCE to make the editor element. This new element should support all types on editors and should be able to switch them on-the-fly. Instances of the old htmleditor element in your forms should be replaced by the new editor element, this may need adding of new format and trusttext columns. For example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;entry&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), null,&lt;br /&gt;
        array(&#039;maxfiles&#039; =&amp;gt; EDITOR_UNLIMITED_FILES, &#039;filearea&#039; =&amp;gt; &#039;glossary_entry&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The editor element can take following options: maxfiles, maxbytes, filearea, subdirs and changeformat. Please note that the embedded files is optional feature and is not expected be used everywhere.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the editor element now includes text format option. You should no longer use the separate format element type.&lt;br /&gt;
&lt;br /&gt;
=====Prepare current data - text and files=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (empty($entry-&amp;gt;id)) {&lt;br /&gt;
  $entry = new object();&lt;br /&gt;
  $entry-&amp;gt;id = null;&lt;br /&gt;
  $entry-&amp;gt;definition = &#039;&#039;;&lt;br /&gt;
  $entry-&amp;gt;format = FORMAT_HTML;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$draftid_editor = file_get_submitted_draft_itemid(&#039;entry&#039;);&lt;br /&gt;
$currenttext = file_prepare_draft_area($draftid_editor, $context-&amp;gt;id, &#039;glossary_entry&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039;=&amp;gt;true), $entry-&amp;gt;definition);&lt;br /&gt;
$entry-&amp;gt;entry = array(&#039;text&#039;=&amp;gt;$currenttext, &#039;format&#039;=&amp;gt;$entry-&amp;gt;format, &#039;itemid&#039;=&amp;gt;$draftid_editor);&lt;br /&gt;
&lt;br /&gt;
$mform-&amp;gt;set_data($entry);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there are multiple files, they will share the same itemid.&lt;br /&gt;
&lt;br /&gt;
=====Obtain text, format and save draft files=====&lt;br /&gt;
&lt;br /&gt;
To retrieve editor content, you need to use following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($fromform = $mform-&amp;gt;get_data()) {&lt;br /&gt;
    // content of editor&lt;br /&gt;
    $messagetext = $fromform-&amp;gt;entry[&#039;text&#039;];&lt;br /&gt;
    // format of content&lt;br /&gt;
    $messageformat  = $fromform-&amp;gt;entry[&#039;format&#039;];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a user selects a file using the file picker, the file is initially stored in a draft file area, and a URL is inserted into the HTML in the editor that lets the person editing the content (but no one else) see the file.&lt;br /&gt;
&lt;br /&gt;
When the user submits the form, we then need to save the draft files to the correct place in permanent storage. (Just like you have to call $DB-&amp;gt;update_record(&#039;tablename&#039;, $data); to have the other parts of the form submission stored correctly.)&lt;br /&gt;
&lt;br /&gt;
The save_files_from_draft_area function and replace absolute links with internal relative links do:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$messagetext = file_save_draft_area_files($draftid_editor, $context-&amp;gt;id, &#039;glossary_entry&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039;=&amp;gt;true), $messagetext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
; $context-&amp;gt;id, &#039;proper_file_area&#039; and $entry-&amp;gt;id : correspond to the contextid, filearea and itemid columns in the [[File_API#Table:_files|files table]].&lt;br /&gt;
; $messagetext : this is the message text. As the files are saved to the real file area, the URLs in this content are rewritten.&lt;br /&gt;
&lt;br /&gt;
All URLs in content that point to files managed to the File API are converted to a form that starts &#039;@@PLUGINFILE@@/&#039; before the content is stored in the database. That is what we mean by rewriting.&lt;br /&gt;
&lt;br /&gt;
== File serving==&lt;br /&gt;
&lt;br /&gt;
=== Convert internal relative links to absolute links ===&lt;br /&gt;
&lt;br /&gt;
Before text content is displayed to the user, any URLs in the &#039;@@PLUGINFILE@@/&#039; form in the content need to be rewritten to the real URL where the user can access the files. &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$messagetext = file_rewrite_pluginfile_urls($messagetext, &#039;pluginfile.php&#039;,&lt;br /&gt;
        &amp;quot;$context-&amp;gt;id/proper_file_area/$itemid/&amp;quot;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
; $messagetext : is the content containing the @@PLUGINFILE@@ URLs from the database.&lt;br /&gt;
; &#039;pluginfile.php&#039; : there are a number of different scripts that can serve files with different permissions checks. You need to specify which one to use.&lt;br /&gt;
; &amp;quot;$context-&amp;gt;id/proper_file_area/$itemid/&amp;quot; : uniquely identifies the file area, as before.&lt;br /&gt;
&lt;br /&gt;
=== Implement file serving access control ===&lt;br /&gt;
&lt;br /&gt;
Attachments and embedded images should have the same access control like the text itself, in majority of cases these files are served using pluginfile.php. Access control is defined in &#039;&#039;module/lib.php&#039;&#039; file in function &#039;&#039;module_pluginfile()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File browsing support ==&lt;br /&gt;
Only owner of each file area is allowed to use low level File API function to access files, other parts of Moodle should use file browsing API.&lt;br /&gt;
&lt;br /&gt;
Activities may specify browsing support in own module/lib.php file by implementing functions module_get_file_areas() and module_get_file_info().&lt;br /&gt;
&lt;br /&gt;
== Upgrading your code ==&lt;br /&gt;
Here I will attempt to describe some simple steps you can take to upgrade your file-handling form elements from pre-2.0 code to 2.0. We will use the example of glossary, since it has been used above.&lt;br /&gt;
&lt;br /&gt;
=== Preparing your options ===&lt;br /&gt;
Unless you are happy with the defaults, you will need to define an array of options for each file-handling form element. You could define it at different places, but it&#039;s best to put it in one place and make the array(s) available to other files if they need it. In the majority of cases, this will be in a file like edit.php&lt;br /&gt;
&lt;br /&gt;
Previous code in mod/glossary/edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform =&amp;amp; new mod_glossary_entry_form(null, compact(&#039;cm&#039;, &#039;glossary&#039;, &#039;hook&#039;, &#039;mode&#039;, &#039;e&#039;, &#039;context&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$maxbytes = $course-&amp;gt;maxbytes; // Could also use $CFG-&amp;gt;maxbytes if you are not coding within a course context&lt;br /&gt;
$definitionoptions = array(&#039;trusttext&#039;=&amp;gt;true, &#039;subdirs&#039;=&amp;gt;false, &#039;maxfiles&#039;=&amp;gt;99, &#039;maxbytes&#039;=&amp;gt;$maxbytes, &#039;trusttext&#039;=&amp;gt;true, &#039;context&#039;=&amp;gt;$context);&lt;br /&gt;
$attachmentoptions = array(&#039;subdirs&#039;=&amp;gt;false, &#039;maxfiles&#039;=&amp;gt;99, &#039;maxbytes&#039;=&amp;gt;$maxbytes);&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note that the data being passed to the form constructor have changed also, but this is not part of the file API changes, I just include them to avoid confusion.&lt;br /&gt;
&lt;br /&gt;
These options are for the htmleditor (definition field) and the filemanager (attachment field). They are used by a file called edit_form.php. &lt;br /&gt;
&lt;br /&gt;
=== Element preparation ===&lt;br /&gt;
Before we look at this, however, we need to &amp;quot;prepare&amp;quot; the elements so that they can correctly display existing embedded images and attached files when you are editing a record instead of just creating one. So, let&#039;s take the code we&#039;ve got so far in edit.php and add to it:&lt;br /&gt;
&lt;br /&gt;
Currently upgraded code in edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code with element preparation:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$entry = file_prepare_standard_editor($entry, &#039;definition&#039;, $definitionoptions, $context, &#039;glossary_entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_prepare_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;glossary_attachment&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Things to note:&lt;br /&gt;
* $entry in this case is simply a stdClass object which may either represent a new glossary entry or an existing one.&lt;br /&gt;
* $entry-&amp;gt;id must be the unique identifier for the current object. If we are creating a new entry, it will be null, but in all cases it must be defined.&lt;br /&gt;
* These two functions (file_prepare_standard_editor and file_prepare_standard_filemanager) are shortcuts functions that take care of some of the tedious setting up for you, but they make a couple of assumptions:&lt;br /&gt;
*# You &#039;&#039;&#039;must&#039;&#039;&#039; name the form element as {element}_editor or {element}_filemanager (see next section)&lt;br /&gt;
*# You &#039;&#039;&#039;must&#039;&#039;&#039; have at least the following fields in the database: {element} and {element}summary, as described earlier in this documentation&lt;br /&gt;
&lt;br /&gt;
We can now look at the upgrades needed in the form definition file.&lt;br /&gt;
&lt;br /&gt;
=== Form definition ===&lt;br /&gt;
Previous code in mod/glossary/edit_form.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;htmleditor&#039;, &#039;definition&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), array(&#039;rows&#039;=&amp;gt;20));&lt;br /&gt;
$mform-&amp;gt;setType(&#039;definition&#039;, PARAM_RAW);&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;definition&#039;, null, &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;definition&#039;, array(&#039;writing&#039;, &#039;richtext&#039;), false, &#039;editorhelpbutton&#039;);&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;format&#039;);&lt;br /&gt;
// a bit further...&lt;br /&gt;
$this-&amp;gt;set_upload_manager(new upload_manager(&#039;attachment&#039;, true, false, $COURSE, false, 0, true, true, false));&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;attachment&#039;, array(&#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), &#039;glossary&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$definitionoptions = $this-&amp;gt;_customdata[&#039;definitionoptions&#039;];&lt;br /&gt;
$attachmentoptions = $this-&amp;gt;_customdata[&#039;attachmentoptions&#039;];&lt;br /&gt;
// a bit further...&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;definition_editor&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), null, $definitionoptions);&lt;br /&gt;
$mform-&amp;gt;setType(&#039;definition_editor&#039;, PARAM_RAW);&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;definition_editor&#039;, get_string(&#039;required&#039;), &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
// a bit further...&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filemanager&#039;, &#039;attachment_filemanager&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), null, $attachmentoptions);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;attachment_filemanager&#039;, array(&#039;attachment2&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), &#039;glossary&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the following:&lt;br /&gt;
* The format element and the help button are no longer required for the HTML editor element&lt;br /&gt;
* The name of the form element needs to be changed by adding &#039;_editor&#039; or &#039;_manager&#039; to the original name. This is a naming convention that is used by a couple of functions we will look at shortly&lt;br /&gt;
&lt;br /&gt;
=== Submitted data handling ===&lt;br /&gt;
The final step is to handle the submitted data properly, i.e. retrieve the files and save them to disk, associating them with the record we have just created (a glossary entry in our example). This happens in edit.php:&lt;br /&gt;
&lt;br /&gt;
Previous code in edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Section that updates an entry:&lt;br /&gt;
$todb-&amp;gt;id = $e;&lt;br /&gt;
$dir = glossary_file_area_name($todb);&lt;br /&gt;
if ($mform-&amp;gt;save_files($dir) and $newfilename = $mform-&amp;gt;get_new_filename()) {&lt;br /&gt;
    $todb-&amp;gt;attachment = $newfilename;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Section that adds an entry:&lt;br /&gt;
if ($todb-&amp;gt;id = insert_record(&amp;quot;glossary_entries&amp;quot;, $todb)) {&lt;br /&gt;
    $e = $todb-&amp;gt;id;&lt;br /&gt;
    $dir = glossary_file_area_name($todb);&lt;br /&gt;
    if ($mform-&amp;gt;save_files($dir) and $newfilename = $mform-&amp;gt;get_new_filename()) {&lt;br /&gt;
        set_field(&amp;quot;glossary_entries&amp;quot;, &amp;quot;attachment&amp;quot;, $newfilename, &amp;quot;id&amp;quot;, $todb-&amp;gt;id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// $todb was renamed to $entry, and the code was refactored &lt;br /&gt;
// so that the file-handling code is only used once for either an add or an update action.&lt;br /&gt;
// If an entry is being added, $DB-&amp;gt;insert() has already been called, so we have a valid $entry-&amp;gt;id&lt;br /&gt;
$entry = file_postupdate_standard_editor($entry, &#039;definition&#039;, $definitionoptions, $context, &#039;glossary_entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_postupdate_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;glossary_attachment&#039;, $entry-&amp;gt;id);&lt;br /&gt;
// store the updated value values&lt;br /&gt;
$DB-&amp;gt;update_record(&#039;glossary_entries&#039;, $entry);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Things to note:&lt;br /&gt;
* If you are adding a new record, you will still need to call update_record after calling the file_postupdate* functions&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[File API]]&lt;br /&gt;
* [[Using the file API]]&lt;br /&gt;
* [[Repository API]]&lt;br /&gt;
* [[Portfolio API]]&lt;br /&gt;
* MDL-14589 - File API Meta issue&lt;br /&gt;
&lt;br /&gt;
{{CategoryDeveloper}}&lt;br /&gt;
[[Category:Files]]&lt;br /&gt;
[[Category:Repositories]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Using_the_File_API_in_Moodle_forms&amp;diff=13026</id>
		<title>Using the File API in Moodle forms</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Using_the_File_API_in_Moodle_forms&amp;diff=13026"/>
		<updated>2009-09-25T07:37:02Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Adding a conversion tutorial&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
This document shows you exactly how to use Moodle forms to get files from users in a standard and secure way.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 all files are stored in a central database accessible via the [[File API|File API]], and every file is associated with a &amp;quot;file area&amp;quot; in Moodle, such as a particular module.&lt;br /&gt;
&lt;br /&gt;
A common use case is to provide a form (using Moodle&#039;s [[lib/formslib.php|Forms API]]) which allows users to upload or import files as attachments or media embedded into HTML.&lt;br /&gt;
&lt;br /&gt;
Normally this works like this:&lt;br /&gt;
# User starts creation or re-edits an existing item in Moodle (eg forum post, resource, glossary entry etc)&lt;br /&gt;
# User presses some sort of button to browse for new files to attach or embed&lt;br /&gt;
# User sees our &amp;quot;Choose file...&amp;quot; dialog, which contains one or more repository instances. &lt;br /&gt;
# User chooses a file, the [[Repository API|Repository API]] takes care of copying the file into a &amp;quot;draft file area&amp;quot; within Moodle&lt;br /&gt;
# File appears in the text or as an attachment in the form.&lt;br /&gt;
# When the user hits save, the [[File API|File API]] is invoked to move the file from the draft file area into a permanent file area associated with that data &lt;br /&gt;
&lt;br /&gt;
This document shows you exactly how to use Moodle forms to interact with users in a standard and secure way.&lt;br /&gt;
&lt;br /&gt;
If you just want to write code to manipulate Moodle files internally (without user input) then see [[Using_the_File_API]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Form elements== &lt;br /&gt;
&lt;br /&gt;
In Moodle 2.0 there are three file-related form elements for interacting with users:&lt;br /&gt;
&lt;br /&gt;
# filemanager - the way to attach one or more files as a set&lt;br /&gt;
# editor - the way to specify a textarea with a HTML editor, and all the handling of images and movies within that HTML&lt;br /&gt;
# filepicker - a way to specify one file for the case when you want to process the file and throw it away &lt;br /&gt;
&lt;br /&gt;
In Moodle 1.9 there were two other types which are now &#039;&#039;&#039;deprecated&#039;&#039;&#039; (they work, but please do not use these anymore)&lt;br /&gt;
# file - used to just allow a normal file upload from the desktop only.&lt;br /&gt;
# htmleditor - this old method of embedding a HTML editor in a textarea is not able to support repositories etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===filepicker===&lt;br /&gt;
&lt;br /&gt;
File picker (&#039;&#039;filepicker&#039;&#039;) is a direct replacement of the older &#039;&#039;file&#039;&#039; formslib element. &lt;br /&gt;
&lt;br /&gt;
It is intended for situations when you want the user to upload &#039;&#039;&#039;one&#039;&#039;&#039; file so you can process it and delete it, such as when you are importing data from a CSV file.&lt;br /&gt;
&lt;br /&gt;
==== Using the filepicker element ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filepicker&#039;, &#039;userfile&#039;, get_string(&#039;file&#039;), null, array(&#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;filetypes&#039; =&amp;gt; &#039;*&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Obtain the chosen file ====&lt;br /&gt;
&lt;br /&gt;
The API for getting file contents is exactly the same as for &#039;&#039;file&#039;&#039; element.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$content = $mform-&amp;gt;get_file_content(&#039;userfile&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== filemanager ===&lt;br /&gt;
&lt;br /&gt;
The File Manager element improves on file picker by allowing you to manage more than one file.  It is expected that the files will be stored permanently for future use (such as forum and glossary attachments).&lt;br /&gt;
&lt;br /&gt;
==== Add file manager element ====&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filemanager&#039;, &#039;attachments&#039;, get_string(&#039;attachment&#039;, &#039;moodle&#039;), null,&lt;br /&gt;
                    array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50, &#039;filetypes&#039; =&amp;gt; array(&#039;document&#039;) ));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here are the fields for filemanager:&lt;br /&gt;
&lt;br /&gt;
;&#039;filemanager&#039;:This is a filemanager element :)&lt;br /&gt;
;elementname:The unique name of the element in the form&lt;br /&gt;
;elementlabel:The label string that users see &lt;br /&gt;
;attributes:(leave it as null)&lt;br /&gt;
;options: an array of further options for the filepicker (see below)&lt;br /&gt;
&lt;br /&gt;
The options array can contain:&lt;br /&gt;
&lt;br /&gt;
;subdirs:(Default 0) Are subdirectories allowed?  (true or false)&lt;br /&gt;
;maxbytes:(Default 0) Restricts the total size of all the files.&lt;br /&gt;
;maxfiles:(Default -1) Restricts the total number of files.&lt;br /&gt;
;filetypes:(Default *) You can specify what file types are accepted by filemanager.  All current file types are listed in this file: [http://cvs.moodle.org/moodle/lib/file/file_types.mm moodle/lib/file/file_types.mm].  This is a [http://freemind.sourceforge.net/wiki/index.php/Main_Page freemind] file: if it is edited the changes will be immediately reflected in Moodle.  Example usage:  &#039;&#039;&#039;array(&#039;audio&#039;, &#039;video&#039;, &#039;documents&#039;)&#039;&#039;&#039;, you can include file extensions as well, for example: &#039;&#039;&#039;array(&#039;*.txt&#039;, &#039;*.jpg&#039;, &#039;audio&#039;)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Load existing files into draft area ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (empty($entry-&amp;gt;id)) {&lt;br /&gt;
    $entry = new object();&lt;br /&gt;
    $entry-&amp;gt;id = null;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$draftitemid = file_get_submitted_draft_itemid(&#039;attachments&#039;);&lt;br /&gt;
file_prepare_draft_area($draftitemid, $context-&amp;gt;id, &#039;glossary_attachment&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50));&lt;br /&gt;
$entry-&amp;gt;attachments = $draftitemid;&lt;br /&gt;
&lt;br /&gt;
$mform-&amp;gt;set_data($entry);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Store updated set of files ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
    // ... store or update $entry&lt;br /&gt;
    file_save_draft_area_files($data-&amp;gt;attachments, $context-&amp;gt;id, &#039;glossary_attachment&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039; =&amp;gt; 0, &#039;maxbytes&#039; =&amp;gt; $maxbytes, &#039;maxfiles&#039; =&amp;gt; 50));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===editor===&lt;br /&gt;
There are two way for using of editor element in code, the first one is easier but expects some standardized fields. The second method is more low level.&lt;br /&gt;
&lt;br /&gt;
====Simple use====&lt;br /&gt;
# name database fields: &#039;&#039;textfield&#039;&#039;, &#039;&#039;textfieldformat&#039;&#039; (and &#039;&#039;textfieldtrust&#039;&#039; if required)&lt;br /&gt;
# create options array &amp;lt;code php&amp;gt;$textfieldoptions = array(&#039;trusttext&#039;=&amp;gt;true, &#039;subdirs&#039;=&amp;gt;true, &#039;maxfiles&#039;=&amp;gt;$maxfiles, &#039;maxbytes&#039;=&amp;gt;$maxbytes);&amp;lt;/code&amp;gt;&lt;br /&gt;
# add editor &#039;&#039;textfield_editor&#039;&#039; to moodle form, pass options through custom data in form constructor, set $data-&amp;gt;id to null if data not exist yet &amp;lt;code php&amp;gt;$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;textfield_editor&#039;, get_string(&#039;fieldname&#039;, &#039;somemodule&#039;), null, $textfieldoptions);&amp;lt;/code&amp;gt;&lt;br /&gt;
# prepare data &amp;lt;code php&amp;gt;$data = file_prepare_standard_editor($data, &#039;textfield&#039;, $textfieldoptions, $context, &#039;somemodule_somearea&#039;, $data-&amp;gt;id);&amp;lt;/code&amp;gt;&lt;br /&gt;
# get submitted data and after inserting/updating of data &amp;lt;code php&amp;gt;$data = file_postupdate_standard_editor($data, &#039;textfield&#039;, $textfieldoptions, $context, &#039;somemodule_somearea&#039;, $data-&amp;gt;id);&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Real world examples are in mod/glossary/edit.php and mod/glossary/comment.php&lt;br /&gt;
&lt;br /&gt;
====Low level use====&lt;br /&gt;
&lt;br /&gt;
When using editor element you  need to preprocess and postprocess the data:&lt;br /&gt;
# detect if form was already submitted (usually means draft is area already exists) - &#039;&#039;file_get_submitted_draft_itemid()&#039;&#039;&lt;br /&gt;
# prepare draft file area, temporary storage of all files attached to the text - &#039;&#039;file_prepare_draft_area()&#039;&#039;&lt;br /&gt;
# convert encoded relative links to absolute links - &#039;&#039;file_prepare_draft_area()&#039;&#039;&lt;br /&gt;
# create form and set current data&lt;br /&gt;
# after submission the changed files must be merged back into original area - &#039;&#039;file_save_draft_area_files()&#039;&#039;&lt;br /&gt;
# absolute links have to be replaced by relative links - &#039;&#039;file_save_draft_area_files()&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====Replace old htmleditor with editor=====&lt;br /&gt;
&lt;br /&gt;
The file picker has been integrated with with TinyMCE to make the editor element. This new element should support all types on editors and should be able to switch them on-the-fly. Instances of the old htmleditor element in your forms should be replaced by the new editor element, this may need adding of new format and trusttext columns. For example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;entry&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), null,&lt;br /&gt;
        array(&#039;maxfiles&#039; =&amp;gt; EDITOR_UNLIMITED_FILES, &#039;filearea&#039; =&amp;gt; &#039;glossary_entry&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The editor element can take following options: maxfiles, maxbytes, filearea, subdirs and changeformat. Please note that the embedded files is optional feature and is not expected be used everywhere.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the editor element now includes text format option. You should no longer use the separate format element type.&lt;br /&gt;
&lt;br /&gt;
=====Prepare current data - text and files=====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (empty($entry-&amp;gt;id)) {&lt;br /&gt;
  $entry = new object();&lt;br /&gt;
  $entry-&amp;gt;id = null;&lt;br /&gt;
  $entry-&amp;gt;definition = &#039;&#039;;&lt;br /&gt;
  $entry-&amp;gt;format = FORMAT_HTML;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$draftid_editor = file_get_submitted_draft_itemid(&#039;entry&#039;);&lt;br /&gt;
$currenttext = file_prepare_draft_area($draftid_editor, $context-&amp;gt;id, &#039;glossary_entry&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039;=&amp;gt;true), $entry-&amp;gt;definition);&lt;br /&gt;
$entry-&amp;gt;entry = array(&#039;text&#039;=&amp;gt;$currenttext, &#039;format&#039;=&amp;gt;$entry-&amp;gt;format, &#039;itemid&#039;=&amp;gt;$draftid_editor);&lt;br /&gt;
&lt;br /&gt;
$mform-&amp;gt;set_data($entry);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there are multiple files, they will share the same itemid.&lt;br /&gt;
&lt;br /&gt;
=====Obtain text, format and save draft files=====&lt;br /&gt;
&lt;br /&gt;
To retrieve editor content, you need to use following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($fromform = $mform-&amp;gt;get_data()) {&lt;br /&gt;
    // content of editor&lt;br /&gt;
    $messagetext = $fromform-&amp;gt;entry[&#039;text&#039;];&lt;br /&gt;
    // format of content&lt;br /&gt;
    $messageformat  = $fromform-&amp;gt;entry[&#039;format&#039;];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a user selects a file using the file picker, the file is initially stored in a draft file area, and a URL is inserted into the HTML in the editor that lets the person editing the content (but no one else) see the file.&lt;br /&gt;
&lt;br /&gt;
When the user submits the form, we then need to save the draft files to the correct place in permanent storage. (Just like you have to call $DB-&amp;gt;update_record(&#039;tablename&#039;, $data); to have the other parts of the form submission stored correctly.)&lt;br /&gt;
&lt;br /&gt;
The save_files_from_draft_area function and replace absolute links with internal relative links do:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$messagetext = file_save_draft_area_files($draftid_editor, $context-&amp;gt;id, &#039;glossary_entry&#039;, $entry-&amp;gt;id, array(&#039;subdirs&#039;=&amp;gt;true), $messagetext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
; $context-&amp;gt;id, &#039;proper_file_area&#039; and $entry-&amp;gt;id : correspond to the contextid, filearea and itemid columns in the [[File_API#Table:_files|files table]].&lt;br /&gt;
; $messagetext : this is the message text. As the files are saved to the real file area, the URLs in this content are rewritten.&lt;br /&gt;
&lt;br /&gt;
All URLs in content that point to files managed to the File API are converted to a form that starts &#039;@@PLUGINFILE@@/&#039; before the content is stored in the database. That is what we mean by rewriting.&lt;br /&gt;
&lt;br /&gt;
== File serving==&lt;br /&gt;
&lt;br /&gt;
=== Convert internal relative links to absolute links ===&lt;br /&gt;
&lt;br /&gt;
Before text content is displayed to the user, any URLs in the &#039;@@PLUGINFILE@@/&#039; form in the content need to be rewritten to the real URL where the user can access the files. &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$messagetext = file_rewrite_pluginfile_urls($messagetext, &#039;pluginfile.php&#039;,&lt;br /&gt;
        &amp;quot;$context-&amp;gt;id/proper_file_area/$itemid/&amp;quot;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
; $messagetext : is the content containing the @@PLUGINFILE@@ URLs from the database.&lt;br /&gt;
; &#039;pluginfile.php&#039; : there are a number of different scripts that can serve files with different permissions checks. You need to specify which one to use.&lt;br /&gt;
; &amp;quot;$context-&amp;gt;id/proper_file_area/$itemid/&amp;quot; : uniquely identifies the file area, as before.&lt;br /&gt;
&lt;br /&gt;
=== Implement file serving access control ===&lt;br /&gt;
&lt;br /&gt;
Attachments and embedded images should have the same access control like the text itself, in majority of cases these files are served using pluginfile.php. Access control is defined in &#039;&#039;module/lib.php&#039;&#039; file in function &#039;&#039;module_pluginfile()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File browsing support ==&lt;br /&gt;
Only owner of each file area is allowed to use low level File API function to access files, other parts of Moodle should use file browsing API.&lt;br /&gt;
&lt;br /&gt;
Activities may specify browsing support in own module/lib.php file by implementing functions module_get_file_areas() and module_get_file_info().&lt;br /&gt;
&lt;br /&gt;
== Upgrading your code ==&lt;br /&gt;
Here I will attempt to describe some simple steps you can take to upgrade your file-handling form elements from pre-2.0 code to 2.0. We will use the example of glossary, since it has been used above.&lt;br /&gt;
&lt;br /&gt;
=== Preparing your options ===&lt;br /&gt;
Unless you are happy with the defaults, you will need to define an array of options for each file-handling form element. You could define it at different places, but it&#039;s best to put it in one place and make the array(s) available to other files if they need it. In the majority of cases, this will be in a file like edit.php&lt;br /&gt;
&lt;br /&gt;
Previous code in mod/glossary/edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform =&amp;amp; new mod_glossary_entry_form(null, compact(&#039;cm&#039;, &#039;glossary&#039;, &#039;hook&#039;, &#039;mode&#039;, &#039;e&#039;, &#039;context&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$maxbytes = $course-&amp;gt;maxbytes; // Could also use $CFG-&amp;gt;maxbytes if you are not coding within a course context&lt;br /&gt;
$definitionoptions = array(&#039;trusttext&#039;=&amp;gt;true, &#039;subdirs&#039;=&amp;gt;false, &#039;maxfiles&#039;=&amp;gt;99, &#039;maxbytes&#039;=&amp;gt;$maxbytes, &#039;trusttext&#039;=&amp;gt;true, &#039;context&#039;=&amp;gt;$context);&lt;br /&gt;
$attachmentoptions = array(&#039;subdirs&#039;=&amp;gt;false, &#039;maxfiles&#039;=&amp;gt;99, &#039;maxbytes&#039;=&amp;gt;$maxbytes);&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note that the data being passed to the form constructor have changed also, but this is not part of the file API changes, I just include them to avoid confusion.&lt;br /&gt;
&lt;br /&gt;
These options are for the htmleditor (definition field) and the filemanager (attachment field). They are used by a file called edit_form.php. &lt;br /&gt;
&lt;br /&gt;
=== Element preparation ===&lt;br /&gt;
Before we look at this, however, we need to &amp;quot;prepare&amp;quot; the elements so that they can correctly display existing embedded images and attached files when you are editing a record instead of just creating one. So, let&#039;s take the code we&#039;ve got so far in edit.php and add to it:&lt;br /&gt;
&lt;br /&gt;
Currently upgraded code in edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code with element preparation:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$entry = file_prepare_standard_editor($entry, &#039;definition&#039;, $definitionoptions, $context, &#039;glossary_entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_prepare_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;glossary_attachment&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$mform = new mod_glossary_entry_form(null, array(&lt;br /&gt;
        &#039;current&#039;=&amp;gt;$entry, &lt;br /&gt;
        &#039;cm&#039;=&amp;gt;$cm, &lt;br /&gt;
        &#039;glossary&#039;=&amp;gt;$glossary,&lt;br /&gt;
        &#039;definitionoptions&#039;=&amp;gt;$definitionoptions, &lt;br /&gt;
        &#039;attachmentoptions&#039;=&amp;gt;$attachmentoptions));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Things to note:&lt;br /&gt;
* $entry in this case is simply a stdClass object which may either represent a new glossary entry or an existing one.&lt;br /&gt;
* $entry-&amp;gt;id must be the unique identifier for the current object. If we are creating a new entry, it will be null, but in all cases it must be defined.&lt;br /&gt;
* These two functions (file_prepare_standard_editor and file_prepare_standard_filemanager) are shortcuts functions that take care of some of the tedious setting up for you, but they make a couple of assumptions:&lt;br /&gt;
## You &#039;&#039;&#039;must&#039;&#039;&#039; name the form element as {element}_editor or {element}_filemanager (see next section)&lt;br /&gt;
## You &#039;&#039;&#039;must&#039;&#039;&#039; have at least the following fields in the database: {element} and {element}summary, as described earlier in this documentation&lt;br /&gt;
&lt;br /&gt;
We can now look at the upgrades needed in the form definition file.&lt;br /&gt;
&lt;br /&gt;
=== Form definition ===&lt;br /&gt;
Previous code in mod/glossary/edit_form.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;htmleditor&#039;, &#039;definition&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), array(&#039;rows&#039;=&amp;gt;20));&lt;br /&gt;
$mform-&amp;gt;setType(&#039;definition&#039;, PARAM_RAW);&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;definition&#039;, null, &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;definition&#039;, array(&#039;writing&#039;, &#039;richtext&#039;), false, &#039;editorhelpbutton&#039;);&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;format&#039;);&lt;br /&gt;
// a bit further...&lt;br /&gt;
$this-&amp;gt;set_upload_manager(new upload_manager(&#039;attachment&#039;, true, false, $COURSE, false, 0, true, true, false));&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;attachment&#039;, array(&#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), &#039;glossary&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$definitionoptions = $this-&amp;gt;_customdata[&#039;definitionoptions&#039;];&lt;br /&gt;
$attachmentoptions = $this-&amp;gt;_customdata[&#039;attachmentoptions&#039;];&lt;br /&gt;
// a bit further...&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;editor&#039;, &#039;definition_editor&#039;, get_string(&#039;definition&#039;, &#039;glossary&#039;), null, $definitionoptions);&lt;br /&gt;
$mform-&amp;gt;setType(&#039;definition_editor&#039;, PARAM_RAW);&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;definition_editor&#039;, get_string(&#039;required&#039;), &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
// a bit further...&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;filemanager&#039;, &#039;attachment_filemanager&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), null, $attachmentoptions);&lt;br /&gt;
$mform-&amp;gt;setHelpButton(&#039;attachment_filemanager&#039;, array(&#039;attachment2&#039;, get_string(&#039;attachment&#039;, &#039;glossary&#039;), &#039;glossary&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the following:&lt;br /&gt;
* The format element and the help button are no longer required for the HTML editor element&lt;br /&gt;
* The name of the form element needs to be changed by adding &#039;_editor&#039; or &#039;_manager&#039; to the original name. This is a naming convention that is used by a couple of functions we will look at shortly&lt;br /&gt;
&lt;br /&gt;
=== Submitted data handling ===&lt;br /&gt;
The final step is to handle the submitted data properly, i.e. retrieve the files and save them to disk, associating them with the record we have just created (a glossary entry in our example). This happens in edit.php:&lt;br /&gt;
&lt;br /&gt;
Previous code in edit.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Section that updates an entry:&lt;br /&gt;
$todb-&amp;gt;id = $e;&lt;br /&gt;
$dir = glossary_file_area_name($todb);&lt;br /&gt;
if ($mform-&amp;gt;save_files($dir) and $newfilename = $mform-&amp;gt;get_new_filename()) {&lt;br /&gt;
    $todb-&amp;gt;attachment = $newfilename;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Section that adds an entry:&lt;br /&gt;
if ($todb-&amp;gt;id = insert_record(&amp;quot;glossary_entries&amp;quot;, $todb)) {&lt;br /&gt;
    $e = $todb-&amp;gt;id;&lt;br /&gt;
    $dir = glossary_file_area_name($todb);&lt;br /&gt;
    if ($mform-&amp;gt;save_files($dir) and $newfilename = $mform-&amp;gt;get_new_filename()) {&lt;br /&gt;
        set_field(&amp;quot;glossary_entries&amp;quot;, &amp;quot;attachment&amp;quot;, $newfilename, &amp;quot;id&amp;quot;, $todb-&amp;gt;id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// $todb was renamed to $entry, and the code was refactored &lt;br /&gt;
// so that the file-handling code is only used once for either an add or an update action.&lt;br /&gt;
// If an entry is being added, $DB-&amp;gt;insert() has already been called, so we have a valid $entry-&amp;gt;id&lt;br /&gt;
$entry = file_postupdate_standard_editor($entry, &#039;definition&#039;, $definitionoptions, $context, &#039;glossary_entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_postupdate_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;glossary_attachment&#039;, $entry-&amp;gt;id);&lt;br /&gt;
// store the updated value values&lt;br /&gt;
$DB-&amp;gt;update_record(&#039;glossary_entries&#039;, $entry);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Things to note:&lt;br /&gt;
* If you are adding a new record, you will still need to call update_record after calling the file_postupdate* functions&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[File API]]&lt;br /&gt;
* [[Using the file API]]&lt;br /&gt;
* [[Repository API]]&lt;br /&gt;
* [[Portfolio API]]&lt;br /&gt;
* MDL-14589 - File API Meta issue&lt;br /&gt;
&lt;br /&gt;
{{CategoryDeveloper}}&lt;br /&gt;
[[Category:Files]]&lt;br /&gt;
[[Category:Repositories]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File_API&amp;diff=11518</id>
		<title>File API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File_API&amp;diff=11518"/>
		<updated>2009-09-25T06:34:57Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Putting link to more useful doc&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
The File API is for managing all the files stored by Moodle. If you are interested in how the file API works internally, see [[File API]]. The page is just about what you need to know to use the file API. Related is the [[Repository API]], which lets users get files into Moodle.&lt;br /&gt;
&lt;br /&gt;
If you are looking for an explanation on how to upgrade pre-2.0 code to using the file API, you most likely need to read [[Using_the_File_API_in_Moodle_forms|Using the File API in Moodle forms]].&lt;br /&gt;
&lt;br /&gt;
==File areas==&lt;br /&gt;
&lt;br /&gt;
Files are conceptually stored in &#039;&#039;&#039;file areas&#039;&#039;&#039;. A file area is uniquely identified by:&lt;br /&gt;
* A context id.&lt;br /&gt;
* A file area type, for example &#039;course_intro&#039; or &#039;forum_post&#039;.&lt;br /&gt;
* A unique itemid. Normally, the itemid relates to something depending on the file area type. For example, for a &#039;course_intro&#039; file area, the itemid is is the course id. For forum post, it is the post id.&lt;br /&gt;
&lt;br /&gt;
File areas are not listed separately anywhere, they are stored implicitly in the files table. Please note that each subsystem is allowed to access only own file areas, for example core code must not access module or block files directly.&lt;br /&gt;
&lt;br /&gt;
===Naming file areas===&lt;br /&gt;
&lt;br /&gt;
It is important that file areas are named consistently so we do not get name collisions, and so the names are easy to understand. Please follow the following guidelines:&lt;br /&gt;
&lt;br /&gt;
====start of the name====&lt;br /&gt;
&lt;br /&gt;
If the file area belongs to a plugin, please use the plugin name as the start of the file area name.&lt;br /&gt;
&lt;br /&gt;
This is the same plugin name that you would use for get_string calls. Some examples:&lt;br /&gt;
* All file areas that belong to the forum modules should have a name beginning with &#039;forum&#039;. For example &#039;forum_post&#039;, &#039;forum_intro&#039;.&lt;br /&gt;
* A file area belonging to the HTML block would start &#039;block_html_&#039;.&lt;br /&gt;
* A file area belonging to a question type would start &#039;qtype_myqtype_&#039;, except this is probably not necessary, because question type images should probably be stored in the core &#039;question_text&#039; file area.&lt;br /&gt;
* If the file area is used by a local hack, the file area name should start &#039;local_&#039;.&lt;br /&gt;
&lt;br /&gt;
If the file area belongs to core code, the file area name should start with a prefix that indicates what part of Moodle it belongs to.&lt;br /&gt;
&lt;br /&gt;
But try to avoid clashing with prefixes that a plugin might use. Some examples:&lt;br /&gt;
* &#039;course_intro&#039;&lt;br /&gt;
* &#039;question_text&#039;&lt;br /&gt;
* &#039;user_draft&#039; (although draft file areas are a special case).&lt;br /&gt;
&lt;br /&gt;
====rest of the name====&lt;br /&gt;
&lt;br /&gt;
Like naming variables or functions, try to find a name that is short, but says exactly what the file area is for.&lt;br /&gt;
&lt;br /&gt;
If possible use the name of the file area to give a clue as to which database table the itemid relates to. For example:&lt;br /&gt;
&lt;br /&gt;
* For the &#039;forum_post&#039; file area, the itemid links to forum_post.id.&lt;br /&gt;
* For &#039;question_text&#039; file area, the itemid links to question.id. (Would it be permissible to call this file area just question?)&lt;br /&gt;
&lt;br /&gt;
==Serving files to users==&lt;br /&gt;
&lt;br /&gt;
You must refer to the file with a URL that includes a file-serving script, often pluginfile.php. For example&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$url = $CFG-&amp;gt;wwwroot/pluginfile.php/$contextid/$filearea/$itemid/file/path.ext;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Often you get these URLs generated automatically for you using the function file_rewrite_pluginfile_urls.&lt;br /&gt;
&lt;br /&gt;
==Getting files from the user==&lt;br /&gt;
&lt;br /&gt;
* See [[Using_the_File_API_in_Moodle_forms|Using the File API in Moodle forms]]&lt;br /&gt;
==Examples==&lt;br /&gt;
Please note that in reality developers outside of core will not deal with file api directly in majority of cases, instead use formslib elements which are doing all this automatically.&lt;br /&gt;
&lt;br /&gt;
===Browsing files===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$browser = get_file_browser();&lt;br /&gt;
$context = get_system_context();&lt;br /&gt;
&lt;br /&gt;
$filearea = null;&lt;br /&gt;
$itemid   = null;&lt;br /&gt;
$filename = null;&lt;br /&gt;
if ($fileinfo = $browser-&amp;gt;get_file_info($context, $filearea, $itemid, &#039;/&#039;, $filename)) {&lt;br /&gt;
    // build a Breadcrumb trail&lt;br /&gt;
    $level = $fileinfo-&amp;gt;get_parent();&lt;br /&gt;
    while ($level) {&lt;br /&gt;
        $params = base64_encode(serialize($level-&amp;gt;get_params()));&lt;br /&gt;
        $path[] = array(&#039;name&#039;=&amp;gt;$level-&amp;gt;get_visible_name(), &#039;path&#039;=&amp;gt;$params);&lt;br /&gt;
        $level = $level-&amp;gt;get_parent();&lt;br /&gt;
    }&lt;br /&gt;
    $path = array_reverse($path);&lt;br /&gt;
    $children = $fileinfo-&amp;gt;get_children();&lt;br /&gt;
    foreach ($children as $child) {&lt;br /&gt;
        if ($child-&amp;gt;is_directory()) {&lt;br /&gt;
            echo $child-&amp;gt;get_visible_name();&lt;br /&gt;
            // display contextid, itemid, filepath and filename&lt;br /&gt;
            var_dump($child-&amp;gt;get_params());&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
===Moving files around===&lt;br /&gt;
&lt;br /&gt;
For example, if you have just built a file at the path&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
 $from_zip_file = $CFG-&amp;gt;dataroot . &#039;/temp/backup/&#039; . $preferences-&amp;gt;backup_unique_code .&lt;br /&gt;
         &#039;/&#039; . $preferences-&amp;gt;backup_name;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
And you want to move it into the course_backup file area, do&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
 $context = get_context_instance(CONTEXT_COURSE, $preferences-&amp;gt;backup_course);&lt;br /&gt;
 $fs = get_file_storage();&lt;br /&gt;
 $file_record = array(&#039;contextid&#039;=&amp;gt;$context-&amp;gt;id, &#039;filearea&#039;=&amp;gt;&#039;course_backup&#039;,&lt;br /&gt;
         &#039;itemid&#039;=&amp;gt;0, &#039;filepath&#039;=&amp;gt;&#039;/&#039;, &#039;filename&#039;=&amp;gt;$preferences-&amp;gt;backup_name,&lt;br /&gt;
         &#039;timecreated&#039;=&amp;gt;time(), &#039;timemodified&#039;=&amp;gt;time());&lt;br /&gt;
 $fs-&amp;gt;create_file_from_pathname($file_record, $from_zip_file);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
===Create a copy of stored file===&lt;br /&gt;
If you need to create a copy of stored file (actually, it add a new record in database):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$context  = get_context_instance_by_id($contextid);&lt;br /&gt;
$file_info = $browser-&amp;gt;get_file_info($context, $filearea, $fileitemid, $filepath, $filename);&lt;br /&gt;
// copy this file to draft area&lt;br /&gt;
$file_info-&amp;gt;copy_to_storage($user_context-&amp;gt;id, &#039;user_draft&#039;, $newitemid, &#039;/&#039;, $title);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== List area files ===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$fs = get_file_storage();&lt;br /&gt;
$files = $fs-&amp;gt;get_area_files($contextid, &#039;user_draft&#039;);&lt;br /&gt;
foreach ($files as $f) {&lt;br /&gt;
    // $f is an instance of stored_file&lt;br /&gt;
    echo $f-&amp;gt;get_filename();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[File API]] how the File API works internally.&lt;br /&gt;
* [[Roadmap|Moodle 2.0 roadmap]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Files]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File_API_internals&amp;diff=8293</id>
		<title>File API internals</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File_API_internals&amp;diff=8293"/>
		<updated>2009-09-25T06:15:55Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Updated name of fileareas&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}&lt;br /&gt;
This specification has now largely been implemented in Moodle 2.0. Inevitably, in the implementation, some details may have changed. If in doubt, read the code. (Then come back and correct this page ;-))&lt;br /&gt;
&lt;br /&gt;
The implementation of this specification is being tracked at MDL-14589.&lt;br /&gt;
&lt;br /&gt;
Please see [[Using the file API]] if you just want to know how to use the File API in your code, rather than how it works internally.&lt;br /&gt;
&lt;br /&gt;
==Objectives==&lt;br /&gt;
&lt;br /&gt;
The goals of these changes are to: &lt;br /&gt;
&lt;br /&gt;
* allow files to be stored within Moodle, as part of the content (as we do now).&lt;br /&gt;
* use a consistent and simple approach for all file handling throughout Moodle.&lt;br /&gt;
* give modules control over which users can access a file, using capabilities and other local rules.&lt;br /&gt;
* make it easy to determine which parts of Moodle use which files, to simplify operations like backup and restore.&lt;br /&gt;
* track where files originally came from.&lt;br /&gt;
* avoid redundant storage, when the same file is used twice.&lt;br /&gt;
* fully support Unicode file names, irrespective of the capabilities of the underlying file system.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The File API is a set of core interfaces to allow the rest of Moodle to:&lt;br /&gt;
# store files, and&lt;br /&gt;
# display files to users.&lt;br /&gt;
It applies only to files that are part of the Moodle site&#039;s content. It is not used for internal files, such as those in the following subdirectories of dataroot: temp, lang, cache, environment, filter, search, sessions, upgradelogs, ...&lt;br /&gt;
&lt;br /&gt;
The API can be subdivided into the following parts:&lt;br /&gt;
; Serving files&lt;br /&gt;
: Lets users accessing a Moodle site get the files (file.php, draftfile.php, pluginfile.php, userfile.php, etc.)&lt;br /&gt;
:* Serve the files on request&lt;br /&gt;
:* with appropriate security checks&lt;br /&gt;
; File API internals&lt;br /&gt;
: Stores the files on disc, with metadata in associated database tables.&lt;br /&gt;
:* Content-addressed storage.&lt;br /&gt;
; File management API&lt;br /&gt;
: Allows code to manipulate the stored files (lib/filelib.php)&lt;br /&gt;
:* find information about stored files.&lt;br /&gt;
:* print links to files.&lt;br /&gt;
:* move/rename/copy/delete/etc.&lt;br /&gt;
:* keep a file synchronised with an external repository.&lt;br /&gt;
; File management user interface&lt;br /&gt;
: Provides the interface for (lib/form/file.php, filemanager.php, filepicker.php and files/index.php, draftfiles.php)&lt;br /&gt;
:* Form elements allowing users to select a file using the Repository API, and have it stored within Moodle.&lt;br /&gt;
:* UI for users to manage their files, replacing the old course files UI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Serving files ==&lt;br /&gt;
&lt;br /&gt;
TODO revise this section.&lt;br /&gt;
&lt;br /&gt;
Deals with serving of files - browser requests file, Moodle sends it back. We have three main files. It is important to setup slasharguments on server (file.php/some/thing/xxx.jpg), any content that relies on relative links can not work without it (scorm, uploaded html pages, etc.).&lt;br /&gt;
&lt;br /&gt;
=== file.php ===&lt;br /&gt;
&lt;br /&gt;
Serves course files.&lt;br /&gt;
&lt;br /&gt;
Implements basic file access. Ideally only images and files linked from course sections should be there, no XSS protection required - we expect javascript, sw, etc. there, no way to make it &amp;quot;secure&amp;quot;. The access control is not critical any more if we move most of the files into modules&lt;br /&gt;
&lt;br /&gt;
The file name and parameter structure is critical for backwards compatibility of existing course content.&lt;br /&gt;
&lt;br /&gt;
 /file.php/courseid/dir/dir/filename.ext&lt;br /&gt;
&lt;br /&gt;
Internally the files would be stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;$coursecontextid, &#039;filearea&#039;=&amp;gt;&#039;coursefiles&#039;, &#039;itemid&#039;=&amp;gt;0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== pluginfile.php ===&lt;br /&gt;
&lt;br /&gt;
(aka modfile.php)&lt;br /&gt;
Sends module, block, question files.&lt;br /&gt;
* modules decide about access control&lt;br /&gt;
* optional XSS protection - student submitted files must not be served with normal headers, we have to force download instead; ideally there should be second wwwroot for serving of untrusted files&lt;br /&gt;
* only internal links to selected areas are supported - you can link images in summary area, but not the assignment submissions&lt;br /&gt;
&lt;br /&gt;
Absolute file links need to be rewritten if html editing allowed in module. The links are stored internally as relative links. Before editing or display the internal link representation is converted to absolute links using simple str_replace() @@thipluginlink/summary@@/image.jpg --&amp;gt; /pluginfile.php/assignmentcontextid/intro/image.jpg, it is converted back to internal links before saving.&lt;br /&gt;
&lt;br /&gt;
::Can the distinct file areas supported by one plugin be declared somehow in order add some information about them? For example, I think it can be interesting to declare:&lt;br /&gt;
::* assignment_summary:&lt;br /&gt;
::** relpath=&#039;intro&#039;&lt;br /&gt;
::** userdata=false&lt;br /&gt;
::** anotherproperty=anothervalue&lt;br /&gt;
::* assignment_submission:&lt;br /&gt;
::** relpath=&#039;submission/@@USERID@@&#039;&lt;br /&gt;
::** userdata=false&lt;br /&gt;
::** anotherproperty=anothervalue&lt;br /&gt;
::* and so on...&lt;br /&gt;
::And then, when the editor &amp;quot;receives&amp;quot; one &amp;quot;assignment_summary&amp;quot; areaname, if knows what to show and so on? Also that info could be useful to know, in backup &amp;amp; restore if some fileareas have to be processed or no (userdata=false). Or also, when reconstructing the links (str_replace() above). And will cause to have a well defined list of fileareas by module, instead of coding them in a free way (prone to errors). [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 16:35, 28 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
::Something like this will be part of file management API, hardcoding this in file storage would make it less flexible imo [[User:Skodak|Skodak]]&lt;br /&gt;
&lt;br /&gt;
::Yup, yup. Storage doesn&#039;t know anything but get/put files (nothing else). It&#039;s part of management, absolutely. [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 11:21, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/contextid/areaname/arbitrary/params/or/dirs/filename.ext&lt;br /&gt;
&lt;br /&gt;
pluginfile.php detects the type of plugin from context table, fetches basic info (like $course or $cm if appropriate) and calls plugin function (or later method) which does the access control and finally sends the file to user. &#039;&#039;areaname&#039;&#039; separates files by type and divides the context into several subtrees - for example &#039;&#039;summary&#039;&#039; files (images used in module intros), post attachments, etc.&lt;br /&gt;
&lt;br /&gt;
==== Assignment example ====&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/intro/someimage.jpg&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/submission/submissionid/attachmentname.ext&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/extra/allsubmissionfiles.zip&lt;br /&gt;
&lt;br /&gt;
::Uhm... all those files together? What&#039;s going to differentiate the &amp;quot;submission&amp;quot; path in the example above from the &amp;quot;summary&amp;quot; path? Is it supposed that the editor, or the filemanager won&#039;t allow , for example to pick-up one file from the &amp;quot;submission&amp;quot; area to be used in the summary of one assignment and only the &amp;quot;summary&amp;quot; area will be showed? That means multiple file managers by context and it&#039;s against the clean &amp;quot;one file manager per context&amp;quot; agreed below [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 21:28, 26 June 2008 (CDT)&lt;br /&gt;
::Yes Eloy, the different areas (summary, submission) etc. have different uses, different access control. There are two types of file manager - the two pane file manager which lists all contexts+areas user may access, and minimalistic manager in html editor which shows only subset of areas from current plugin (because you can not link anything else).&lt;br /&gt;
&lt;br /&gt;
====scorm example====&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/scormcontextid/intro/someimage.jpg&lt;br /&gt;
 /pluginfile.php/scormcontextid/content/revisionnumber/dir/somescormfile.js&lt;br /&gt;
&lt;br /&gt;
The revision counter is incremented when any file changes in order to prevent caching problems. The lifetime should be adjustable in module settings.&lt;br /&gt;
&lt;br /&gt;
====quiz example====&lt;br /&gt;
&lt;br /&gt;
 pluginfile.php/quizcontextid/intro/niceimage.jpg&lt;br /&gt;
 pluginfile.php/quizcontextid/report/type/export.ods&lt;br /&gt;
&lt;br /&gt;
====questions example====&lt;br /&gt;
&lt;br /&gt;
 pluginfile.php/SYSCONTEXTID/question/questionid/file.jpg&lt;br /&gt;
&lt;br /&gt;
====blog example====&lt;br /&gt;
Blog entries or notes in general do not have context id (because they live in system context, SYSCONTEXTID below is the id of system context).&lt;br /&gt;
The note attachments are always served with XSS protection on, ideally we should use separate wwwroot for this. Access control can be hardcoded.&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/SYSCONTEXTID/blog_attachment/blogentryid/attachmentname.ext&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;SYSCONTEXTID, &#039;filearea&#039;=&amp;gt;&#039;blog_attachment&#039;, &#039;itemid&#039;=&amp;gt;$blogentryid)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/SYSCONTEXTID/blog_post/blogentryid/embeddedimage.ext&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;SYSCONTEXTID, &#039;filearea&#039;=&amp;gt;&#039;blog_post&#039;, &#039;itemid&#039;=&amp;gt;$blogentryid)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====backup example====&lt;br /&gt;
It would be nice to have some special protection of backup files - new capabilities for backup file download, upload. Backups contain a lot of personal info, we could block restoring of backups from other sites too.&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/coursecontextid/backup/backupfile.zip&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;$coursecontextid, &#039;filearea&#039;=&amp;gt;&#039;backup&#039;, &#039;itemid&#039;=&amp;gt;0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===userfile.php===&lt;br /&gt;
Personal file storage, intended as an online storage of work in progress like assignments before the submission.&lt;br /&gt;
* read/write own files only for now&lt;br /&gt;
* option to share with others later&lt;br /&gt;
* personal &amp;quot;websites&amp;quot; will not be supported (security)&lt;br /&gt;
&lt;br /&gt;
 /userfile.php/userid/dir/dir/filename.ext&lt;br /&gt;
&lt;br /&gt;
===rssfile.php===&lt;br /&gt;
Replaces rss/file.php which is kept only for backwards compatibility.&lt;br /&gt;
RSS files should not require sessions/cookies, URLs should contain some sort of security token/key.&lt;br /&gt;
Internally the files may be stored in database or together with other files.&lt;br /&gt;
Performance improvements - we should support both Etag (cool) and Last-Modified (more used), when we receive If-None-Match/If-Modified-Since =&amp;gt; 304 &lt;br /&gt;
&lt;br /&gt;
 /rssfile.php/contextid/any/parameters/module/wants/rss.xml&lt;br /&gt;
 /rssfile.php/SYSCONTEXTID/blog/userid/rss.xml&lt;br /&gt;
&lt;br /&gt;
Again modules and plugins decide what gets sent to user.&lt;br /&gt;
&lt;br /&gt;
=== Temporary files ===&lt;br /&gt;
Temporary files are usually used during the lifetime of one script only.&lt;br /&gt;
uses:&lt;br /&gt;
* exports&lt;br /&gt;
* imports&lt;br /&gt;
* zipping/unzipping&lt;br /&gt;
* processing by executable files (latex, mimetex)&lt;br /&gt;
&lt;br /&gt;
Ideally these files should never use utf-8 (which is a major problem for zipping at the moment).&lt;br /&gt;
Proposed new sha1 based file storage is not suitable both for performance and technical reasons.&lt;br /&gt;
&lt;br /&gt;
=== Legacy file storage and serving ===&lt;br /&gt;
Going to use good-old separate directories in $CFG-&amp;gt;dataroot.&lt;br /&gt;
&lt;br /&gt;
file serving and storage:&lt;br /&gt;
# user avatars - user/pix.php&lt;br /&gt;
# group avatars - user/pixgroup.php&lt;br /&gt;
# tex, algebra - filter/tex/* and filter/algebra/*&lt;br /&gt;
# rss cache (?full rss rewrite soon?) - backwards compatibility only rss/file.php&lt;br /&gt;
&lt;br /&gt;
only storage:&lt;br /&gt;
#sessions&lt;br /&gt;
&lt;br /&gt;
== File API internals ==&lt;br /&gt;
&lt;br /&gt;
=== File storage on disk ===&lt;br /&gt;
&lt;br /&gt;
Files are stored in $CFG-&amp;gt;dataroot (also known as moodledata) in the filedir subfolder.&lt;br /&gt;
&lt;br /&gt;
Files are stored according to the SHA1 hash of their content. This means each file with particular contents is stored once, irrespective of how many times it is included in different places, even if it is referred to by different names. (This idea comes from the git version control system.) To relate a file on disc to a user-comprehensible path or filename, you need to use the file database tables. See the next section.&lt;br /&gt;
&lt;br /&gt;
Suppose a file has SHA1 hash 081371cb102fa559e81993fddc230c79205232ce. Then it will be stored in on disc as moodledata/filedir/08/13/71/081371cb102fa559e81993fddc230c79205232ce.&lt;br /&gt;
&lt;br /&gt;
If you were wondering, in PHP, SHA1 hashes can be computed with either the [http://php.net/sha1 sha1] or [http://php.net/sha1_file sha1_file] functions.&lt;br /&gt;
&lt;br /&gt;
The information in this section should be considered completely internal to the file API. Other parts of the Moodle code should manipulate files using the higher level functions of the file API. For example, they should refer to files by file id in the file table, not the SHA1 hash.&lt;br /&gt;
&lt;br /&gt;
=== Files database tables ===&lt;br /&gt;
&lt;br /&gt;
==== Table: files ====&lt;br /&gt;
&lt;br /&gt;
This table contains one entry for each usage of a file. Enough information is kept here so that the file can be fully identified and retrieved again if necessary.&lt;br /&gt;
&lt;br /&gt;
If, for example, the same image is used in a user&#039;s profile, and a forum post, then there will be two rows in this table, one for each use of the file, and Moodle will treat the two as separate files, even though the file is only stored once on disc.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
| int(10)  &lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this file.&lt;br /&gt;
|-&lt;br /&gt;
| contenthash&lt;br /&gt;
| varchar(40)&lt;br /&gt;
|  &lt;br /&gt;
| The sha1 hash of content.&lt;br /&gt;
|-&lt;br /&gt;
| pathnamehash&lt;br /&gt;
| varchar(40)&lt;br /&gt;
| &lt;br /&gt;
| The sha1 hash of contextid+filearea+itemid+filepath+filename - prevents file duplicates and allows fast lookup&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;contextid&#039;&#039;&#039; &lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| The context id defined in context table - identifies the instance of plugin owning the file.&lt;br /&gt;
|-&lt;br /&gt;
| filearea&lt;br /&gt;
| varchar(50)&lt;br /&gt;
|&lt;br /&gt;
| Like &amp;quot;submissions&amp;quot;, &amp;quot;intro&amp;quot; and &amp;quot;content&amp;quot; (images and swf linked from summaries), etc.; &amp;quot;blogs&amp;quot; and &amp;quot;userfiles&amp;quot; are special case that live at the system context.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;itemid&#039;&#039;&#039;&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| Some plugin specific item id (eg. forum post, blog entry or assignment submission or user id for user files)&lt;br /&gt;
|-&lt;br /&gt;
| filepath&lt;br /&gt;
| text&lt;br /&gt;
| &lt;br /&gt;
| relative path to file from module content root, useful in Scorm and Resource mod - most of the mods do not need this&lt;br /&gt;
|-&lt;br /&gt;
| filename&lt;br /&gt;
| varchar(255)&lt;br /&gt;
| &lt;br /&gt;
| The full Unicode name of this file (case sensitive)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;filesize&#039;&#039;&#039;&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| size of file - bytes&lt;br /&gt;
|-&lt;br /&gt;
| mimetype&lt;br /&gt;
| varchar(100)&lt;br /&gt;
| NULL&lt;br /&gt;
| type of file&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;userid&#039;&#039;&#039;&lt;br /&gt;
| int(10)  &lt;br /&gt;
| NULL&lt;br /&gt;
| Optional - general user id field - meaning depending on plugin&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;timecreated&#039;&#039;&#039;&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| The time this file was created&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;timemodified&#039;&#039;&#039;&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| The last time the file was last modified&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Indexes:&lt;br /&gt;
* non-unique index on (contextid, filearea, itemid)&lt;br /&gt;
* non-unique index on (contenthash)&lt;br /&gt;
* unique index on (pathnamehash).&lt;br /&gt;
&lt;br /&gt;
The plugin type does not need to be specified because it can be derived from the context. Items like blog that do not have their own context will use their own file area inside a suitable context. In this case, the user context.&lt;br /&gt;
&lt;br /&gt;
Entries with filename = &#039;.&#039; represent directories. Directory entries like this are created automatically when a file is added within them.&lt;br /&gt;
&lt;br /&gt;
Note: &#039;files&#039; plural used even thought that goes against the [[Database|coding guidelines]] because &#039;file&#039; is a reserved word.&lt;br /&gt;
&lt;br /&gt;
==== Table: files_metadata ====&lt;br /&gt;
&lt;br /&gt;
This table contains extra metadata about files.  Repositories could provide this, or it could be manually edited in the local copy.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
| int(10)  &lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;fileid&#039;&#039;&#039;&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| Foreign key, references files.id&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;name&#039;&#039;&#039;&lt;br /&gt;
| varchar(255)&lt;br /&gt;
| &lt;br /&gt;
| The name of the metadata field&lt;br /&gt;
|-&lt;br /&gt;
| value&lt;br /&gt;
| text&lt;br /&gt;
| &lt;br /&gt;
| The value of this metadata field&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: this is not implemented yet.&lt;br /&gt;
&lt;br /&gt;
==== Table: files_sync ====&lt;br /&gt;
&lt;br /&gt;
This table contains information on how to synchronise data with repositories. Data would be synchronised from cron.php or on demand from file manager. The sync would be one way only (repository -&amp;gt; local file).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Field&lt;br /&gt;
! Type &lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
| int(10)  &lt;br /&gt;
| auto-incrementing &lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;fileid&#039;&#039;&#039; &lt;br /&gt;
| int(10)&lt;br /&gt;
|  &lt;br /&gt;
| Foreign key, references files.id&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repositoryid&#039;&#039;&#039;&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| The repository instance this is associated with, see [[Repository_API]]&lt;br /&gt;
|-&lt;br /&gt;
| updates&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| Specifies the update schedule (0 = none, 1 = on demand, other = some period in seconds)&lt;br /&gt;
|-&lt;br /&gt;
| repositorypath&lt;br /&gt;
| text&lt;br /&gt;
| &lt;br /&gt;
| The full path to the original file on the repository&lt;br /&gt;
|-&lt;br /&gt;
| timeimportfirst&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| The first time this file was imported into Moodle&lt;br /&gt;
|-&lt;br /&gt;
| timeimportlast&lt;br /&gt;
| int(10)&lt;br /&gt;
| &lt;br /&gt;
| The most recent time that this file was imported into Moodle&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note: this is not implemented yet. It may end up being implemented within the Repository API istead. That is, this table may end up being called repository_sync.&lt;br /&gt;
&lt;br /&gt;
==== Table: files_cleanup ====&lt;br /&gt;
&lt;br /&gt;
This table contains candidates for deletion from the file pool. Files are not deleted immediately because there may be multiple references to the same file. Therefore, it is better for performance to simply add a row to this table, and later, on cron, clean the files off disc if they are really no longer used. Also, batch deletion on cron makes it easier to avoid concurrency issues when one user deletes what was the last reference to the file as another user adds a new reference. (The cron cleanup code should be doing appropriate locking.)&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
| int(10)  &lt;br /&gt;
| auto-incrementing &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| contenthash&lt;br /&gt;
| varchar(40)&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Implementation of basic operations===&lt;br /&gt;
&lt;br /&gt;
This is just an overview. See the external API description below for how to do this from client code.&lt;br /&gt;
&lt;br /&gt;
====Storing a file====&lt;br /&gt;
&lt;br /&gt;
# Calculate the SHA1 hash of the file contents.&lt;br /&gt;
# Check if a file with this SHA1 hash already exists on disc. If not, store the file there.&lt;br /&gt;
# Remove this SHA1 hash from list of deleted files, if present.&lt;br /&gt;
# Add the record for this file to the files table.&lt;br /&gt;
&lt;br /&gt;
====Reading a file====&lt;br /&gt;
&lt;br /&gt;
# Fetch the record (which includes the SHA1 hash) for the file you want from the files table.&lt;br /&gt;
# Retrieve the contents using the SHA1 hash.&lt;br /&gt;
&lt;br /&gt;
====Deleting a file====&lt;br /&gt;
&lt;br /&gt;
# Store the SHA1 hash of the file being deleted in the files_cleanup table.&lt;br /&gt;
# Delete the record from the files table.&lt;br /&gt;
# Later, admin/cron.php will actually delete the file from disc if it is no longer used (with proper table locking to prevent race conditions when adding/deleting files simultaneously).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File browsing and management API ==&lt;br /&gt;
&lt;br /&gt;
This is what other parts of Moodle use to access and manage files. The code is in lib/file/.&lt;br /&gt;
&lt;br /&gt;
These section documents both the public facing parts of this code, and also the inner workings. Hopefully it makes it sufficiently clear which is which. For more information, see the API documentation in the code, and [[Using_the_file_API]].&lt;br /&gt;
&lt;br /&gt;
TODO write the rest of this section.&lt;br /&gt;
&lt;br /&gt;
=== lib/filelib.php ===&lt;br /&gt;
&lt;br /&gt;
Including this file includes all of the other library files.&lt;br /&gt;
&lt;br /&gt;
=== Class: file_browser ===&lt;br /&gt;
&lt;br /&gt;
=== Class: file_info and subclasses ===&lt;br /&gt;
&lt;br /&gt;
=== Class: file_storage ===&lt;br /&gt;
&lt;br /&gt;
=== Class: stored_file ===&lt;br /&gt;
&lt;br /&gt;
=== File exceptions===&lt;br /&gt;
&lt;br /&gt;
== File management user-interface ==&lt;br /&gt;
&lt;br /&gt;
=== File manager ===&lt;br /&gt;
&lt;br /&gt;
All the contexts, file areas and files now form a single huge tree structure, although each user only has access to certain parts of that tree. The file manager (files/index.php) allow users to brows this tree, and manage files within it, according to the level of permissions they have.&lt;br /&gt;
&lt;br /&gt;
Single pane file manager is hard to implement without drag &amp;amp; drop which is notoriously problematic in web based applications. I propose to implement a two pane commander-style file manager. Two pane manager allows you to easily copy/move files between two different contexts (ex: courses).&lt;br /&gt;
&lt;br /&gt;
File manager must not interact directly with filesystem API, instead each module should return traversable tree of files and directories with both real and localised names (localised names are needed for dirs like backupdata).&lt;br /&gt;
&lt;br /&gt;
This code will be in file/.&lt;br /&gt;
&lt;br /&gt;
=== Formslib field types ===&lt;br /&gt;
&lt;br /&gt;
This code will be in lib/form/&lt;br /&gt;
&lt;br /&gt;
* Upload single files.&lt;br /&gt;
* Upload multiple files to a files area.&lt;br /&gt;
* HTML editor (see next)&lt;br /&gt;
&lt;br /&gt;
=== Integration with the HTML editor ===&lt;br /&gt;
&lt;br /&gt;
Each instance of the HTML editor will be told to store related files in a particular file area.&lt;br /&gt;
&lt;br /&gt;
During editing, files will be stored in a draft files area. Then when the form is submitted they will be (automatically) moved into the real file area.&lt;br /&gt;
&lt;br /&gt;
Files will be selected using the repository file picker.&lt;br /&gt;
&lt;br /&gt;
=== Local files repository plugin ===&lt;br /&gt;
&lt;br /&gt;
Allows users to see an browse files they already have access to within this Moodle, for inclusion in the page currently being edited.&lt;br /&gt;
&lt;br /&gt;
This code will be in repository/local/&lt;br /&gt;
&lt;br /&gt;
== Backwards compatibility ==&lt;br /&gt;
&lt;br /&gt;
=== Content backwards compatibility ===&lt;br /&gt;
&lt;br /&gt;
This should be preserved as much as possible. This will involve rewriting links in content during the upgrade to 2.0. &lt;br /&gt;
&lt;br /&gt;
Some new features (like resource sharing - if implemented) may not work with existing data that still uses files from course files area.&lt;br /&gt;
&lt;br /&gt;
There might be a breakage of links due to special characters stripping in uploaded files which will not match the links in uploaded html files any more. This should not be very common I hope.&lt;br /&gt;
&lt;br /&gt;
===Code backwards compatibility===&lt;br /&gt;
&lt;br /&gt;
Other Moodle code (for example plugins) will have to be converted to the new APIs. See [[Using_the_file_API]] for guidance.&lt;br /&gt;
&lt;br /&gt;
It is not possible to provide backwards-compatibility here. For example, the old $CFG-&amp;gt;dataroot/$courseid/ will no longer exist, and there is no way to emulate that, so we won&#039;t try.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Upgrade and migration ==&lt;br /&gt;
&lt;br /&gt;
When a site is upgraded to Moodle 2.0, all the files in moodledata will have to be migrated. This is going to be a pain, like DML/DDL was :-(&lt;br /&gt;
&lt;br /&gt;
The upgrade process should be interruptible (like the Unicode upgrade was) so it can be stopped/restarted any time.&lt;br /&gt;
&lt;br /&gt;
=== Migration of content ===&lt;br /&gt;
&lt;br /&gt;
* resources - move files to new resource content file area; can be done automatically for pdf, image resources; definitely not accurate for uploaded web pages&lt;br /&gt;
* questions - image file moved to new area, image tag appended to questions&lt;br /&gt;
* moddata files - the easiest part, just move to new storage&lt;br /&gt;
* coursefiles - there might be many outdated files :-( :-(&lt;br /&gt;
* rss feeds links in readers - will be broken, the new security related code would break it anyway&lt;br /&gt;
&lt;br /&gt;
=== Moving files to files table and file pool ===&lt;br /&gt;
&lt;br /&gt;
The migration process must be interruptable because it might take a very long time. The files would be moved from old location, the restarting would be straightforward.&lt;br /&gt;
&lt;br /&gt;
Proposed stages:&lt;br /&gt;
#migration of all course files except moddata - finish marked by some $CFG-&amp;gt;files_migrated=true; - this step breaks the old file manager and html editor integration&lt;br /&gt;
#migration of blog attachments&lt;br /&gt;
#migration of question files&lt;br /&gt;
#migration of moddata files - each module is responsible to copy data from converted coursefiles or directly from moddata which is not converted automatically&lt;br /&gt;
&lt;br /&gt;
Some people use symbolic links in coursefiles - we must make sure that those will be copied to new storage in both places, though they can not be linked any more - anybody wanting to have content synced will need to move the files to some repository and set up the sync again.&lt;br /&gt;
&lt;br /&gt;
::Talked about a double task here, when migrating course files to module areas:&lt;br /&gt;
::# Parse html files to detect all the dependencies and move them together.&lt;br /&gt;
::# Fallback in pluginfile.php so, if something isn&#039;t found in module filearea, search for it in course filearea, copying it and finally, serving it.&lt;br /&gt;
&lt;br /&gt;
:: Also we talked about the possibility of add a new setting to resource in order to define if it should work against old coursefiles or new autocontained file areas. Migrated resources will point to old coursefiles while new ones will enforce autocontained file areas.&lt;br /&gt;
&lt;br /&gt;
:: it seems that only resource files will be really complex (because allow arbitrary HTML inclusion). The rest (labels, intros... doesn&#039;t) and should be easier to parse.&lt;br /&gt;
&lt;br /&gt;
::[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 19:00, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parts of Moodle that need to be modified ==&lt;br /&gt;
&lt;br /&gt;
Of course, all parts of Moodle that use files need to be changed. See MDL-14589 for a more detailed list.&lt;br /&gt;
&lt;br /&gt;
=== Any form where the user can upload files ===&lt;br /&gt;
&lt;br /&gt;
=== Any from containing a HTML editor ===&lt;br /&gt;
&lt;br /&gt;
Since users can embed images etc. there.&lt;br /&gt;
&lt;br /&gt;
=== Backup/restore ===&lt;br /&gt;
&lt;br /&gt;
File handling in backups needs to be fully rewritten - list of files in xml + pool of sha1 named files with contents. This solves the utf-8 trouble here, yay!!&lt;br /&gt;
&lt;br /&gt;
=== Antivirus scanning ===&lt;br /&gt;
&lt;br /&gt;
== Issues that need to be resolved ==&lt;br /&gt;
&lt;br /&gt;
=== Unicode support in zip format ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;This has now been solved by using the built in zip in PHP 5.2.8.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Zip format is an old standard for compressing files. It was created long before Unicode existed, and Unicode support was only recently added. There are several ways used for encoding of non-ASCII characters in path names, but unfortunately it is not very standardised. Most Windows packers use DOS encoding.&lt;br /&gt;
&lt;br /&gt;
Client software:&lt;br /&gt;
* Windows built-in compression - bundled with Windows, non-standard DOS encoding only&lt;br /&gt;
* WinZip - shareware, Unicode option (since v11.2)&lt;br /&gt;
* TotalCommander - shareware, single byte(DOS) encoding only&lt;br /&gt;
* 7-Zip - free, Unicode or DOS encoding depending on characters used in file name (since v4.58beta)&lt;br /&gt;
* Info-ZIP - free, uses some weird character set conversions&lt;br /&gt;
&lt;br /&gt;
PHP extraction:&lt;br /&gt;
* Info-ZIP binary execution - no Unicode support at all, mangles character sets in file names (depends on OS, see docs), files must be copied to temp directory before compression and after extraction&lt;br /&gt;
* PclZip PHP library - reads single byte encoded names only, problems with random problems and higher memory usage.&lt;br /&gt;
* Zip PHP extension - reads single byte encoded names only, 64bit operating system can not open/create archives with more than 500 files (depends on sum of lengths of all filenames and directories, to be fixed in PHP 5.3 and external PECL library, no PHP 5.2.x backport planned!), adding of files is limited by number of free file handles (around 1000 - depends on OS and other PHP code, workaround is to close and reopen archive)&lt;br /&gt;
&lt;br /&gt;
Large file support:&lt;br /&gt;
PHP running under 32bit operating systems does not support files &amp;gt;2GB (do not expect fix before PHP 6). This might be a potential problem for larger backups.&lt;br /&gt;
&lt;br /&gt;
Tar Alternative:&lt;br /&gt;
* tar with gzip compression - easy to implement in PHP + zlib extension (PclTar, Tar from PEAR or custom code)&lt;br /&gt;
* no problem with unicode in *nix, Windows again expects DOS encoding :-(&lt;br /&gt;
* seems suitable for backup/restore - yay!&lt;br /&gt;
&lt;br /&gt;
Roadmap:&lt;br /&gt;
# add zip processing class that fully hides the underlying library&lt;br /&gt;
# use single byte encoding &amp;quot;garbage in/garbage out&amp;quot; approach for encoding of files in zip archives; add new &#039;zipencoding&#039; string into lang packs (ex: cp852 DOS charset for Czech locale) and use it during extraction, we might support true unicode later when PHP Zip extension does that&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Possible future ideas ==&lt;br /&gt;
&lt;br /&gt;
=== Files maintenance report ===&lt;br /&gt;
&lt;br /&gt;
This would do deep validation of the files on disc. It would:&lt;br /&gt;
* Report when there is a row in the files table, but the corresponding file is missing from the file system.&lt;br /&gt;
* Report files that still exist on disc, even though no references remain.&lt;br /&gt;
* Report orphaned files.&lt;br /&gt;
* Report total disc space usage by context (as much as is possible with content-addressible file storage).&lt;br /&gt;
* Verify that the SHA1 hash of the contents of each file matches the file name.&lt;br /&gt;
&lt;br /&gt;
In addition, it might offer options to fix these problems, where possible.&lt;br /&gt;
&lt;br /&gt;
=== Support quotas per user, course, etc. ===&lt;br /&gt;
&lt;br /&gt;
People want this.&lt;br /&gt;
&lt;br /&gt;
If we have implemented the above report, it would then just be a matter of adding the interface hooks to stop people uploading more files once the quota has been reached.&lt;br /&gt;
&lt;br /&gt;
(We could also divide the file size by number of instances that are using it, this might be considered more accurate in some scenarios.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Using the file API]]&lt;br /&gt;
* [[Repository API]]&lt;br /&gt;
* [[Portfolio API]]&lt;br /&gt;
* [[Resource module file API migration]]&lt;br /&gt;
* MDL-14589 - File API Meta issue&lt;br /&gt;
&lt;br /&gt;
{{CategoryDeveloper}}&lt;br /&gt;
[[Category:Files]]&lt;br /&gt;
&lt;br /&gt;
[[ja:開発:ファイルAPI]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Deprecated_functions_in_2.0&amp;diff=15485</id>
		<title>Deprecated functions in 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Deprecated_functions_in_2.0&amp;diff=15485"/>
		<updated>2009-09-24T09:21:41Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
This page lists all functions that have been deprecated between 1.9 and 2.0, with instructions on how to upgrade your code. A count of remaining function calls in core is kept next to each function name.&lt;br /&gt;
&lt;br /&gt;
== General notes ==&lt;br /&gt;
&lt;br /&gt;
The following functions have been arranged in alphabetical order, although some of them are very closely related to each other. The number of occurrences of these function calls at the time of writing are indicated in parentheses.&lt;br /&gt;
&lt;br /&gt;
== Functions to migrate ==&lt;br /&gt;
=== button_to_popup_window (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
button_to_popup_window($url, $name, $linkname, $height, $width, $title, $options, $return, $id, $class);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$form = new html_form();&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;text = $linkname;&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;title = $title;&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;id = $id;&lt;br /&gt;
$form-&amp;gt;url = $url;&lt;br /&gt;
$form-&amp;gt;add_class($class);&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;add_action(new popup_action(&#039;click&#039;, $url, $name, $options));&lt;br /&gt;
echo $OUTPUT-&amp;gt;button($form);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: popup parameters ($options) &#039;&#039;&#039;must&#039;&#039;&#039; be prepared as an associative array, not a string as previously. Simply reusing the $options param previously passed to button_to_popup_window() will not work. The $height and $width params are also part of the popup params. See lib/deprecatedlib.php for an example of how the legacy function call is handled.&lt;br /&gt;
&lt;br /&gt;
=== choose_from_menu (1) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
choose_from_menu($options, $name, $selected, $nothing, $script, $nothingvalue, $return, $disabled, $tabindex, $id, $listbox, $multiple, $class);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$select = moodle_select::make($options, $name, $selected); // Required&lt;br /&gt;
$select-&amp;gt;nothinglabel = $nothing;&lt;br /&gt;
$select-&amp;gt;nothingvalue = $nothingvalue;&lt;br /&gt;
$select-&amp;gt;disabled = $disabled;&lt;br /&gt;
$select-&amp;gt;tabindex = $tabindex;&lt;br /&gt;
$select-&amp;gt;id = $id;&lt;br /&gt;
$select-&amp;gt;listbox = $listbox;&lt;br /&gt;
$select-&amp;gt;multiple = $multiple;&lt;br /&gt;
$select-&amp;gt;add_classes($class);&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== choose_from_menu_nested (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
choose_from_menu_nested($options, $name, $selected, $nothing, $script, $nothingvalue, $return, $disabled, $tabindex);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$select = moodle_select::make($options, $name, $selected);&lt;br /&gt;
$select-&amp;gt;tabindex = $tabindex;&lt;br /&gt;
$select-&amp;gt;disabled = $disabled;&lt;br /&gt;
$select-&amp;gt;nothingvalue = $nothingvalue;&lt;br /&gt;
$select-&amp;gt;nested = true;&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: &lt;br /&gt;
#The lang string &amp;quot;choose&amp;quot; is no longer used, in favour of &amp;quot;choosedots&amp;quot;.&lt;br /&gt;
#The $script param is no longer supported. Please use component::add_action() instead.&lt;br /&gt;
&lt;br /&gt;
=== choose_from_menu_yesno (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
choose_from_menu_yesno($name, $selected, $script, $return, $disabled, $tabindex);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$select = moodle_select::make_yes_no($name, $selected);&lt;br /&gt;
$select-&amp;gt;disabled = $disabled;&lt;br /&gt;
$select-&amp;gt;tabindex = $tabindex;&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: The $script param has been dropped, you need to use component::add_action() instead.&lt;br /&gt;
&lt;br /&gt;
=== choose_from_radio (0) ===&lt;br /&gt;
&lt;br /&gt;
=== close_window_button (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
close_window_button($name, $return, $reloadopener);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;close_window_button(get_string($name));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: You must pass a language string already processed by get_string().&lt;br /&gt;
&lt;br /&gt;
=== print_continue (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_continue($link, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;continue_button($link);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== doc_link (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
doc_link($path, $text, $iconpath);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;doc_link($path, $text, $iconpath);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: This is not for general use, do not confuse with help_icon()&lt;br /&gt;
&lt;br /&gt;
=== formerr (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
formerr($string);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;error_text($error);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== helpbutton (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
helpbutton($page, $title, $module, $image, $linktext, $text, $return, $imagetext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$helpicon = new help_icon();&lt;br /&gt;
$helpicon-&amp;gt;page = $page; // required&lt;br /&gt;
$helpicon-&amp;gt;text = $title; // required&lt;br /&gt;
$helpicon-&amp;gt;module = $module; // defaults to &#039;moodle&#039;&lt;br /&gt;
$helpicon-&amp;gt;linktext = $linktext;&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;help_icon($helpicon);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== link_to_popup_window (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
link_to_popup_window($url, $name, $linkname, $height, $width, $title, $options, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$link = html_link::make($url, $linkname);&lt;br /&gt;
$link-&amp;gt;title = $title; // optional&lt;br /&gt;
$options[&#039;height&#039;] = $height; // optional&lt;br /&gt;
$options[&#039;width&#039;] = $width; // optional&lt;br /&gt;
$link-&amp;gt;add_action(new popup_action(&#039;click&#039;, $url, $name, $options));&lt;br /&gt;
echo $OUTPUT-&amp;gt;link($link);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: popup parameters ($options) &#039;&#039;&#039;must&#039;&#039;&#039; be prepared as an associative array, not a string as previously. Simply reusing the $options param previously passed to link_to_popup_window() will not work. See lib/deprecatedlib.php for an example of how the legacy function call is handled.&lt;br /&gt;
&lt;br /&gt;
=== notice_yesno (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
notice_yesno($message, $linkyes, $linkno, $optionsyes, $optionsno, $methodyes, $methodno);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code (simplest form):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;confirm($message, $linkyes, $linkno);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
New code (with options in arrays):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;confirm($message, new moodle_url($linkyes, $optionsyes), new moodle_url($linkno, $optionsno));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
New code (if the &amp;quot;get&amp;quot; method is required):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$formcontinue = html_form::make_button($linkyes, $optionsyes, get_string(&#039;yes&#039;), $methodyes);&lt;br /&gt;
$formcancel = html_form::make_button($linkno, $optionsno, get_string(&#039;no&#039;), $methodno);&lt;br /&gt;
echo $OUTPUT-&amp;gt;confirm($message, $formcontinue, $formcancel);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== notify (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
notify($message, $classes, $align, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;notification($message, $classes=&#039;&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: Use a CSS rule for alignment.&lt;br /&gt;
&lt;br /&gt;
=== popup_form (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
popup_form($baseurl, $options, $formid, $selected, $nothing, $help, $helptext, $return, $targetwindow, $selectlabel, $optionsextra, $submitvalue, $disabled, $showbutton);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// if $baseurl == &#039;http://domain.com/index.php?var1=1&amp;amp;amp;var2=&#039;&lt;br /&gt;
$select = html_select::make_popup_form(&#039;http://domain.com/index.php?var1=1&#039;, &#039;var2&#039;, $options, $formid, $selected);&lt;br /&gt;
$select-&amp;gt;disabled = $disabled; // optional&lt;br /&gt;
$select-&amp;gt;set_label($selectlabel, $select-&amp;gt;id); // optional, set to false if no &amp;quot;nothing&amp;quot; option is desired (when $selectlabel == &#039;&#039; in original call)&lt;br /&gt;
$select-&amp;gt;set_help_icon($help, $helptext); // optional&lt;br /&gt;
$select-&amp;gt;form-&amp;gt;button-&amp;gt;text = $submitvalue; // optional&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: &lt;br /&gt;
*The $optionsextra param is not supported. If your code uses it, you should find another way to add extra params to your &amp;lt;option&amp;gt; tags without using this horrible hack which usually includes inline JS or CSS.&lt;br /&gt;
&lt;br /&gt;
=== print_arrow (15) ===&lt;br /&gt;
=== print_box* (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_box($message, $classes, $ids, $return);&lt;br /&gt;
print_box_start($classes, $ids, $return);&lt;br /&gt;
print_box_end();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;box($message, $classes, $ids);&lt;br /&gt;
echo $OUTPUT-&amp;gt;box_start($classes, $ids);&lt;br /&gt;
echo $OUTPUT-&amp;gt;box_end();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_checkbox (10) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_checkbox($name, $value, $checked, $label, $alt, $script, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$checkbox = new html_select_option();&lt;br /&gt;
$checkbox-&amp;gt;value = $value; // Required&lt;br /&gt;
$checkbox-&amp;gt;selected = $checked;&lt;br /&gt;
$checkbox-&amp;gt;text = $label;&lt;br /&gt;
$checkbox-&amp;gt;label-&amp;gt;text = $label;&lt;br /&gt;
$checkbox-&amp;gt;alt = $alt;&lt;br /&gt;
                                               &lt;br /&gt;
echo $OUTPUT-&amp;gt;checkbox($checkbox, $name);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: html_select_option is a component that can be rendered as a select &amp;lt;option&amp;gt;, a radio button or a checkbox. It holds sufficient information to render all these elements, except the $name variable which is attached to a moodle_select component. This is why we pass the $name string to $OUTPUT-&amp;gt;checkbox().&lt;br /&gt;
&lt;br /&gt;
=== print_container (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_container($message, $clearfix, $classes, $idbase, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($clearfix) {&lt;br /&gt;
    $classes .= &#039; clearfix&#039;;&lt;br /&gt;
}&lt;br /&gt;
echo $OUTPUT-&amp;gt;container($message, $classes, $idbase);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_date_selector (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_footer (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_footer($course, $usercourse, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: No parameters are required.&lt;br /&gt;
&lt;br /&gt;
=== print_header (501) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_header($title, $heading, $navigation, $focus, $meta, $cache, $button, $menu, $usexml, $bodytags, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;set_heading($heading); // Required&lt;br /&gt;
$PAGE-&amp;gt;set_title($title);&lt;br /&gt;
$PAGE-&amp;gt;set_cacheable($cache);&lt;br /&gt;
$PAGE-&amp;gt;set_focuscontrol($focus);&lt;br /&gt;
$PAGE-&amp;gt;set_button($button);&lt;br /&gt;
echo $OUTPUT-&amp;gt;header($navigation, $menu);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: Navigation code is being rewritten, this doc will then be updated with the new usage.&lt;br /&gt;
&lt;br /&gt;
=== print_heading_with_help (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_heading (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_heading($text, $align, $size, $class, $return, $id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;heading($text, $size, $class, $id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: Use a CSS class rule to control alignment.&lt;br /&gt;
&lt;br /&gt;
=== print_heading_block (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_headline (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_headline($text, $size);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;heading($text, $size);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_paging_bar (2) ===&lt;br /&gt;
&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar, $nocurr, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$pagingbar = new moodle_paging_bar();&lt;br /&gt;
$pagingbar-&amp;gt;totalcount = $totalcount; // Required&lt;br /&gt;
$pagingbar-&amp;gt;page = $page; // Required&lt;br /&gt;
$pagingbar-&amp;gt;perpage = $perpage; // Required&lt;br /&gt;
$pagingbar-&amp;gt;baseurl = $baseurl; // Required&lt;br /&gt;
$pagingbar-&amp;gt;pagevar = $pagevar;&lt;br /&gt;
echo $OUTPUT-&amp;gt;paging_bar($pagingbar);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: The last two instances of print_paging_bar are found in the old tablelib, which will soon be deprecated.&lt;br /&gt;
&lt;br /&gt;
Note: $nocurr has been dropped. Current page number is never displayed as a link&lt;br /&gt;
&lt;br /&gt;
=== print_scale_menu_helpbutton (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_scale_menu_helpbutton($courseid, $scale);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;help_button(help_button::make_scale_menu($courseid, $scale));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_side_block (4) ===&lt;br /&gt;
=== print_single_button (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_single_button($link, $options, $label, $method, $notusedanymore, $return, $tooltip, $disabled, $jsconfirmmessage, $formid)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$form = new html_form();&lt;br /&gt;
$form-&amp;gt;url = new moodle_url($link, $options); // Required&lt;br /&gt;
$form-&amp;gt;button = new html_button();&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;text = $label; // Required&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;disabled = $disabled;&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;title = $tooltip;&lt;br /&gt;
$form-&amp;gt;method = $method;&lt;br /&gt;
$form-&amp;gt;id = $formid;&lt;br /&gt;
&lt;br /&gt;
if ($jsconfirmmessage) {&lt;br /&gt;
    $confirmaction = new component_action(&#039;click&#039;, &#039;confirm_dialog&#039;, array(&#039;message&#039; =&amp;gt; $jsconfirmmessage));&lt;br /&gt;
    $form-&amp;gt;button-&amp;gt;add_action($confirmaction);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$output = $OUTPUT-&amp;gt;button($form);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_spacer (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_spacer($height, $width, $br, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$spacer = new html_image();&lt;br /&gt;
$spacer-&amp;gt;height = $height;&lt;br /&gt;
$spacer-&amp;gt;width = $width;&lt;br /&gt;
echo $OUTPUT-&amp;gt;spacer($spacer);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: The $br attribute has been dropped. You can simply add a line break manually if you need one.&lt;br /&gt;
&lt;br /&gt;
=== print_table (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$table = new stdClass();&lt;br /&gt;
$table-&amp;gt;class = &#039;mytable&#039;;&lt;br /&gt;
$table-&amp;gt;head  = array(&#039;Firstname&#039;, &#039;Lastname&#039;);&lt;br /&gt;
$table-&amp;gt;rowclasses = array();&lt;br /&gt;
// (other table properties here)&lt;br /&gt;
$table-&amp;gt;data = array(array(...),array(...),array(...));&lt;br /&gt;
print_table($table, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$table = new html_table();&lt;br /&gt;
$table-&amp;gt;set_classes(&#039;mytable&#039;); // note the new style of setting CSS class&lt;br /&gt;
$table-&amp;gt;head = array(&#039;First name&#039;, &#039;Last name&#039;);&lt;br /&gt;
$table-&amp;gt;colclasses = array(&#039;name fname&#039;,&#039;name lname&#039;);&lt;br /&gt;
$table-&amp;gt;rowclasses = array();&lt;br /&gt;
// (other table properties here)&lt;br /&gt;
$table-&amp;gt;data = array(array(...),array(...),array(...));&lt;br /&gt;
echo $OUTPUT-&amp;gt;table($newtable);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: The new html_table object has the same member variables as the one originally used by print_table(), but has additional, required methods. You must map the old $table object to the new html_table object, as demonstrated above and in lib/deprecatedlib.php (see old print_table function).&lt;br /&gt;
&lt;br /&gt;
=== print_textarea (55) ===&lt;br /&gt;
=== print_textfield (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_time_selector (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_user_picture (0) ===&lt;br /&gt;
&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_user_picture($user, $courseid, $picture, $size, $return, $link, $target, $alttext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code (simple):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$userpic = new moodle_user_picture();&lt;br /&gt;
$userpic-&amp;gt;user = $user;&lt;br /&gt;
$userpic-&amp;gt;courseid = $courseid;&lt;br /&gt;
echo $OUTPUT-&amp;gt;user_picture($userpic);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code (complex: alternate text, size, different image and popup action):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$userpic = new user_picture();&lt;br /&gt;
$userpic-&amp;gt;user = $user;&lt;br /&gt;
$userpic-&amp;gt;courseid = $courseid;&lt;br /&gt;
$userpic-&amp;gt;size = $size;&lt;br /&gt;
$userpic-&amp;gt;link = $link;&lt;br /&gt;
$userpic-&amp;gt;alttext = $alttext;&lt;br /&gt;
$userpic-&amp;gt;image-&amp;gt;src = $picture;&lt;br /&gt;
$userpic-&amp;gt;add_action(new popup_action(&#039;click&#039;, new moodle_url($target)));&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;user_picture($userpic);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_course_button (0) ===&lt;br /&gt;
&lt;br /&gt;
=== update_module_button (94) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$button = update_module_button($cm-&amp;gt;id, $course-&amp;gt;id, get_string(&#039;modulename&#039;, &#039;workshop&#039;)); // passed to print_header_simple()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;set_button($OUTPUT-&amp;gt;update_module_button($cm-&amp;gt;id, &#039;workshop&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: $courseid param has been dropped.&lt;br /&gt;
&lt;br /&gt;
=== update_tag_button (0) ===&lt;br /&gt;
&lt;br /&gt;
== Non-supported functions ==&lt;br /&gt;
These functions have been completely dropped in 2.0, most likely because they were used very little or not at all. All calls to these functions in core should have been replaced.&lt;br /&gt;
=== blocks_print_group ===&lt;br /&gt;
=== print_file_picture ===&lt;br /&gt;
=== print_png ===&lt;br /&gt;
=== print_scale_menu ===&lt;br /&gt;
=== print_side_block_end ===&lt;br /&gt;
=== print_side_block_start ===&lt;br /&gt;
=== print_timer_selector ===&lt;br /&gt;
=== print_user ===&lt;br /&gt;
=== update_categories_search_button ===&lt;br /&gt;
=== update_mymoodle_icon ===&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Developement:How_Moodle_outputs_HTML]]&lt;br /&gt;
* [[Migrating your code to the 2.0 rendering_API]]&lt;br /&gt;
&lt;br /&gt;
{{CategoryDeveloper}}&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Deprecated_functions_in_2.0&amp;diff=15484</id>
		<title>Deprecated functions in 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Deprecated_functions_in_2.0&amp;diff=15484"/>
		<updated>2009-09-24T09:20:33Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* General notes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
This page lists the old HTML-outputting functions of pre-2.0 lib/weblib.php and their 2.0 equivalents using $OUTPUT functions. &lt;br /&gt;
&lt;br /&gt;
== General notes ==&lt;br /&gt;
&lt;br /&gt;
The following functions have been arranged in alphabetical order, although some of them are very closely related to each other. The number of occurrences of these function calls at the time of writing are indicated in parentheses.&lt;br /&gt;
&lt;br /&gt;
== Functions to migrate ==&lt;br /&gt;
=== button_to_popup_window (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
button_to_popup_window($url, $name, $linkname, $height, $width, $title, $options, $return, $id, $class);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$form = new html_form();&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;text = $linkname;&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;title = $title;&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;id = $id;&lt;br /&gt;
$form-&amp;gt;url = $url;&lt;br /&gt;
$form-&amp;gt;add_class($class);&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;add_action(new popup_action(&#039;click&#039;, $url, $name, $options));&lt;br /&gt;
echo $OUTPUT-&amp;gt;button($form);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: popup parameters ($options) &#039;&#039;&#039;must&#039;&#039;&#039; be prepared as an associative array, not a string as previously. Simply reusing the $options param previously passed to button_to_popup_window() will not work. The $height and $width params are also part of the popup params. See lib/deprecatedlib.php for an example of how the legacy function call is handled.&lt;br /&gt;
&lt;br /&gt;
=== choose_from_menu (1) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
choose_from_menu($options, $name, $selected, $nothing, $script, $nothingvalue, $return, $disabled, $tabindex, $id, $listbox, $multiple, $class);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$select = moodle_select::make($options, $name, $selected); // Required&lt;br /&gt;
$select-&amp;gt;nothinglabel = $nothing;&lt;br /&gt;
$select-&amp;gt;nothingvalue = $nothingvalue;&lt;br /&gt;
$select-&amp;gt;disabled = $disabled;&lt;br /&gt;
$select-&amp;gt;tabindex = $tabindex;&lt;br /&gt;
$select-&amp;gt;id = $id;&lt;br /&gt;
$select-&amp;gt;listbox = $listbox;&lt;br /&gt;
$select-&amp;gt;multiple = $multiple;&lt;br /&gt;
$select-&amp;gt;add_classes($class);&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== choose_from_menu_nested (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
choose_from_menu_nested($options, $name, $selected, $nothing, $script, $nothingvalue, $return, $disabled, $tabindex);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$select = moodle_select::make($options, $name, $selected);&lt;br /&gt;
$select-&amp;gt;tabindex = $tabindex;&lt;br /&gt;
$select-&amp;gt;disabled = $disabled;&lt;br /&gt;
$select-&amp;gt;nothingvalue = $nothingvalue;&lt;br /&gt;
$select-&amp;gt;nested = true;&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: &lt;br /&gt;
#The lang string &amp;quot;choose&amp;quot; is no longer used, in favour of &amp;quot;choosedots&amp;quot;.&lt;br /&gt;
#The $script param is no longer supported. Please use component::add_action() instead.&lt;br /&gt;
&lt;br /&gt;
=== choose_from_menu_yesno (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
choose_from_menu_yesno($name, $selected, $script, $return, $disabled, $tabindex);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$select = moodle_select::make_yes_no($name, $selected);&lt;br /&gt;
$select-&amp;gt;disabled = $disabled;&lt;br /&gt;
$select-&amp;gt;tabindex = $tabindex;&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: The $script param has been dropped, you need to use component::add_action() instead.&lt;br /&gt;
&lt;br /&gt;
=== choose_from_radio (0) ===&lt;br /&gt;
&lt;br /&gt;
=== close_window_button (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
close_window_button($name, $return, $reloadopener);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;close_window_button(get_string($name));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: You must pass a language string already processed by get_string().&lt;br /&gt;
&lt;br /&gt;
=== print_continue (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_continue($link, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;continue_button($link);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== doc_link (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
doc_link($path, $text, $iconpath);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;doc_link($path, $text, $iconpath);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: This is not for general use, do not confuse with help_icon()&lt;br /&gt;
&lt;br /&gt;
=== formerr (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
formerr($string);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;error_text($error);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== helpbutton (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
helpbutton($page, $title, $module, $image, $linktext, $text, $return, $imagetext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$helpicon = new help_icon();&lt;br /&gt;
$helpicon-&amp;gt;page = $page; // required&lt;br /&gt;
$helpicon-&amp;gt;text = $title; // required&lt;br /&gt;
$helpicon-&amp;gt;module = $module; // defaults to &#039;moodle&#039;&lt;br /&gt;
$helpicon-&amp;gt;linktext = $linktext;&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;help_icon($helpicon);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== link_to_popup_window (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
link_to_popup_window($url, $name, $linkname, $height, $width, $title, $options, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$link = html_link::make($url, $linkname);&lt;br /&gt;
$link-&amp;gt;title = $title; // optional&lt;br /&gt;
$options[&#039;height&#039;] = $height; // optional&lt;br /&gt;
$options[&#039;width&#039;] = $width; // optional&lt;br /&gt;
$link-&amp;gt;add_action(new popup_action(&#039;click&#039;, $url, $name, $options));&lt;br /&gt;
echo $OUTPUT-&amp;gt;link($link);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: popup parameters ($options) &#039;&#039;&#039;must&#039;&#039;&#039; be prepared as an associative array, not a string as previously. Simply reusing the $options param previously passed to link_to_popup_window() will not work. See lib/deprecatedlib.php for an example of how the legacy function call is handled.&lt;br /&gt;
&lt;br /&gt;
=== notice_yesno (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
notice_yesno($message, $linkyes, $linkno, $optionsyes, $optionsno, $methodyes, $methodno);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code (simplest form):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;confirm($message, $linkyes, $linkno);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
New code (with options in arrays):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;confirm($message, new moodle_url($linkyes, $optionsyes), new moodle_url($linkno, $optionsno));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
New code (if the &amp;quot;get&amp;quot; method is required):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$formcontinue = html_form::make_button($linkyes, $optionsyes, get_string(&#039;yes&#039;), $methodyes);&lt;br /&gt;
$formcancel = html_form::make_button($linkno, $optionsno, get_string(&#039;no&#039;), $methodno);&lt;br /&gt;
echo $OUTPUT-&amp;gt;confirm($message, $formcontinue, $formcancel);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== notify (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
notify($message, $classes, $align, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;notification($message, $classes=&#039;&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: Use a CSS rule for alignment.&lt;br /&gt;
&lt;br /&gt;
=== popup_form (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
popup_form($baseurl, $options, $formid, $selected, $nothing, $help, $helptext, $return, $targetwindow, $selectlabel, $optionsextra, $submitvalue, $disabled, $showbutton);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// if $baseurl == &#039;http://domain.com/index.php?var1=1&amp;amp;amp;var2=&#039;&lt;br /&gt;
$select = html_select::make_popup_form(&#039;http://domain.com/index.php?var1=1&#039;, &#039;var2&#039;, $options, $formid, $selected);&lt;br /&gt;
$select-&amp;gt;disabled = $disabled; // optional&lt;br /&gt;
$select-&amp;gt;set_label($selectlabel, $select-&amp;gt;id); // optional, set to false if no &amp;quot;nothing&amp;quot; option is desired (when $selectlabel == &#039;&#039; in original call)&lt;br /&gt;
$select-&amp;gt;set_help_icon($help, $helptext); // optional&lt;br /&gt;
$select-&amp;gt;form-&amp;gt;button-&amp;gt;text = $submitvalue; // optional&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: &lt;br /&gt;
*The $optionsextra param is not supported. If your code uses it, you should find another way to add extra params to your &amp;lt;option&amp;gt; tags without using this horrible hack which usually includes inline JS or CSS.&lt;br /&gt;
&lt;br /&gt;
=== print_arrow (15) ===&lt;br /&gt;
=== print_box* (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_box($message, $classes, $ids, $return);&lt;br /&gt;
print_box_start($classes, $ids, $return);&lt;br /&gt;
print_box_end();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;box($message, $classes, $ids);&lt;br /&gt;
echo $OUTPUT-&amp;gt;box_start($classes, $ids);&lt;br /&gt;
echo $OUTPUT-&amp;gt;box_end();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_checkbox (10) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_checkbox($name, $value, $checked, $label, $alt, $script, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$checkbox = new html_select_option();&lt;br /&gt;
$checkbox-&amp;gt;value = $value; // Required&lt;br /&gt;
$checkbox-&amp;gt;selected = $checked;&lt;br /&gt;
$checkbox-&amp;gt;text = $label;&lt;br /&gt;
$checkbox-&amp;gt;label-&amp;gt;text = $label;&lt;br /&gt;
$checkbox-&amp;gt;alt = $alt;&lt;br /&gt;
                                               &lt;br /&gt;
echo $OUTPUT-&amp;gt;checkbox($checkbox, $name);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: html_select_option is a component that can be rendered as a select &amp;lt;option&amp;gt;, a radio button or a checkbox. It holds sufficient information to render all these elements, except the $name variable which is attached to a moodle_select component. This is why we pass the $name string to $OUTPUT-&amp;gt;checkbox().&lt;br /&gt;
&lt;br /&gt;
=== print_container (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_container($message, $clearfix, $classes, $idbase, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($clearfix) {&lt;br /&gt;
    $classes .= &#039; clearfix&#039;;&lt;br /&gt;
}&lt;br /&gt;
echo $OUTPUT-&amp;gt;container($message, $classes, $idbase);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_date_selector (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_footer (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_footer($course, $usercourse, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: No parameters are required.&lt;br /&gt;
&lt;br /&gt;
=== print_header (501) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_header($title, $heading, $navigation, $focus, $meta, $cache, $button, $menu, $usexml, $bodytags, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;set_heading($heading); // Required&lt;br /&gt;
$PAGE-&amp;gt;set_title($title);&lt;br /&gt;
$PAGE-&amp;gt;set_cacheable($cache);&lt;br /&gt;
$PAGE-&amp;gt;set_focuscontrol($focus);&lt;br /&gt;
$PAGE-&amp;gt;set_button($button);&lt;br /&gt;
echo $OUTPUT-&amp;gt;header($navigation, $menu);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: Navigation code is being rewritten, this doc will then be updated with the new usage.&lt;br /&gt;
&lt;br /&gt;
=== print_heading_with_help (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_heading (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_heading($text, $align, $size, $class, $return, $id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;heading($text, $size, $class, $id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: Use a CSS class rule to control alignment.&lt;br /&gt;
&lt;br /&gt;
=== print_heading_block (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_headline (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_headline($text, $size);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;heading($text, $size);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_paging_bar (2) ===&lt;br /&gt;
&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar, $nocurr, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$pagingbar = new moodle_paging_bar();&lt;br /&gt;
$pagingbar-&amp;gt;totalcount = $totalcount; // Required&lt;br /&gt;
$pagingbar-&amp;gt;page = $page; // Required&lt;br /&gt;
$pagingbar-&amp;gt;perpage = $perpage; // Required&lt;br /&gt;
$pagingbar-&amp;gt;baseurl = $baseurl; // Required&lt;br /&gt;
$pagingbar-&amp;gt;pagevar = $pagevar;&lt;br /&gt;
echo $OUTPUT-&amp;gt;paging_bar($pagingbar);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: The last two instances of print_paging_bar are found in the old tablelib, which will soon be deprecated.&lt;br /&gt;
&lt;br /&gt;
Note: $nocurr has been dropped. Current page number is never displayed as a link&lt;br /&gt;
&lt;br /&gt;
=== print_scale_menu_helpbutton (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_scale_menu_helpbutton($courseid, $scale);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;help_button(help_button::make_scale_menu($courseid, $scale));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_side_block (4) ===&lt;br /&gt;
=== print_single_button (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_single_button($link, $options, $label, $method, $notusedanymore, $return, $tooltip, $disabled, $jsconfirmmessage, $formid)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$form = new html_form();&lt;br /&gt;
$form-&amp;gt;url = new moodle_url($link, $options); // Required&lt;br /&gt;
$form-&amp;gt;button = new html_button();&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;text = $label; // Required&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;disabled = $disabled;&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;title = $tooltip;&lt;br /&gt;
$form-&amp;gt;method = $method;&lt;br /&gt;
$form-&amp;gt;id = $formid;&lt;br /&gt;
&lt;br /&gt;
if ($jsconfirmmessage) {&lt;br /&gt;
    $confirmaction = new component_action(&#039;click&#039;, &#039;confirm_dialog&#039;, array(&#039;message&#039; =&amp;gt; $jsconfirmmessage));&lt;br /&gt;
    $form-&amp;gt;button-&amp;gt;add_action($confirmaction);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$output = $OUTPUT-&amp;gt;button($form);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_spacer (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_spacer($height, $width, $br, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$spacer = new html_image();&lt;br /&gt;
$spacer-&amp;gt;height = $height;&lt;br /&gt;
$spacer-&amp;gt;width = $width;&lt;br /&gt;
echo $OUTPUT-&amp;gt;spacer($spacer);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: The $br attribute has been dropped. You can simply add a line break manually if you need one.&lt;br /&gt;
&lt;br /&gt;
=== print_table (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$table = new stdClass();&lt;br /&gt;
$table-&amp;gt;class = &#039;mytable&#039;;&lt;br /&gt;
$table-&amp;gt;head  = array(&#039;Firstname&#039;, &#039;Lastname&#039;);&lt;br /&gt;
$table-&amp;gt;rowclasses = array();&lt;br /&gt;
// (other table properties here)&lt;br /&gt;
$table-&amp;gt;data = array(array(...),array(...),array(...));&lt;br /&gt;
print_table($table, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$table = new html_table();&lt;br /&gt;
$table-&amp;gt;set_classes(&#039;mytable&#039;); // note the new style of setting CSS class&lt;br /&gt;
$table-&amp;gt;head = array(&#039;First name&#039;, &#039;Last name&#039;);&lt;br /&gt;
$table-&amp;gt;colclasses = array(&#039;name fname&#039;,&#039;name lname&#039;);&lt;br /&gt;
$table-&amp;gt;rowclasses = array();&lt;br /&gt;
// (other table properties here)&lt;br /&gt;
$table-&amp;gt;data = array(array(...),array(...),array(...));&lt;br /&gt;
echo $OUTPUT-&amp;gt;table($newtable);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: The new html_table object has the same member variables as the one originally used by print_table(), but has additional, required methods. You must map the old $table object to the new html_table object, as demonstrated above and in lib/deprecatedlib.php (see old print_table function).&lt;br /&gt;
&lt;br /&gt;
=== print_textarea (55) ===&lt;br /&gt;
=== print_textfield (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_time_selector (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_user_picture (0) ===&lt;br /&gt;
&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_user_picture($user, $courseid, $picture, $size, $return, $link, $target, $alttext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code (simple):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$userpic = new moodle_user_picture();&lt;br /&gt;
$userpic-&amp;gt;user = $user;&lt;br /&gt;
$userpic-&amp;gt;courseid = $courseid;&lt;br /&gt;
echo $OUTPUT-&amp;gt;user_picture($userpic);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code (complex: alternate text, size, different image and popup action):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$userpic = new user_picture();&lt;br /&gt;
$userpic-&amp;gt;user = $user;&lt;br /&gt;
$userpic-&amp;gt;courseid = $courseid;&lt;br /&gt;
$userpic-&amp;gt;size = $size;&lt;br /&gt;
$userpic-&amp;gt;link = $link;&lt;br /&gt;
$userpic-&amp;gt;alttext = $alttext;&lt;br /&gt;
$userpic-&amp;gt;image-&amp;gt;src = $picture;&lt;br /&gt;
$userpic-&amp;gt;add_action(new popup_action(&#039;click&#039;, new moodle_url($target)));&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;user_picture($userpic);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_course_button (0) ===&lt;br /&gt;
&lt;br /&gt;
=== update_module_button (94) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$button = update_module_button($cm-&amp;gt;id, $course-&amp;gt;id, get_string(&#039;modulename&#039;, &#039;workshop&#039;)); // passed to print_header_simple()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;set_button($OUTPUT-&amp;gt;update_module_button($cm-&amp;gt;id, &#039;workshop&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: $courseid param has been dropped.&lt;br /&gt;
&lt;br /&gt;
=== update_tag_button (0) ===&lt;br /&gt;
&lt;br /&gt;
== Non-supported functions ==&lt;br /&gt;
These functions have been completely dropped in 2.0, most likely because they were used very little or not at all. All calls to these functions in core should have been replaced.&lt;br /&gt;
=== blocks_print_group ===&lt;br /&gt;
=== print_file_picture ===&lt;br /&gt;
=== print_png ===&lt;br /&gt;
=== print_scale_menu ===&lt;br /&gt;
=== print_side_block_end ===&lt;br /&gt;
=== print_side_block_start ===&lt;br /&gt;
=== print_timer_selector ===&lt;br /&gt;
=== print_user ===&lt;br /&gt;
=== update_categories_search_button ===&lt;br /&gt;
=== update_mymoodle_icon ===&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Developement:How_Moodle_outputs_HTML]]&lt;br /&gt;
* [[Migrating your code to the 2.0 rendering_API]]&lt;br /&gt;
&lt;br /&gt;
{{CategoryDeveloper}}&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Outputting_HTML_in_2.0&amp;diff=15973</id>
		<title>Outputting HTML in 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Outputting_HTML_in_2.0&amp;diff=15973"/>
		<updated>2009-09-24T09:19:56Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Outputting HTML in 2.0 moved to Deprecated functions in 2.0: Expanding the use of this page for all deprecated functions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Deprecated functions in 2.0]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Deprecated_functions_in_2.0&amp;diff=15483</id>
		<title>Deprecated functions in 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Deprecated_functions_in_2.0&amp;diff=15483"/>
		<updated>2009-09-24T09:19:56Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Outputting HTML in 2.0 moved to Deprecated functions in 2.0: Expanding the use of this page for all deprecated functions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
This page lists the old HTML-outputting functions of pre-2.0 lib/weblib.php and their 2.0 equivalents using $OUTPUT functions. &lt;br /&gt;
&lt;br /&gt;
== General notes ==&lt;br /&gt;
The principle of the new rendering API is to let the developer build metadata-rich components that &#039;&#039;represent&#039;&#039; HTML elements, but may be rendered in many different ways. A menu component, for example, could be rendered as a &amp;lt;select&amp;gt; element, or as a list of radio buttons or check boxes.&lt;br /&gt;
&lt;br /&gt;
The following functions have been arranged in alphabetical order, although some of them are very closely related to each other. The number of occurrences of these function calls at the time of writing are indicated in parentheses.&lt;br /&gt;
&lt;br /&gt;
== Functions to migrate ==&lt;br /&gt;
=== button_to_popup_window (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
button_to_popup_window($url, $name, $linkname, $height, $width, $title, $options, $return, $id, $class);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$form = new html_form();&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;text = $linkname;&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;title = $title;&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;id = $id;&lt;br /&gt;
$form-&amp;gt;url = $url;&lt;br /&gt;
$form-&amp;gt;add_class($class);&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;add_action(new popup_action(&#039;click&#039;, $url, $name, $options));&lt;br /&gt;
echo $OUTPUT-&amp;gt;button($form);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: popup parameters ($options) &#039;&#039;&#039;must&#039;&#039;&#039; be prepared as an associative array, not a string as previously. Simply reusing the $options param previously passed to button_to_popup_window() will not work. The $height and $width params are also part of the popup params. See lib/deprecatedlib.php for an example of how the legacy function call is handled.&lt;br /&gt;
&lt;br /&gt;
=== choose_from_menu (1) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
choose_from_menu($options, $name, $selected, $nothing, $script, $nothingvalue, $return, $disabled, $tabindex, $id, $listbox, $multiple, $class);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$select = moodle_select::make($options, $name, $selected); // Required&lt;br /&gt;
$select-&amp;gt;nothinglabel = $nothing;&lt;br /&gt;
$select-&amp;gt;nothingvalue = $nothingvalue;&lt;br /&gt;
$select-&amp;gt;disabled = $disabled;&lt;br /&gt;
$select-&amp;gt;tabindex = $tabindex;&lt;br /&gt;
$select-&amp;gt;id = $id;&lt;br /&gt;
$select-&amp;gt;listbox = $listbox;&lt;br /&gt;
$select-&amp;gt;multiple = $multiple;&lt;br /&gt;
$select-&amp;gt;add_classes($class);&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== choose_from_menu_nested (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
choose_from_menu_nested($options, $name, $selected, $nothing, $script, $nothingvalue, $return, $disabled, $tabindex);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$select = moodle_select::make($options, $name, $selected);&lt;br /&gt;
$select-&amp;gt;tabindex = $tabindex;&lt;br /&gt;
$select-&amp;gt;disabled = $disabled;&lt;br /&gt;
$select-&amp;gt;nothingvalue = $nothingvalue;&lt;br /&gt;
$select-&amp;gt;nested = true;&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: &lt;br /&gt;
#The lang string &amp;quot;choose&amp;quot; is no longer used, in favour of &amp;quot;choosedots&amp;quot;.&lt;br /&gt;
#The $script param is no longer supported. Please use component::add_action() instead.&lt;br /&gt;
&lt;br /&gt;
=== choose_from_menu_yesno (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
choose_from_menu_yesno($name, $selected, $script, $return, $disabled, $tabindex);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$select = moodle_select::make_yes_no($name, $selected);&lt;br /&gt;
$select-&amp;gt;disabled = $disabled;&lt;br /&gt;
$select-&amp;gt;tabindex = $tabindex;&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: The $script param has been dropped, you need to use component::add_action() instead.&lt;br /&gt;
&lt;br /&gt;
=== choose_from_radio (0) ===&lt;br /&gt;
&lt;br /&gt;
=== close_window_button (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
close_window_button($name, $return, $reloadopener);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;close_window_button(get_string($name));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: You must pass a language string already processed by get_string().&lt;br /&gt;
&lt;br /&gt;
=== print_continue (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_continue($link, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;continue_button($link);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== doc_link (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
doc_link($path, $text, $iconpath);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;doc_link($path, $text, $iconpath);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: This is not for general use, do not confuse with help_icon()&lt;br /&gt;
&lt;br /&gt;
=== formerr (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
formerr($string);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;error_text($error);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== helpbutton (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
helpbutton($page, $title, $module, $image, $linktext, $text, $return, $imagetext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$helpicon = new help_icon();&lt;br /&gt;
$helpicon-&amp;gt;page = $page; // required&lt;br /&gt;
$helpicon-&amp;gt;text = $title; // required&lt;br /&gt;
$helpicon-&amp;gt;module = $module; // defaults to &#039;moodle&#039;&lt;br /&gt;
$helpicon-&amp;gt;linktext = $linktext;&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;help_icon($helpicon);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== link_to_popup_window (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
link_to_popup_window($url, $name, $linkname, $height, $width, $title, $options, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$link = html_link::make($url, $linkname);&lt;br /&gt;
$link-&amp;gt;title = $title; // optional&lt;br /&gt;
$options[&#039;height&#039;] = $height; // optional&lt;br /&gt;
$options[&#039;width&#039;] = $width; // optional&lt;br /&gt;
$link-&amp;gt;add_action(new popup_action(&#039;click&#039;, $url, $name, $options));&lt;br /&gt;
echo $OUTPUT-&amp;gt;link($link);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: popup parameters ($options) &#039;&#039;&#039;must&#039;&#039;&#039; be prepared as an associative array, not a string as previously. Simply reusing the $options param previously passed to link_to_popup_window() will not work. See lib/deprecatedlib.php for an example of how the legacy function call is handled.&lt;br /&gt;
&lt;br /&gt;
=== notice_yesno (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
notice_yesno($message, $linkyes, $linkno, $optionsyes, $optionsno, $methodyes, $methodno);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code (simplest form):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;confirm($message, $linkyes, $linkno);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
New code (with options in arrays):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;confirm($message, new moodle_url($linkyes, $optionsyes), new moodle_url($linkno, $optionsno));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
New code (if the &amp;quot;get&amp;quot; method is required):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$formcontinue = html_form::make_button($linkyes, $optionsyes, get_string(&#039;yes&#039;), $methodyes);&lt;br /&gt;
$formcancel = html_form::make_button($linkno, $optionsno, get_string(&#039;no&#039;), $methodno);&lt;br /&gt;
echo $OUTPUT-&amp;gt;confirm($message, $formcontinue, $formcancel);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== notify (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
notify($message, $classes, $align, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;notification($message, $classes=&#039;&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: Use a CSS rule for alignment.&lt;br /&gt;
&lt;br /&gt;
=== popup_form (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
popup_form($baseurl, $options, $formid, $selected, $nothing, $help, $helptext, $return, $targetwindow, $selectlabel, $optionsextra, $submitvalue, $disabled, $showbutton);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// if $baseurl == &#039;http://domain.com/index.php?var1=1&amp;amp;amp;var2=&#039;&lt;br /&gt;
$select = html_select::make_popup_form(&#039;http://domain.com/index.php?var1=1&#039;, &#039;var2&#039;, $options, $formid, $selected);&lt;br /&gt;
$select-&amp;gt;disabled = $disabled; // optional&lt;br /&gt;
$select-&amp;gt;set_label($selectlabel, $select-&amp;gt;id); // optional, set to false if no &amp;quot;nothing&amp;quot; option is desired (when $selectlabel == &#039;&#039; in original call)&lt;br /&gt;
$select-&amp;gt;set_help_icon($help, $helptext); // optional&lt;br /&gt;
$select-&amp;gt;form-&amp;gt;button-&amp;gt;text = $submitvalue; // optional&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;select($select);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: &lt;br /&gt;
*The $optionsextra param is not supported. If your code uses it, you should find another way to add extra params to your &amp;lt;option&amp;gt; tags without using this horrible hack which usually includes inline JS or CSS.&lt;br /&gt;
&lt;br /&gt;
=== print_arrow (15) ===&lt;br /&gt;
=== print_box* (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_box($message, $classes, $ids, $return);&lt;br /&gt;
print_box_start($classes, $ids, $return);&lt;br /&gt;
print_box_end();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;box($message, $classes, $ids);&lt;br /&gt;
echo $OUTPUT-&amp;gt;box_start($classes, $ids);&lt;br /&gt;
echo $OUTPUT-&amp;gt;box_end();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_checkbox (10) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_checkbox($name, $value, $checked, $label, $alt, $script, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$checkbox = new html_select_option();&lt;br /&gt;
$checkbox-&amp;gt;value = $value; // Required&lt;br /&gt;
$checkbox-&amp;gt;selected = $checked;&lt;br /&gt;
$checkbox-&amp;gt;text = $label;&lt;br /&gt;
$checkbox-&amp;gt;label-&amp;gt;text = $label;&lt;br /&gt;
$checkbox-&amp;gt;alt = $alt;&lt;br /&gt;
                                               &lt;br /&gt;
echo $OUTPUT-&amp;gt;checkbox($checkbox, $name);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: html_select_option is a component that can be rendered as a select &amp;lt;option&amp;gt;, a radio button or a checkbox. It holds sufficient information to render all these elements, except the $name variable which is attached to a moodle_select component. This is why we pass the $name string to $OUTPUT-&amp;gt;checkbox().&lt;br /&gt;
&lt;br /&gt;
=== print_container (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_container($message, $clearfix, $classes, $idbase, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($clearfix) {&lt;br /&gt;
    $classes .= &#039; clearfix&#039;;&lt;br /&gt;
}&lt;br /&gt;
echo $OUTPUT-&amp;gt;container($message, $classes, $idbase);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_date_selector (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_footer (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_footer($course, $usercourse, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: No parameters are required.&lt;br /&gt;
&lt;br /&gt;
=== print_header (501) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_header($title, $heading, $navigation, $focus, $meta, $cache, $button, $menu, $usexml, $bodytags, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;set_heading($heading); // Required&lt;br /&gt;
$PAGE-&amp;gt;set_title($title);&lt;br /&gt;
$PAGE-&amp;gt;set_cacheable($cache);&lt;br /&gt;
$PAGE-&amp;gt;set_focuscontrol($focus);&lt;br /&gt;
$PAGE-&amp;gt;set_button($button);&lt;br /&gt;
echo $OUTPUT-&amp;gt;header($navigation, $menu);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: Navigation code is being rewritten, this doc will then be updated with the new usage.&lt;br /&gt;
&lt;br /&gt;
=== print_heading_with_help (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_heading (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_heading($text, $align, $size, $class, $return, $id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;heading($text, $size, $class, $id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: Use a CSS class rule to control alignment.&lt;br /&gt;
&lt;br /&gt;
=== print_heading_block (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_headline (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_headline($text, $size);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;heading($text, $size);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_paging_bar (2) ===&lt;br /&gt;
&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar, $nocurr, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$pagingbar = new moodle_paging_bar();&lt;br /&gt;
$pagingbar-&amp;gt;totalcount = $totalcount; // Required&lt;br /&gt;
$pagingbar-&amp;gt;page = $page; // Required&lt;br /&gt;
$pagingbar-&amp;gt;perpage = $perpage; // Required&lt;br /&gt;
$pagingbar-&amp;gt;baseurl = $baseurl; // Required&lt;br /&gt;
$pagingbar-&amp;gt;pagevar = $pagevar;&lt;br /&gt;
echo $OUTPUT-&amp;gt;paging_bar($pagingbar);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: The last two instances of print_paging_bar are found in the old tablelib, which will soon be deprecated.&lt;br /&gt;
&lt;br /&gt;
Note: $nocurr has been dropped. Current page number is never displayed as a link&lt;br /&gt;
&lt;br /&gt;
=== print_scale_menu_helpbutton (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_scale_menu_helpbutton($courseid, $scale);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo $OUTPUT-&amp;gt;help_button(help_button::make_scale_menu($courseid, $scale));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_side_block (4) ===&lt;br /&gt;
=== print_single_button (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_single_button($link, $options, $label, $method, $notusedanymore, $return, $tooltip, $disabled, $jsconfirmmessage, $formid)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$form = new html_form();&lt;br /&gt;
$form-&amp;gt;url = new moodle_url($link, $options); // Required&lt;br /&gt;
$form-&amp;gt;button = new html_button();&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;text = $label; // Required&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;disabled = $disabled;&lt;br /&gt;
$form-&amp;gt;button-&amp;gt;title = $tooltip;&lt;br /&gt;
$form-&amp;gt;method = $method;&lt;br /&gt;
$form-&amp;gt;id = $formid;&lt;br /&gt;
&lt;br /&gt;
if ($jsconfirmmessage) {&lt;br /&gt;
    $confirmaction = new component_action(&#039;click&#039;, &#039;confirm_dialog&#039;, array(&#039;message&#039; =&amp;gt; $jsconfirmmessage));&lt;br /&gt;
    $form-&amp;gt;button-&amp;gt;add_action($confirmaction);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$output = $OUTPUT-&amp;gt;button($form);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== print_spacer (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_spacer($height, $width, $br, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$spacer = new html_image();&lt;br /&gt;
$spacer-&amp;gt;height = $height;&lt;br /&gt;
$spacer-&amp;gt;width = $width;&lt;br /&gt;
echo $OUTPUT-&amp;gt;spacer($spacer);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: The $br attribute has been dropped. You can simply add a line break manually if you need one.&lt;br /&gt;
&lt;br /&gt;
=== print_table (0) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$table = new stdClass();&lt;br /&gt;
$table-&amp;gt;class = &#039;mytable&#039;;&lt;br /&gt;
$table-&amp;gt;head  = array(&#039;Firstname&#039;, &#039;Lastname&#039;);&lt;br /&gt;
$table-&amp;gt;rowclasses = array();&lt;br /&gt;
// (other table properties here)&lt;br /&gt;
$table-&amp;gt;data = array(array(...),array(...),array(...));&lt;br /&gt;
print_table($table, $return);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$table = new html_table();&lt;br /&gt;
$table-&amp;gt;set_classes(&#039;mytable&#039;); // note the new style of setting CSS class&lt;br /&gt;
$table-&amp;gt;head = array(&#039;First name&#039;, &#039;Last name&#039;);&lt;br /&gt;
$table-&amp;gt;colclasses = array(&#039;name fname&#039;,&#039;name lname&#039;);&lt;br /&gt;
$table-&amp;gt;rowclasses = array();&lt;br /&gt;
// (other table properties here)&lt;br /&gt;
$table-&amp;gt;data = array(array(...),array(...),array(...));&lt;br /&gt;
echo $OUTPUT-&amp;gt;table($newtable);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Notes: The new html_table object has the same member variables as the one originally used by print_table(), but has additional, required methods. You must map the old $table object to the new html_table object, as demonstrated above and in lib/deprecatedlib.php (see old print_table function).&lt;br /&gt;
&lt;br /&gt;
=== print_textarea (55) ===&lt;br /&gt;
=== print_textfield (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_time_selector (0) ===&lt;br /&gt;
&lt;br /&gt;
=== print_user_picture (0) ===&lt;br /&gt;
&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
print_user_picture($user, $courseid, $picture, $size, $return, $link, $target, $alttext);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code (simple):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$userpic = new moodle_user_picture();&lt;br /&gt;
$userpic-&amp;gt;user = $user;&lt;br /&gt;
$userpic-&amp;gt;courseid = $courseid;&lt;br /&gt;
echo $OUTPUT-&amp;gt;user_picture($userpic);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code (complex: alternate text, size, different image and popup action):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$userpic = new user_picture();&lt;br /&gt;
$userpic-&amp;gt;user = $user;&lt;br /&gt;
$userpic-&amp;gt;courseid = $courseid;&lt;br /&gt;
$userpic-&amp;gt;size = $size;&lt;br /&gt;
$userpic-&amp;gt;link = $link;&lt;br /&gt;
$userpic-&amp;gt;alttext = $alttext;&lt;br /&gt;
$userpic-&amp;gt;image-&amp;gt;src = $picture;&lt;br /&gt;
$userpic-&amp;gt;add_action(new popup_action(&#039;click&#039;, new moodle_url($target)));&lt;br /&gt;
&lt;br /&gt;
echo $OUTPUT-&amp;gt;user_picture($userpic);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== update_course_button (0) ===&lt;br /&gt;
&lt;br /&gt;
=== update_module_button (94) ===&lt;br /&gt;
Old code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$button = update_module_button($cm-&amp;gt;id, $course-&amp;gt;id, get_string(&#039;modulename&#039;, &#039;workshop&#039;)); // passed to print_header_simple()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
New code:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$PAGE-&amp;gt;set_button($OUTPUT-&amp;gt;update_module_button($cm-&amp;gt;id, &#039;workshop&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Note: $courseid param has been dropped.&lt;br /&gt;
&lt;br /&gt;
=== update_tag_button (0) ===&lt;br /&gt;
&lt;br /&gt;
== Non-supported functions ==&lt;br /&gt;
These functions have been completely dropped in 2.0, most likely because they were used very little or not at all. All calls to these functions in core should have been replaced.&lt;br /&gt;
=== blocks_print_group ===&lt;br /&gt;
=== print_file_picture ===&lt;br /&gt;
=== print_png ===&lt;br /&gt;
=== print_scale_menu ===&lt;br /&gt;
=== print_side_block_end ===&lt;br /&gt;
=== print_side_block_start ===&lt;br /&gt;
=== print_timer_selector ===&lt;br /&gt;
=== print_user ===&lt;br /&gt;
=== update_categories_search_button ===&lt;br /&gt;
=== update_mymoodle_icon ===&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Developement:How_Moodle_outputs_HTML]]&lt;br /&gt;
* [[Migrating your code to the 2.0 rendering_API]]&lt;br /&gt;
&lt;br /&gt;
{{CategoryDeveloper}}&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Talk:Blog_2.0&amp;diff=28191</id>
		<title>Talk:Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Talk:Blog_2.0&amp;diff=28191"/>
		<updated>2009-09-24T08:44:25Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Some questions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Some questions ===&lt;br /&gt;
&lt;br /&gt;
* about the &amp;quot;who can view&amp;quot;/&amp;quot;externl blogs&amp;quot;/&amp;quot;associations&amp;quot; things... is going to be some capability like:&lt;br /&gt;
** viewblogentries&lt;br /&gt;
** viewdraftblogentries&lt;br /&gt;
** fetchexternalblogs&lt;br /&gt;
** searchblogentries&lt;br /&gt;
** associateblogentries&lt;br /&gt;
:Yes to the above [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* Also, are &amp;quot;fetched&amp;quot; and manual blog entries differentiated in any way?&lt;br /&gt;
:In the database, entries that have been copied from an external blog will have a value in the post.uniquehash field. Also, you will be able to automatically tag an external blog&#039;s entries with one or more tags [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* Finally, both tags and associations are going to be published in the RSS Feeds? &lt;br /&gt;
:I don&#039;t see why not, maybe that could be optional [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* Is it going to be possible to get RSS Feeds by tag or association?&lt;br /&gt;
:Yes [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Atom_(standard) ATOM] (import from external blog feeds / publish local feeds)&lt;br /&gt;
:Not sure, can you explain? [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* When blog entries are displayed for say, course XX, will also &amp;quot;child&amp;quot; entries be displayed (activity-associated).&lt;br /&gt;
:Yes, whenever an entry is associated with an activity, it is also automatically associated with the course. [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
::&amp;quot;automatically associated&amp;quot; stands for: association record created automatically or rely in accesslib (nested contexts) abilities?&lt;br /&gt;
* If association is disabled in one site (or user if the capability exists), which will be the behaviour of the blocks?&lt;br /&gt;
:Instead of searching for entries associated with the course, it will just show all blog entries (for the site). The links in the blog_menu will be simplified accordingly. [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
--[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 08:45, 22 September 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
===More questions===&lt;br /&gt;
&#039;&#039;&#039;Who can view blog entries&#039;&#039;&#039; - the description is not correct, there were more access levels, we can not just remove them - please explain the differences and propose upgrade routes. The descriptions have to contain all relevant capabilities and their use.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Associations&#039;&#039;&#039; - just the context id does not seem to be granular enough, we use are+itemid elsewhere because it allows you to specify the exact forum discussion or post for example. Another problem is should we allow association with context you are not enrolled in? Please include all relevant capabilities too. &#039;&#039;blogid&#039;&#039; in blog_assoc table links which column and table? Should it be called postid?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;External blogs&#039;&#039;&#039; I would not call it import, maybe better &amp;quot;sync with external blog&amp;quot;. I suppose that it might be good to have a tag name in the blog description. There could be also separate option for synchronisation of external tags. Hmm, we could also fetch posts with some tags only. We do not need tags in order to distinguish external feeds - they have the hash there. The problem I see is if you accidentally add some ext blog and then you want to delete it - we do not know from which feed it was added (I suppose one user may have multiple ext blogs linked). Again include all caps.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Atom&#039;&#039;&#039; what is it &#039;&#039;atom write&#039;&#039; support?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attachments&#039;&#039;&#039; we need embedded images too.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Problems&#039;&#039;&#039;&lt;br /&gt;
* different access control compared to 1.9&lt;br /&gt;
* RSS feeds when posts are not accessible without login need some form of protection&lt;br /&gt;
&lt;br /&gt;
[[User:Petr Škoda (škoďák)|Petr Škoda (škoďák)]] 21:13, 22 September 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Talk:Blog_2.0&amp;diff=28186</id>
		<title>Talk:Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Talk:Blog_2.0&amp;diff=28186"/>
		<updated>2009-09-22T08:56:05Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Some questions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Some questions ===&lt;br /&gt;
&lt;br /&gt;
* about the &amp;quot;who can view&amp;quot;/&amp;quot;externl blogs&amp;quot;/&amp;quot;associations&amp;quot; things... is going to be some capability like:&lt;br /&gt;
** viewblogentries&lt;br /&gt;
** viewdraftblogentries&lt;br /&gt;
** fetchexternalblogs&lt;br /&gt;
** searchblogentries&lt;br /&gt;
** associateblogentries&lt;br /&gt;
:Yes to the above [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* Also, are &amp;quot;fetched&amp;quot; and manual blog entries differentiated in any way?&lt;br /&gt;
:In the database, entries that have been copied from an external blog will have a value in the post.uniquehash field. Also, you will be able to automatically tag an external blog&#039;s entries with one or more tags [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* Finally, both tags and associations are going to be published in the RSS Feeds? &lt;br /&gt;
:I don&#039;t see why not, maybe that could be optional [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* Is it going to be possible to get RSS Feeds by tag or association?&lt;br /&gt;
:Yes [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* ATOM (read/write)?&lt;br /&gt;
:Yes [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* When blog entries are displayed for say, course XX, will also &amp;quot;child&amp;quot; entries be displayed (activity-associated).&lt;br /&gt;
:Yes, whenever an entry is associated with an activity, it is also automatically associated with the course. [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
* If association is disabled in one site (or user if the capability exists), which will be the behaviour of the blocks?&lt;br /&gt;
:Instead of searching for entries associated with the course, it will just show all blog entries (for the site). The links in the blog_menu will be simplified accordingly. [[User:Nicolas Connault|Nicolas Connault]] 08:56, 22 September 2009 (UTC)&lt;br /&gt;
--[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 08:45, 22 September 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
----&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15961</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15961"/>
		<updated>2009-09-22T08:46:15Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Description of improvements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See MDL-19676)&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: MDL-14408&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: MDL-19683&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19686&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19687&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19744&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-8776&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: MDL-19688&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15959</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15959"/>
		<updated>2009-09-22T08:08:51Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: http://moodle.org/mod/forum/discuss.php?d=133348&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See [http://tracker.moodle.org/browse/MDL-19676 this tracker issue])&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-14408]&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19683]&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19686]&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19687]&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19744]&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-8776]&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19688]&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15958</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15958"/>
		<updated>2009-09-22T08:01:59Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Upgrade process */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See [http://tracker.moodle.org/browse/MDL-19676 this tracker issue])&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-14408]&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19683]&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19686]&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19687]&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19744]&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-8776]&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19688]&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
The only task required by the upgrade process is to install the new DB tables.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15957</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15957"/>
		<updated>2009-09-22T08:00:14Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* DB Structure */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See [http://tracker.moodle.org/browse/MDL-19676 this tracker issue])&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-14408]&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19683]&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19686]&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19687]&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19744]&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-8776]&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19688]&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_association&lt;br /&gt;
&lt;br /&gt;
=== blog_external ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this external blog.&lt;br /&gt;
|-&lt;br /&gt;
| userid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The owner of the external blog&lt;br /&gt;
|-&lt;br /&gt;
| name&lt;br /&gt;
| char(255)&lt;br /&gt;
|&lt;br /&gt;
| A descriptive name for the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| description&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| A long description of the external blog. Automatically fetched during initial import, can be overridden.&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| text(small)&lt;br /&gt;
|&lt;br /&gt;
| The URL to the external blog (e.g. http://moodle.org/rss.xml)&lt;br /&gt;
|-&lt;br /&gt;
| timemodified&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| timefetched&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The last time the entries from this external blog were fetched by Moodle.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== blog_association ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Default&lt;br /&gt;
! Info&lt;br /&gt;
|-&lt;br /&gt;
| id&lt;br /&gt;
| int(10)&lt;br /&gt;
| auto-incrementing&lt;br /&gt;
| The unique ID for this blog entry&amp;lt;-&amp;gt;contextid association.&lt;br /&gt;
|-&lt;br /&gt;
| contextid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The context id defined in context table - identifies the instance of the course or plugin associated with the blog entry.&lt;br /&gt;
|-&lt;br /&gt;
| blogid&lt;br /&gt;
| int(10)&lt;br /&gt;
|&lt;br /&gt;
| The id of the blog entry (from the post table) being associated with a course or module instance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15956</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15956"/>
		<updated>2009-09-21T06:52:31Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Who can view blog entries? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See [http://tracker.moodle.org/browse/MDL-19676 this tracker issue])&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-14408]&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19683]&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19686]&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19687]&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19744]&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-8776]&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19688]&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_associations&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15955</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15955"/>
		<updated>2009-09-21T06:52:12Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Who can view blog entries? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See [http://tracker.moodle.org/browse/MDL-19676 |this tracker issue])&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-14408]&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19683]&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19686]&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19687]&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19744]&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-8776]&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19688]&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_associations&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15954</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15954"/>
		<updated>2009-09-21T06:48:14Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* DB Structure */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See [http://tracker.moodle.org/browse/MDL-19676|this tracker issue])&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-14408]&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19683]&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19686]&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19687]&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19744]&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-8776]&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19688]&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
2 new tables need to be added: blog_external and blog_associations&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15953</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15953"/>
		<updated>2009-09-21T06:29:09Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public. (See [http://tracker.moodle.org/browse/MDL-19676|this tracker issue])&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-14408]&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19683]&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19686]&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19687]&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19744]&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-8776]&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
Tracker issue: [http://tracker.moodle.org/browse/MDL-19688]&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15952</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15952"/>
		<updated>2009-09-21T06:24:43Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Other links and resources */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public.&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
&lt;br /&gt;
== Other links and resources ==&lt;br /&gt;
*[[Blogs|Early wishlist]]&lt;br /&gt;
*[[Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15951</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15951"/>
		<updated>2009-09-21T06:24:21Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Other links and resources&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public.&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;br /&gt;
&lt;br /&gt;
== Other links and resources ==&lt;br /&gt;
*[[https://docs.moodle.org/en/Development:Blogs|Early wishlist]]&lt;br /&gt;
*[[https://docs.moodle.org/en/Student_projects/Blog_improvements|2008 GSoC project addressing some of these improvements]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15950</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15950"/>
		<updated>2009-09-21T06:20:48Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Description of improvements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public.&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. &lt;br /&gt;
&lt;br /&gt;
These external blogs are a user preference, and two types of tags can be added to each of them: &amp;quot;External tags&amp;quot; and &amp;quot;Moodle tags&amp;quot;:&lt;br /&gt;
*External tags are used to filter the external blog entries that get copied into Moodle. They must match the tags used by that external blog.&lt;br /&gt;
*Moodle tags are automatically added to each blog entry copied into Moodle from an external blog that uses such tags. This makes it easier to distinguish which of a user&#039;s blog entries come from an external blog.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new DB table.&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15949</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15949"/>
		<updated>2009-09-21T06:16:08Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Description of improvements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public.&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. This requires the creation of a new DB table.&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
=== New &amp;quot;Recent blog entries&amp;quot; block ===&lt;br /&gt;
This block can be configured to display the last N blog entries, filtered by context. For example, if you are viewing an assignment activity, this block would display the last N blog entries that are associated with that assignment.&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15948</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15948"/>
		<updated>2009-09-21T06:14:13Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Description of improvements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public.&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table, in which the associations are recorded.&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. This requires the creation of a new DB table.&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The [[File_API|new file manager]] must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
=== Comments ===&lt;br /&gt;
The new [[Comments_2.0|Comments API]] makes it possible for anyone having the correct capability to comment on blog entries.&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15947</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15947"/>
		<updated>2009-09-21T06:08:30Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Description of improvements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public.&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
This requires the creation of a new table.&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can import blog entries into Moodle from external blogs. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting. This requires the creation of a new DB table.&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
=== File attachments ===&lt;br /&gt;
The new file manager must be implemented to allow for multiple file attachments per blog entry.&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15946</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15946"/>
		<updated>2009-09-21T06:04:46Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Description of improvements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public.&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can enter URLs to other blog to make it possible to import this blog&#039;s entries into the user&#039;s blog in Moodle. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting.&lt;br /&gt;
&lt;br /&gt;
=== Search blog entries ===&lt;br /&gt;
It is now possible to search blog entries using a search box. The results maintain the current context, so that if you are viewing a list of blog entries for a particular user, entering a term in the search box will return only the blog entries for that user that match the search term. The search term also appears in the navigation breadcrumbs.&lt;br /&gt;
&lt;br /&gt;
=== Contextual blog menu ===&lt;br /&gt;
Previously, the &amp;quot;blog menu&amp;quot; block was the same on every page on which it appeared. In 2.0 it changes depending on which page you are. For example, if you are on course/view.php, it will include a link to blog entries associated with that course. Also, the link for adding a new entry will include the courseid, making the course association automatically pre-selected in the blog entry edit form.&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15945</id>
		<title>Blog 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Blog_2.0&amp;diff=15945"/>
		<updated>2009-09-21T05:59:42Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Description of improvements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Work in progress}}{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PROJECT STATE: Coding&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAIN TRACKER ISSUE&#039;&#039;&#039;: MDL-19676 Blog 2.0 improvements&lt;br /&gt;
* &#039;&#039;&#039;DISCUSSION AND COMMENTS&#039;&#039;&#039;: Link to forum discussion will appear here&lt;br /&gt;
&lt;br /&gt;
== Description of improvements ==&lt;br /&gt;
=== Who can view blog entries? ===&lt;br /&gt;
Blog entries are either in draft mode (only the author can view it), or in public mode. In public mode, everyone on the entire site can view the blog entry. &lt;br /&gt;
&lt;br /&gt;
At one stage in 2.0 development there was an attempt to restrict who can view certain blog entries, based on course enrolment and capabilities. However, after extensive discussion and feasibility testing it became clear that this would lead to serious performance and usability issues, and would turn the blog into something more akin to a forum. It was thus decided to make all blogs public.&lt;br /&gt;
&lt;br /&gt;
An admin setting can allow for blog entries to be accessible by non-logged-in users, for the purpose of publishing and RSS feed aggregation.&lt;br /&gt;
&lt;br /&gt;
=== Associations ===&lt;br /&gt;
A blog entry can optionally be associated with a course. This makes it possible for a user to blog &amp;quot;about&amp;quot; that course. All blog entries associated in that way can be viewed on a page like blog/index.php?courseid=3. This type of filtering of blog entries does not take into account course enrolments.&lt;br /&gt;
&lt;br /&gt;
Additionally, blog entries can be associated with activity modules, in which case the blog entry is automatically associated with the module&#039;s course.&lt;br /&gt;
&lt;br /&gt;
=== Improved navigation ===&lt;br /&gt;
Before 2.0, the navigation for any listing of blog entries was either &amp;quot;Site &amp;gt; User &amp;gt; Blogs&amp;quot; (for a user&#039;s blog entries) or &amp;quot;Site &amp;gt; Blogs&amp;quot; for the whole site&#039;s blog entries, optionally filtered by tag. With the new association features, we need a better way to display what type of blog listing we are looking at, based on the parameters passed to blog/index.php. The navigation should also reflect additional filters such as tag and search.&lt;br /&gt;
&lt;br /&gt;
=== External blogs ===&lt;br /&gt;
A user can enter URLs to other blog to make it possible to import this blog&#039;s entries into the user&#039;s blog in Moodle. The external blog&#039;s entries are copied into Moodle when the external blog URL is entered, and a cron task periodically checks these external blogs to copy new entries not yet entered in Moodle. The period of time between checks can be adjusted as an admin setting.&lt;br /&gt;
&lt;br /&gt;
== Potential problems and limitations ==&lt;br /&gt;
&lt;br /&gt;
== DB Structure ==&lt;br /&gt;
&lt;br /&gt;
== Upgrade process ==&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
</feed>