<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://docs.moodle.org/39/en/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Quen</id>
	<title>MoodleDocs - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://docs.moodle.org/39/en/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Quen"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/Special:Contributions/Quen"/>
	<updated>2026-04-07T20:38:31Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Windows_installation&amp;diff=112587</id>
		<title>Windows installation</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Windows_installation&amp;diff=112587"/>
		<updated>2014-05-12T16:47:02Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Known problems */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Installing Moodle}}&lt;br /&gt;
&lt;br /&gt;
==Known problems==&lt;br /&gt;
&lt;br /&gt;
Windows is not suitable for large Moodle installations because PHP for Windows does not currently support 64-bit numbers, even if it is running on 64-bit Windows. The most significant limitation this causes is a maximum file size of 2GB, which can cause problems (for example) when making backups of large courses. If you require files larger than 2GB, please consider a different operating system.&lt;br /&gt;
&lt;br /&gt;
==Installation Packages==&lt;br /&gt;
If you are running a small (less than 30 users) Moodle server or just want to test Moodle on your Windows  PC, pre-built packages are available for you to use. Here are links to pages containing step-by-step instructions for installing Moodle using install packages:&lt;br /&gt;
&lt;br /&gt;
*[[Complete install packages for Windows]] for most Windows versions&lt;br /&gt;
*[[Windows installation using Git|Installation guide for Windows using WAMP and Git]] How to install Moodle on your Windows PC and update it regularly via Git.&lt;br /&gt;
&lt;br /&gt;
*[[Windows installation using XAMPP|Installation guide for Windows using XAMPP]] A more typical webserver installation than a complete install package. Both use XAMPP.&lt;br /&gt;
&lt;br /&gt;
*[[Installation guide for Windows using EasyPHP]]&lt;br /&gt;
&lt;br /&gt;
== Manual Installation ==&lt;br /&gt;
For medium to large installations (e.g. a college, university or business), it is best practice to install Moodle on your server manually. &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Plan your system capacity&#039;&#039;&#039;. This involves estimating the appropriate hardware to support the number of users in your organisation. See [[Installing Moodle#How_many_users.3F| Installing Moodle ]] in the How Many Users section for a method of doing this.&lt;br /&gt;
* &#039;&#039;&#039;Install your database server&#039;&#039;&#039;. You have a choice of [http://dev.mysql.com/downloads/ MySQL]/[http://mariadb.org/ MariaDB] (recommended), [http://www.postgresql.org/download/ PostgreSQL] (recommended), [[Installing MSSQL for PHP | Microsoft SQL Server 2005]] or Oracle (not recommended).&lt;br /&gt;
* &#039;&#039;&#039;Install your web server&#039;&#039;&#039;. You have several choices - the decision as to which one to use will depend on your in-house expertise and your required level of sustainability:&lt;br /&gt;
**Apache 2 is recommended as the most tested and popular for Moodle installations. See these instructions for [[Installing Apache on Windows |manually installing Apache 2 on Windows]].&lt;br /&gt;
**[[IIS]] 7 server can also be used. See the Windows forum for guidance on installation and, in particular, permission settings for using Moodle with IIS and CGI timeouts.&lt;br /&gt;
**Other webservers are known to install on Windows, e.g. Lighttpd, so you may wish to experiment with these if available memory is low on your server.&lt;br /&gt;
* &#039;&#039;&#039;Install PHP&#039;&#039;&#039;. Use Microsoft Web Platform Installer when using [[IIS]] server.&lt;br /&gt;
* &#039;&#039;&#039;Install Moodle&#039;&#039;&#039; by getting the standard installation for Moodle from [http://download.moodle.org/ http://download.moodle.org/] and read [[Installing Moodle]] which has detailed generic information.&lt;br /&gt;
* &#039;&#039;&#039;Setup backups&#039;&#039;&#039;. Once Moodle is setup and configured, you should setup backups of the system in case of failure or loss of data. &lt;br /&gt;
** &#039;&#039;&#039;To perform full site backups&#039;&#039;&#039; you need to backup the moodledata and moodle directories, Apache webserver configuration (httpd.conf) if you&#039;re using Apache, PHP configuration (php.ini) and any php extensions which are non-standard, and the mysql database. To do this use the integrated backup program (Start -&amp;gt; All Programs -&amp;gt; Accessories -&amp;gt; System Tools -&amp;gt; Backup) or your own proprietary backup software (e.g. BackupExec). To backup your mysql database see the [[Backup and restore FAQ]].&lt;br /&gt;
** &#039;&#039;&#039;To perform course backups&#039;&#039;&#039; see the [[Course backup]] page.&lt;br /&gt;
** You should also perform a &#039;&#039;&#039;state backup&#039;&#039;&#039; of the [http://technet2.microsoft.com/WindowsServer/en/library/921f0ed5-523d-48ac-8825-e850b0e548841033.mspx?mfr=true server] or [http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/ntbackup_backup_sysstate.mspx?mfr=true PC]. This is especially important if you&#039;re using IIS as this will backup the IIS metabase. &lt;br /&gt;
* &#039;&#039;&#039;Check your server security and performance&#039;&#039;&#039;. It is also good practice to read the [[Performance]] and [[Security]] documentation. Although much of the content is targeted at Linux/Unix users, there is a growing amount for Windows systems.&lt;br /&gt;
* Set-up your &#039;&#039;&#039;Active Directory authentication&#039;&#039;&#039;. You can use the standard [[LDAP authentication]] which prompts users with a username/password, or [[NTLM authentication | integrated NTLM authentication]] which does not require campus users to enter their credentials.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Manual install on Windows 7 with Apache and MySQL]]&lt;br /&gt;
* [[Installing APC in Windows]] contains instructions for using a PHP accelerator to reduce processor load.&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=56835 Running Apache and IIS on the same server] forum discussion.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Windows_installation&amp;diff=112586</id>
		<title>Windows installation</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Windows_installation&amp;diff=112586"/>
		<updated>2014-05-12T16:46:22Z</updated>

		<summary type="html">&lt;p&gt;Quen: Corrected paragraph about Windows 32-bit limit&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Installing Moodle}}&lt;br /&gt;
&lt;br /&gt;
==Known problems==&lt;br /&gt;
&lt;br /&gt;
Windows is not suitable for large Moodle installations because PHP for Windows does not currently support 64-bit numbers even in 64-bit Windows. The most significant limitation this causes is a maximum file size of 2GB, which can cause problems (for example) when making backups of large courses. If you require files larger than 2GB, please consider a different operating system.&lt;br /&gt;
&lt;br /&gt;
==Installation Packages==&lt;br /&gt;
If you are running a small (less than 30 users) Moodle server or just want to test Moodle on your Windows  PC, pre-built packages are available for you to use. Here are links to pages containing step-by-step instructions for installing Moodle using install packages:&lt;br /&gt;
&lt;br /&gt;
*[[Complete install packages for Windows]] for most Windows versions&lt;br /&gt;
*[[Windows installation using Git|Installation guide for Windows using WAMP and Git]] How to install Moodle on your Windows PC and update it regularly via Git.&lt;br /&gt;
&lt;br /&gt;
*[[Windows installation using XAMPP|Installation guide for Windows using XAMPP]] A more typical webserver installation than a complete install package. Both use XAMPP.&lt;br /&gt;
&lt;br /&gt;
*[[Installation guide for Windows using EasyPHP]]&lt;br /&gt;
&lt;br /&gt;
== Manual Installation ==&lt;br /&gt;
For medium to large installations (e.g. a college, university or business), it is best practice to install Moodle on your server manually. &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Plan your system capacity&#039;&#039;&#039;. This involves estimating the appropriate hardware to support the number of users in your organisation. See [[Installing Moodle#How_many_users.3F| Installing Moodle ]] in the How Many Users section for a method of doing this.&lt;br /&gt;
* &#039;&#039;&#039;Install your database server&#039;&#039;&#039;. You have a choice of [http://dev.mysql.com/downloads/ MySQL]/[http://mariadb.org/ MariaDB] (recommended), [http://www.postgresql.org/download/ PostgreSQL] (recommended), [[Installing MSSQL for PHP | Microsoft SQL Server 2005]] or Oracle (not recommended).&lt;br /&gt;
* &#039;&#039;&#039;Install your web server&#039;&#039;&#039;. You have several choices - the decision as to which one to use will depend on your in-house expertise and your required level of sustainability:&lt;br /&gt;
**Apache 2 is recommended as the most tested and popular for Moodle installations. See these instructions for [[Installing Apache on Windows |manually installing Apache 2 on Windows]].&lt;br /&gt;
**[[IIS]] 7 server can also be used. See the Windows forum for guidance on installation and, in particular, permission settings for using Moodle with IIS and CGI timeouts.&lt;br /&gt;
**Other webservers are known to install on Windows, e.g. Lighttpd, so you may wish to experiment with these if available memory is low on your server.&lt;br /&gt;
* &#039;&#039;&#039;Install PHP&#039;&#039;&#039;. Use Microsoft Web Platform Installer when using [[IIS]] server.&lt;br /&gt;
* &#039;&#039;&#039;Install Moodle&#039;&#039;&#039; by getting the standard installation for Moodle from [http://download.moodle.org/ http://download.moodle.org/] and read [[Installing Moodle]] which has detailed generic information.&lt;br /&gt;
* &#039;&#039;&#039;Setup backups&#039;&#039;&#039;. Once Moodle is setup and configured, you should setup backups of the system in case of failure or loss of data. &lt;br /&gt;
** &#039;&#039;&#039;To perform full site backups&#039;&#039;&#039; you need to backup the moodledata and moodle directories, Apache webserver configuration (httpd.conf) if you&#039;re using Apache, PHP configuration (php.ini) and any php extensions which are non-standard, and the mysql database. To do this use the integrated backup program (Start -&amp;gt; All Programs -&amp;gt; Accessories -&amp;gt; System Tools -&amp;gt; Backup) or your own proprietary backup software (e.g. BackupExec). To backup your mysql database see the [[Backup and restore FAQ]].&lt;br /&gt;
** &#039;&#039;&#039;To perform course backups&#039;&#039;&#039; see the [[Course backup]] page.&lt;br /&gt;
** You should also perform a &#039;&#039;&#039;state backup&#039;&#039;&#039; of the [http://technet2.microsoft.com/WindowsServer/en/library/921f0ed5-523d-48ac-8825-e850b0e548841033.mspx?mfr=true server] or [http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/ntbackup_backup_sysstate.mspx?mfr=true PC]. This is especially important if you&#039;re using IIS as this will backup the IIS metabase. &lt;br /&gt;
* &#039;&#039;&#039;Check your server security and performance&#039;&#039;&#039;. It is also good practice to read the [[Performance]] and [[Security]] documentation. Although much of the content is targeted at Linux/Unix users, there is a growing amount for Windows systems.&lt;br /&gt;
* Set-up your &#039;&#039;&#039;Active Directory authentication&#039;&#039;&#039;. You can use the standard [[LDAP authentication]] which prompts users with a username/password, or [[NTLM authentication | integrated NTLM authentication]] which does not require campus users to enter their credentials.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Manual install on Windows 7 with Apache and MySQL]]&lt;br /&gt;
* [[Installing APC in Windows]] contains instructions for using a PHP accelerator to reduce processor load.&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=56835 Running Apache and IIS on the same server] forum discussion.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Upgrading&amp;diff=112164</id>
		<title>Upgrading</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Upgrading&amp;diff=112164"/>
		<updated>2014-04-29T09:45:25Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Check the requirements */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Installing Moodle}}	&lt;br /&gt;
&#039;&#039;This page explains in detail how to upgrade Moodle. For a summary of the process, see [[Upgrade overview]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Check the requirements==&lt;br /&gt;
&lt;br /&gt;
Check that your server meets all requirements for 2.7 in &#039;&#039;Administration &amp;gt; Site administration &amp;gt; Server &amp;gt; [[Environment]]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Note: You can only upgrade to Moodle 2.7 from Moodle 2.2 or later. If upgrading from earlier versions, you must [https://docs.moodle.org/22/en/Upgrading_to_Moodle_2.2 upgrade to 2.2] as a first step.&lt;br /&gt;
&lt;br /&gt;
==Before upgrading==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;We advise that you test the upgrade first on a COPY of your production site, to make sure it works as you expect.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Themes===&lt;br /&gt;
&lt;br /&gt;
All core themes, except clean, have been removed from Moodle 2.7. Thus sites using any core theme (including sites using a core theme as a parent theme) other than clean must do the following:&lt;br /&gt;
&lt;br /&gt;
# Download the 2.7 version of your theme as a zip file from the [https://moodle.org/plugins/browse.php?list=category&amp;amp;id=3 Themes section of the Moodle plugins directory].&lt;br /&gt;
# Follow the steps above to install the new Moodle software. In step 4, unzip the theme and copy it to moodle/theme/.&lt;br /&gt;
# Proceed with the upgrade.&lt;br /&gt;
&lt;br /&gt;
Note: Only installed add-on themes may be updated automatically during the upgrade, NOT core themes. Because core themes have been removed from Moodle 2.7, they have to be re-added.&lt;br /&gt;
&lt;br /&gt;
===Questions===&lt;br /&gt;
&lt;br /&gt;
In Moodle 2.1 there was a major upgrade to questions. As explained in [https://docs.moodle.org/21/en/Upgrading_to_Moodle_2.1#Planning_the_question_engine_upgrade the upgrade documentation for that version], it was possible to delay parts of the database upgrade to be run later. Before you upgrade to Moodle 2.7, this upgrade must be completed.  You can check for this by looking at the bottom of the [[Environment]] check page in your site.&lt;br /&gt;
&lt;br /&gt;
== Backup important data ==&lt;br /&gt;
&lt;br /&gt;
There are three areas that should be backed up before any upgrade:&lt;br /&gt;
#Moodle software (For example, everything in server/htdocs/moodle)&lt;br /&gt;
#Moodle uploaded files (For example, server/moodledata)&lt;br /&gt;
#Moodle database (For example, your Postgres or MySQL database dump)&lt;br /&gt;
&lt;br /&gt;
See [[Site backup]] for more specific information.&lt;br /&gt;
&lt;br /&gt;
==Put your site into maintenance mode==&lt;br /&gt;
Before you begin upgrading your site, you should put it into [[Maintenance_mode | maintenance mode]] to stop any non-admin users from logging in.&lt;br /&gt;
&lt;br /&gt;
== Check for add-on updates ==&lt;br /&gt;
&lt;br /&gt;
If you have [[Automatic updates deployment]] enabled, you will be able to update installed add-ons automatically during the upgrade. Just make sure you check for available updates (via the button for it) at the Plugins check screen.&lt;br /&gt;
&lt;br /&gt;
If you are updating add-ons manually, it is a good moment now to check in the [http://moodle.org/plugins Moodle Plugins directory] whether there is a 2.7 version available for any add-ons (including themes) that you have previously installed on your site. If so, download the add-on package. In the next step, you will copy it to the appropriate location in your Moodle code (see [[Installing add-ons]]).&lt;br /&gt;
&lt;br /&gt;
The upgrade of the add-on will then happen as part of the Moodle upgrade process.&lt;br /&gt;
&lt;br /&gt;
If an out-of-date add-on causes your upgrade to fail, you can usually delete the add-on code rather than uninstalling it from within Moodle so that the data associated with it is not deleted.&lt;br /&gt;
&lt;br /&gt;
== Install the new Moodle software ==&lt;br /&gt;
&lt;br /&gt;
=== Standard install package ===&lt;br /&gt;
&lt;br /&gt;
# Move your old Moodle software program files to another location. &#039;&#039;Do NOT copy new files over the old files.&#039;&#039;&lt;br /&gt;
# Unzip or unpack the upgrade file so that all the new Moodle software program files are in the location the old files used to be in on the server. Moodle will adjust SQL and moodledata if it needs to in the upgrade.&lt;br /&gt;
# Copy your old [[Configuration file|config.php file]] back to the new Moodle directory. &lt;br /&gt;
# As mentioned above, if you had installed any custom add-ons on your site you should add them to the new code tree now. It is important to check that you get the correct version for your new version of Moodle. Be particularly careful that you do not overwrite any code in the new version of Moodle.&lt;br /&gt;
# Dont forget to also copy over your moodledata folder / directory.  If you don&#039;t you will get a &amp;quot;fatal error $cfg- dataroot is not configured properly&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
====Linux====&lt;br /&gt;
 mv moodle moodle.backup&lt;br /&gt;
 tar xvzf moodle-2.7.tgz&lt;br /&gt;
&lt;br /&gt;
Next, copy across your config.php, any custom plugins, and your .htaccess file if you created one (&#039;&#039;&#039;check that custom plugins are the correct version for your new Moodle first&#039;&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
 cp moodle.backup/config.php moodle&lt;br /&gt;
 cp -pr moodle.backup/theme/mytheme moodle/theme/mytheme&lt;br /&gt;
 cp -pr moodle.backup/mod/mymod moodle/mod/mymod&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to make moodle/config.php (and the rest of the source code) readable by your www server. Ideally the files should not be writeable by your server.&lt;br /&gt;
&lt;br /&gt;
If you use cron, take care that cron.php is executeable and uses the correct php command: &lt;br /&gt;
 chmod 740 admin/cli/cron.php (some configurations need chmod 750 or chmod 755)&lt;br /&gt;
 copy the first line from cron.php (if it looks like &#039;#!/usr/local/bin/php&#039; or &#039;#!/usr/local/bin/php5.3&#039;, no need to copy &#039;&amp;lt;?php&#039;)&lt;br /&gt;
&lt;br /&gt;
if necessary.&lt;br /&gt;
&lt;br /&gt;
=== Using Git ===&lt;br /&gt;
&lt;br /&gt;
You can use Git for updating or upgrading your Moodle. See [[Git for Administrators]] for details.&lt;br /&gt;
&lt;br /&gt;
===Command line upgrade===&lt;br /&gt;
&lt;br /&gt;
On Linux servers, Moodle 2.7 supports running the [[CLI|upgrade from the command line]], rather than through a web browser. This is likely to be more reliable, particularly for large sites.&lt;br /&gt;
&lt;br /&gt;
== Finishing the upgrade ==&lt;br /&gt;
&lt;br /&gt;
The last step is to trigger the upgrade processes within Moodle. &lt;br /&gt;
&lt;br /&gt;
To do this just go to &#039;&#039;Administration &amp;gt; Site administration &amp;gt; Notifications&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Moodle will automatically detect the new version and perform all the SQL database or file system upgrades that are necessary. If there is anything it can&#039;t do itself (very rare) then you will see messages telling you what you need to do.&lt;br /&gt;
&lt;br /&gt;
Assuming all goes well (no error messages) then you can start using your new version of Moodle and enjoy the new features!&lt;br /&gt;
&lt;br /&gt;
Note: If you are running multiple servers then you should purge all caches manually (via &#039;&#039;Administration &amp;gt; Site administration &amp;gt; Development &amp;gt; Purge all caches&#039;&#039;) after completing the upgrade on all servers.&lt;br /&gt;
&lt;br /&gt;
===Fatal error: Maximum execution time of 30 seconds exceeded...===&lt;br /&gt;
&lt;br /&gt;
If your server uses a main language other than English, you may encounter a &#039;Fatal error: Maximum execution time of 30 seconds exceeded&#039; when you try to upgrade it. You can increase max_execution_time = 160 on php.ini to allow the scripts enough time to process the language update. Otherwise, you can switch to English as the default language before doing the upgrade and back to your original language after a succcessful upgrade. See the forum discussion at https://moodle.org/mod/forum/discuss.php?d=119598.&lt;br /&gt;
&lt;br /&gt;
==After upgrading==&lt;br /&gt;
&lt;br /&gt;
The config.php file from your installation should work fine but if you take a look at config-dist.php that came with Moodle 2.7 there are more/different options available (e.g. database drivers and settings). It&#039;s a good idea to map your old config.php settings to a new one based on the 2.7 config-dist.php.&lt;br /&gt;
&lt;br /&gt;
===Assignments===&lt;br /&gt;
&lt;br /&gt;
The old assignment (2.2) module has been removed from core and has been replaced by a stub to support transparently remapping URLs and restoring course backups from the old module to the new one. &lt;br /&gt;
&lt;br /&gt;
If you are still using the old assignment (2.2) module, after upgrading to Moodle 2.7 all assignment (2.2) activities will be hidden. You need to run the [[Assignment upgrade tool]] to un-hide the activities.&lt;br /&gt;
&lt;br /&gt;
If you really, really need to keep using the old assignment (2.2) module, you should update the code to Moodle 2.7, and then replace the &amp;quot;mod/assignment&amp;quot; folder with the one from the plugins directory before completing the upgrade.&lt;br /&gt;
&lt;br /&gt;
==Possible issues that may affect you in Moodle 2.7==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Items to be added here...&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Moodle 2.3, 2.4, 2.5 and 2.6 improvements ===&lt;br /&gt;
&lt;br /&gt;
Depending on which version you are upgrading from, please see the section &#039;Possible issues that may affect you&#039; in the documentation&lt;br /&gt;
&lt;br /&gt;
* [https://docs.moodle.org/23/en/Upgrading Upgrading to Moodle 2.3]&lt;br /&gt;
* [https://docs.moodle.org/24/en/Upgrading Upgrading to Moodle 2.4]&lt;br /&gt;
* [https://docs.moodle.org/25/en/Upgrading Upgrading to Moodle 2.5]&lt;br /&gt;
* [https://docs.moodle.org/26/en/Upgrading Upgrading to Moodle 2.6]&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/view.php?id=28 Installation problems forum] &lt;br /&gt;
* [[dev:Moodle 2.7 release notes|Moodle 2.7 release notes]]&lt;br /&gt;
* [[dev:Upgrade API|Upgrade API]]&lt;br /&gt;
&lt;br /&gt;
[[es:Actualización de moodle]]&lt;br /&gt;
[[fr:Mise à jour]]&lt;br /&gt;
[[ja:Moodleをアップグレードする]]&lt;br /&gt;
[[de:Aktualisierung von Moodle]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Talk:Restrict_access_settings&amp;diff=112147</id>
		<title>Talk:Restrict access settings</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Talk:Restrict_access_settings&amp;diff=112147"/>
		<updated>2014-04-25T17:13:35Z</updated>

		<summary type="html">&lt;p&gt;Quen: Created page with &amp;quot;==sam comments==  Thanks for your work on this documentation! I had a look at the page and I have a few minor comments:  1) &amp;quot;It may be hidden from students on the course page ...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==sam comments==&lt;br /&gt;
&lt;br /&gt;
Thanks for your work on this documentation! I had a look at the page and I have a few minor comments:&lt;br /&gt;
&lt;br /&gt;
1) &amp;quot;It may be hidden from students on the course page by clicking the eye:&amp;quot;&lt;br /&gt;
&lt;br /&gt;
As the eye is the hardest part to understand, it may be worth ensuring this part of the documentation is accurate and complete (because it&#039;s the sort of thing people might hit up the documentation for). I&#039;m not a documentation expert so I didn&#039;t want to change it directly but here&#039;s what the eye icon actually does:&lt;br /&gt;
&lt;br /&gt;
* If the eye is SHUT then students who do not meet that part of the condition will not see the activity at all.&lt;br /&gt;
* If the eye is OPEN the students who do not meet that part of the condition will see the activity but it will be greyed out and have information about why they can&#039;t access it yet.&lt;br /&gt;
&lt;br /&gt;
The shut eye takes precedence. For example, you could have 2 conditions, one based on date (with eye shut) and one based on completing a previous activity (with eye open). That way, the activity will not appear at all until the date; then it will appear but tell you that you need to complete the other activity; then when you complete the other activity you can access it.&lt;br /&gt;
&lt;br /&gt;
For OR and NOT AND type conditions, you only get a single eye icon instead of one for each condition (it doesn&#039;t logically make sense if there are multiple ones in those cases).&lt;br /&gt;
&lt;br /&gt;
There is a really good reason for this to be as complicated as it is, honest. :)&lt;br /&gt;
&lt;br /&gt;
2) At present you&#039;ve left the old documentation and screenshot in there for the section settings. In 2.7 the section interface is the same as the activity interface.&lt;br /&gt;
&lt;br /&gt;
3) I wonder if it is worth mentioning that certain restriction options are not always available. (In other words, whether &#039;there are loads of buttons on your screenshot but I only have 3&#039; might become an FAQ) depending on whether they make sense for your situation. For example the group/grouping buttons are available but only if you actually have group/groupings in the course (and you haven&#039;t turned on the old &#039;groupmembersonly&#039; option). The activity completion option is only available if you turned on activity completion/progress for the course. Not sure this needs mentioning as may be obvious.&lt;br /&gt;
&lt;br /&gt;
[[User:sam marshall|sam marshall]] ([[User talk:sam marshall|talk]]) 01:13, 26 April 2014 (WST)&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Session_handling&amp;diff=109663</id>
		<title>Session handling</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Session_handling&amp;diff=109663"/>
		<updated>2014-02-04T10:58:09Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Memcached */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Server settings}}&lt;br /&gt;
An administrator can change the following settings in &#039;&#039;Administration &amp;gt; Site administration &amp;gt; Server &amp;gt; Session Handling&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Once someone logs in to your Moodle server, the server starts a session. The session data allows the server to track users as they access different pages.&lt;br /&gt;
&lt;br /&gt;
==Use database for session information==&lt;br /&gt;
&lt;br /&gt;
Moodle needs to store the session data in some storage. By default either file or database session storage is selected, this option allows admin to change it. Large installation should use memcached driver described below.&lt;br /&gt;
&lt;br /&gt;
Note that this option disappears after setting the $CFG-&amp;gt;session_handler_class in config.php file.&lt;br /&gt;
&lt;br /&gt;
==Timeout==&lt;br /&gt;
&lt;br /&gt;
If users don&#039;t load a new page during the amount of time set here, Moodle will end their session and log them out.&lt;br /&gt;
&lt;br /&gt;
Be sure this time frame is long enough to cover the longest test your teachers may offer. If a student is logged out while they are taking a test, their responses to the test questions may be lost.&lt;br /&gt;
&lt;br /&gt;
==Cookie prefix==&lt;br /&gt;
&lt;br /&gt;
Most of the time, you can leave this blank, unless you are running more than one Moodle site on the same server. In this case, you will want to customize the name of the cookie each Moodle site uses to track the session. This enables you to be logged into more than one Moodle site at the same time.&lt;br /&gt;
&lt;br /&gt;
Note: If you change &amp;quot;Cookie prefix&amp;quot; or &amp;quot;Cookie path&amp;quot; you will need to login again as the changes take effect immediately.&lt;br /&gt;
&lt;br /&gt;
==Cookie path==&lt;br /&gt;
&lt;br /&gt;
The relative path to this Moodle installation, this may be used to force sending of Moodle session cookie to parent directories. Invalid values are ignored automatically.&lt;br /&gt;
&lt;br /&gt;
==Cookie domain==&lt;br /&gt;
&lt;br /&gt;
This can be used to send session cookies to higher domains instead of just the server domain. This may be useful for some SSO solutions. Invalid values are ignored automatically.&lt;br /&gt;
&lt;br /&gt;
==Session drivers==&lt;br /&gt;
User sessions may be stored in different backends. Session drivers can be configured only in config.php file - see examples in config-dist.php file.&lt;br /&gt;
&lt;br /&gt;
===Memcached===&lt;br /&gt;
Memcached session driver is the fastest driver, it requires external memcached server and memcached PHP extension. Server cluster nodes must use shared session storage.&lt;br /&gt;
&lt;br /&gt;
Configuration options in config.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;session_handler_class = &#039;\core\session\memcached&#039;;&lt;br /&gt;
$CFG-&amp;gt;session_memcached_save_path = &#039;127.0.0.1:11211&#039;;&lt;br /&gt;
$CFG-&amp;gt;session_memcached_prefix = &#039;memc.sess.key.&#039;;&lt;br /&gt;
$CFG-&amp;gt;session_memcached_acquire_lock_timeout = 120;&lt;br /&gt;
$CFG-&amp;gt;session_memcached_lock_expire = 7200;       // Ignored if memcached extension &amp;lt;= 2.1.0&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
* Make sure the memcached server has enough memory.&lt;br /&gt;
* Use different prefix when storing sessions from multiple Moodles in one server.&lt;br /&gt;
* If memcached extension &amp;lt;= 2.1.0 the locking works differently from other drivers, the lock is expired/released at the end of timeout - see MDL-42485.&lt;br /&gt;
* Unlike the caching infrastructure there is currently no driver for memcache, only memcached.&lt;br /&gt;
&lt;br /&gt;
===Files===&lt;br /&gt;
This driver is used by default in new installation.&lt;br /&gt;
&lt;br /&gt;
Configuration options in config.php:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;session_handler_class = &#039;\core\session\file&#039;;&lt;br /&gt;
$CFG-&amp;gt;session_file_save_path = $CFG-&amp;gt;dataroot.&#039;/sessions&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
* File based sessions require file system that supports file locking.&lt;br /&gt;
&lt;br /&gt;
===Database===&lt;br /&gt;
This type of driver was used by default in Moodle 2.0-2.5&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;session_handler_class = &#039;\core\session\database&#039;;&lt;br /&gt;
$CFG-&amp;gt;session_database_acquire_lock_timeout = 120;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
* DB sessions are not compatible with MyISAM database engine.&lt;br /&gt;
* If you are using MySQL/MariaDB make sure that \&#039;max_allowed_packet\&#039; in my.cnf (or my.ini) is at least 4M.&lt;br /&gt;
* The performance is relatively low, it is not recommended for large sites.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
*[[Sessions FAQ]]&lt;br /&gt;
&lt;br /&gt;
[[cs:admin/setting/sessionhandling]]&lt;br /&gt;
[[ja:セッションハンドリング]]&lt;br /&gt;
[[de:Sitzungsinformationen]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Experimental_settings&amp;diff=107846</id>
		<title>Experimental settings</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Experimental_settings&amp;diff=107846"/>
		<updated>2013-11-15T12:23:43Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Developer tools}}&lt;br /&gt;
An administrator can enable certain experimental features in &#039;&#039;Settings &amp;gt; Site administration &amp;gt; Development &amp;gt; Experimental &amp;gt; Experimental settings&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
An experimental feature is defined as a feature which require additional testing and bug-fixing.&lt;br /&gt;
&lt;br /&gt;
*[[Safe exam browser]]&lt;br /&gt;
*Group members only - adding the &#039;[[Available for group members only]]&#039; checkbox to the activity settings page&lt;br /&gt;
*[[CSS optimiser]] (new in Moodle 2.3) &lt;br /&gt;
*Drag and drop upload of text/links (see MDL-22504 for further details)&lt;br /&gt;
* New backup format (supports large backups). May become default in a future Moodle version. See [[Backup and restore FAQ#Using the new backup format (experimental)]].&lt;br /&gt;
&lt;br /&gt;
[[es:Configuraciones experimentales]]&lt;br /&gt;
[[eu:Esperimentala]]&lt;br /&gt;
[[fr:Expérimental]]&lt;br /&gt;
[[ja:実験用]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Backup_and_restore_FAQ&amp;diff=107845</id>
		<title>Backup and restore FAQ</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Backup_and_restore_FAQ&amp;diff=107845"/>
		<updated>2013-11-15T12:20:43Z</updated>

		<summary type="html">&lt;p&gt;Quen: Added information about using the new backup format for large backups&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Backup}}&lt;br /&gt;
==How do I backup a course?==&lt;br /&gt;
&lt;br /&gt;
See [[Course backup]] and [[Automated backup setup]]. &lt;br /&gt;
&lt;br /&gt;
==How do I restore a course?==&lt;br /&gt;
&lt;br /&gt;
See [[Course restore]].&lt;br /&gt;
&lt;br /&gt;
==How do I backup my site?==&lt;br /&gt;
&lt;br /&gt;
See [[Site backup]].&lt;br /&gt;
&lt;br /&gt;
==What are the pros and cons of course versus site backups?==&lt;br /&gt;
&lt;br /&gt;
[[Site backup|Site backups]] are recommended in order to have all data saved with the best confidence and the shortest recovery time.&lt;br /&gt;
&lt;br /&gt;
For a site administrator, [[Automated course backup|automated course backups]] are more expensive in terms of time, CPU usage and storage. The recovery time to have a site running again takes longer than a site backup. However, teachers and site administrators might find a course backups as a way to create a &amp;quot;fresh&amp;quot; copy of a course that can be re-used (in older versions of Moodle, in newer versions see [[Import course data]]) or as a method to distribute a course(s) to other Moodle sites.&lt;br /&gt;
&lt;br /&gt;
==Why is my automated course backup much smaller in size than my manual course backup?==&lt;br /&gt;
&lt;br /&gt;
This is an intentional design decision. Because of the way files are stored in Moodle 2.x, there is no need to include the files in the backup if you are planning to restore them to the same Moodle site. Leaving them out saves huge amounts of disk space and makes the backup procedure much faster. &lt;br /&gt;
&lt;br /&gt;
==What data is not contained in course backups?==&lt;br /&gt;
&lt;br /&gt;
By selecting all the options when setting up the backup you can include almost all the data in the course. However you should be aware of the fact that some things are not backed up:&lt;br /&gt;
* Quiz questions are only backed up if at least one question from their category has been added to a quiz.&lt;br /&gt;
* Scales are only backed up if they are used by at least one activity.&lt;br /&gt;
* Users&#039; passwords are not backed up when the &amp;quot;Include enrolled users&amp;quot; option is selected.&lt;br /&gt;
&lt;br /&gt;
==The process ends with: &amp;quot;Error: An error occurred deleting old backup data&amp;quot;. What should I do?==&lt;br /&gt;
&lt;br /&gt;
This part of the backup (or restore) procedure tries to delete old info, used in previous executions, performing the following tasks:&lt;br /&gt;
&lt;br /&gt;
# Delete old records from &amp;quot;backup_ids&amp;quot; table: Check the table exists, repair it and try again.&lt;br /&gt;
# Delete old records from &amp;quot;backup_files&amp;quot; table: Check the table exists, repair it and try again.&lt;br /&gt;
# Delete old files from &amp;quot;moodledata/temp/backup&amp;quot;: Delete the dir completely and try again.&lt;br /&gt;
&lt;br /&gt;
[[Image:BackupProblem.gif|thumb|Backup error message]]For points 1 &amp;amp; 2, there are various ways of repairing tables, including using MySQL Admin.&lt;br /&gt;
For point 3 see below:&lt;br /&gt;
&lt;br /&gt;
The error message states that the &amp;quot;directory not empty&amp;quot; and gives the path to that directory. If you go there with an FTP program you can see what is there and clean up. It could be just some empty subfolders that were leftover. Deleting these has been able to help. One can also delete the dir &amp;quot;moodledata/temp/backup&amp;quot; completely. That can take a bit longer but may solve several problems at once.&lt;br /&gt;
&lt;br /&gt;
==The process ends with: &amp;quot;XML error: not well-formed (invalid token) at line YYYY&amp;quot;. What can I do?==&lt;br /&gt;
&lt;br /&gt;
This problem can appear at any point in the restore process. It&#039;s caused when the XML parser detects something incorrect in the backup file that prevent correct operation. Usually, it&#039;s caused by some &amp;quot;illegal&amp;quot; characters added in the original course due to some copy/paste of text containing them (control characters, or invalid sequences...).&lt;br /&gt;
&lt;br /&gt;
The best method to handle this issue is:&lt;br /&gt;
&lt;br /&gt;
* Unzip the problematic backup file under one empty folder.&lt;br /&gt;
&lt;br /&gt;
* Open the moodle.xml with Firefox. It will show you where (exact char) the problem is happening.&lt;br /&gt;
&lt;br /&gt;
* Edit the moodle.xml file with some UTF8-compatible editor and delete such characters. Save changes.&lt;br /&gt;
&lt;br /&gt;
* Test the moodle.xml file again with Firefox until no error was displayed.&lt;br /&gt;
&lt;br /&gt;
* Zip everything again (all the folder contents but not the folder itself!).&lt;br /&gt;
&lt;br /&gt;
* Restore the course. It should work now.&lt;br /&gt;
&lt;br /&gt;
* Restore still not working? See the next question.&lt;br /&gt;
&lt;br /&gt;
Also, if possible, it&#039;s highly recommended to solve those problems in the original course too from Moodle itself. Once &amp;quot;repaired&amp;quot; there, problems will be out if you create new backup files in the future.&lt;br /&gt;
&lt;br /&gt;
==The process ends with: &amp;quot;moodle xml not found at root level of zip file&amp;quot;. What can I do?==&lt;br /&gt;
If you are restoring from a zip file backup make sure the moodle.xml file is at the root level. To ensure this:&lt;br /&gt;
#Unzip the backup file of the course (example: mycourse.zip)&lt;br /&gt;
#Once the file is unzipped, open the folder (example: mycourse).&lt;br /&gt;
#Select the folders within the mycourse folder AND the moodle.xml file and create a zip of those item (example: mycourse_new.zip)&lt;br /&gt;
#Upload the new zip file (example: mycourse_new.zip) and restore from that.&lt;br /&gt;
&lt;br /&gt;
If the backup file is guaranteed to be correct, check paths to external files (zip, unzip). Incorrect settings also lead to this error message (see the Using Moodle forum discussion [http://moodle.org/mod/forum/discuss.php?d=140355 moodle.xml not found in root...] and MDL-14812).&lt;br /&gt;
&lt;br /&gt;
==The process ends with: &amp;quot;An error occurred while copying the zip file...&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
This problem is most likely caused by a permissions issue in the destination directory. Backup files are copied to &amp;quot;XXX/backupdata&amp;quot; under your dataroot directory (where XXX is the id of the course being backed up).&lt;br /&gt;
&lt;br /&gt;
The problem could also be caused by a disk being full, though this is far less likely.&lt;br /&gt;
&lt;br /&gt;
To obtain precise information about what&#039;s happening, you can enable debug messages in &#039;&#039;Administration &amp;gt; Server &amp;gt; [[Debugging]]&#039;&#039; (select the maximum level - DEVELOPER) and/or check the web server error logs.&lt;br /&gt;
&lt;br /&gt;
==I Still get an XML error. How can I clean the borked XML file?==&lt;br /&gt;
&lt;br /&gt;
In some cases XML backup files may contain characters causing the restore process to abort, even after the steps described in the previous question. In such cases you may want to try the following:&lt;br /&gt;
&lt;br /&gt;
* Download the [http://repository.atlassian.com/atlassian-xml-cleaner/jars/atlassian-xml-cleaner-0.1.jar Atlassian XML Cleaner Utility] from the [http://confluence.atlassian.com/display/JIRA/Removing+invalid+characters+from+XML+backups JIRA Atlassian site].&lt;br /&gt;
&lt;br /&gt;
* Unzip the problematic Moodle backup file under one empty folder. Moodle will create the course file folders as long as the unclean moodle.xml file. Please unzip using the Moodle unzip feature.&lt;br /&gt;
&lt;br /&gt;
* Rename the unclean moodle.xml file to moodle-unclean.xml.&lt;br /&gt;
&lt;br /&gt;
* If you don&#039;t have access to your Moodle server&#039;s command prompt, using the Moodle zip feature, zip the moodle-unclean.xml file only, download the zip file locally and unzip it. It is very important to download the xml file in zipped format to avoid unwanted character encoding when transferring from an operating system to another.&lt;br /&gt;
&lt;br /&gt;
* Move the downloaded Atlassian XML Cleaner Utility in the same folder where is your moodle-unclean.xml file.&lt;br /&gt;
&lt;br /&gt;
* Issue the following command from the command prompt: &lt;br /&gt;
&lt;br /&gt;
 java -jar atlassian-xml-cleaner-0.1.jar moodle-unclean.xml &amp;gt; moodle.xml&lt;br /&gt;
&lt;br /&gt;
* If you launched the utility on your local computer, zip the just created (and hopefully cleaned) moodle.xml file and upload it in the same place from where you downloaded the moodle-unclean.xml file. Once uploaded, unzip it using the Moodle unzip feature.&lt;br /&gt;
&lt;br /&gt;
* Zip everything again (all the folder contents but the folder itself!).&lt;br /&gt;
&lt;br /&gt;
* Restore the course. It should work now.&lt;br /&gt;
&lt;br /&gt;
==What does &amp;quot;Some of your courses weren&#039;t saved!!&amp;quot; mean?==&lt;br /&gt;
&lt;br /&gt;
There are three possible causes of this problem:&lt;br /&gt;
# Error - this happens when the backup procedure has found an error and so hasn&#039;t finished the backup of a particular course. These are &amp;quot;controlled&amp;quot; errors and the scheduled backup continues with the next course.&lt;br /&gt;
# Unfinished - this happens when the backup procedure dies without knowing why. When the cron is next executed it detects that the last execution went wrong, and continues skipping the problematic course. A possible solution would be to raise the PHP/Apache limit in your installation (memory, time of execution...). By taking a look to your log tables you should be able to see if the &amp;quot;crash&amp;quot; is happening at exact time intervals (usually a problem with the max_execution_time php&#039;s variable), or if there is some exact point were all the courses are breaking.&lt;br /&gt;
# Skipped - this happens when a course is unavailable to students and has not been changed in the last month (31 days). This isn&#039;t an error situation - it&#039;s a feature, especially useful for sites with many unavailable old courses, saving process time.&lt;br /&gt;
&lt;br /&gt;
==Why are some courses being skipped?==&lt;br /&gt;
&lt;br /&gt;
Course backups automatically skip courses which are unavailable to students and have not been changed in the time period specified in &#039;Skip courses not modified&#039; in &#039;&#039;Settings &amp;gt; Site administration &amp;gt; &amp;gt; Courses &amp;gt; Backups &amp;gt; Automated backup setup&#039;&#039; (by default 30 days).&lt;br /&gt;
&lt;br /&gt;
==Why does restore stop, rather than completing?==&lt;br /&gt;
&lt;br /&gt;
Attempting to restore a course to an older version of Moodle than the one the course was backed up on can result in the restore process failing to complete. To ensure a successful restore, make sure that the version of Moodle you are restoring the course to is the same, or newer, than the one the course was backed up on.&lt;br /&gt;
&lt;br /&gt;
If it stop unexpectedly with no errors shown try again with [[Debugging]] switched on. Any errors you now see can help experts in the support forums diagnose your problem. You can also check the discussion links in the See also section below for further advice.&lt;br /&gt;
&lt;br /&gt;
==Restore stops with the message &amp;quot;Trying to restore user xxxx from backup file will cause conflict&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
[[Image:Moodle 2.0 Restore breaks.png|thumb|Error message in Moodle 2.0]]&lt;br /&gt;
&lt;br /&gt;
This message is displayed when:&lt;br /&gt;
&lt;br /&gt;
# The target site has a user xxxx (xxxx being the username)&lt;br /&gt;
# The backup archive being restored also contains a user xxxx (same username)&lt;br /&gt;
# After various comparisons, Moodle has determined that the target site user xxxx and the backup user xxxx aren&#039;t the same person.&lt;br /&gt;
&lt;br /&gt;
If 1, 2 and 3 are all true, the restore process stops in order to prevent the backup user xxxx&#039;s activities (forum posts, quiz attempts, assignment uploads, etc) from being associated with the target site user xxxx. &lt;br /&gt;
&lt;br /&gt;
These checks and behaviour were introduced in Moodle 1.9.x and continue being valid under 2.0. It&#039;s common for the user in question to be the &amp;quot;admin&amp;quot; user (which exists in practically all Moodle installations).&lt;br /&gt;
&lt;br /&gt;
There are two possible methods to make the xxxx users match (and avoid the conflict):&lt;br /&gt;
&lt;br /&gt;
a) Modify the backup archive &#039;&#039;&#039;users.xml&#039;&#039;&#039; file and make the &#039;&#039;email&#039;&#039; or &#039;&#039;firstaccess&#039;&#039; fields match the ones in target site.&amp;lt;br /&amp;gt;&lt;br /&gt;
b) Modify the &#039;&#039;&#039;target site&#039;&#039;&#039; and set the user &#039;&#039;email&#039;&#039; or &#039;&#039;firstaccess&#039;&#039; fields to match the ones in backup archive users.xml file.&lt;br /&gt;
&lt;br /&gt;
Method a) is recommended so the restore process will match both xxxx users and all activities in the backup file belonging to xxxx will be associated to the already existing target site user xxxx user.&lt;br /&gt;
&lt;br /&gt;
  &#039;&#039;&#039;NOTE:&#039;&#039;&#039; When using method a) be aware that the &#039;&#039;moodle-filename-backup.&#039;&#039;&#039;mbz&#039;&#039;&#039;&#039;&#039; is a zip file and can be renamed to &#039;&#039;moodle-filename-backup.&#039;&#039;&#039;zip&#039;&#039;&#039;&#039;&#039; and unzipped. &lt;br /&gt;
  When editing is complete, rezip and then rename using the original file name with the &amp;quot;*.mbz&amp;quot; extention.&lt;br /&gt;
&lt;br /&gt;
==Why are certain course links broken in a restored course?==&lt;br /&gt;
&lt;br /&gt;
Inter-activity links must be absolute (full) URLs e.g. &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;http://site.com/mod/resource/view.php?id=xxx&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; in order to be processed properly during backup and restore.&lt;br /&gt;
&lt;br /&gt;
Any relative URLs e.g. &amp;lt;code&amp;gt;/mod/resource/view.php?id=xxx&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;../resource/view.php?id=xxx&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;view.php?id=xxx&amp;lt;/code&amp;gt; will result in broken links when the course is restored.&lt;br /&gt;
&lt;br /&gt;
==I have a very large course, over 2GB, and the backup process stops.==&lt;br /&gt;
&lt;br /&gt;
Courses over 4GB cannot be backed up without turning on an experimental option. See &#039;New backup format&#039; below.&lt;br /&gt;
&lt;br /&gt;
Below that size, large courses can be restored in Moodle, but sometimes it needs a bit of tweaking to get it right. Moodle backup files are *.mbz fies and can be renamed to zip files. They can be unzipped, then edited, rezipped and restored. It does not matter if you are using a Linux or Windows or Mac server, a local host or anything else, the technique is the same. &lt;br /&gt;
&lt;br /&gt;
The editing comes in two different ways, one is the resources, activities, quizzes, images. video files and so on are listed, written and referred to in the moodle.xml file. You can find the starting point and the end point of each resouce that you can delete out of the xml file. &lt;br /&gt;
&lt;br /&gt;
The xml might look something like this:&lt;br /&gt;
  &amp;lt;file id=&amp;quot;111&amp;quot;&amp;gt;    &amp;lt;contenthash&amp;gt;b11ac9bc0cebee17acfd28d13b548331f76645bc&amp;lt;/contenthash&amp;gt;&lt;br /&gt;
    &amp;lt;contextid&amp;gt;21&amp;lt;/contextid&amp;gt;&lt;br /&gt;
    &amp;lt;component&amp;gt;mod_resource&amp;lt;/component&amp;gt;&lt;br /&gt;
    &amp;lt;filearea&amp;gt;content&amp;lt;/filearea&amp;gt;&lt;br /&gt;
    &amp;lt;itemid&amp;gt;0&amp;lt;/itemid&amp;gt;&lt;br /&gt;
    &amp;lt;filepath&amp;gt;/&amp;lt;/filepath&amp;gt;&lt;br /&gt;
    &amp;lt;filename&amp;gt;howtomakeatimemachine.flv&amp;lt;/filename&amp;gt;&lt;br /&gt;
    &amp;lt;userid&amp;gt;4&amp;lt;/userid&amp;gt;&lt;br /&gt;
    &amp;lt;filesize&amp;gt;1092320586557&amp;lt;/filesize&amp;gt;&lt;br /&gt;
    &amp;lt;mimetype&amp;gt;video/flv&amp;lt;/mimetype&amp;gt;&lt;br /&gt;
    &amp;lt;status&amp;gt;0&amp;lt;/status&amp;gt;&lt;br /&gt;
    &amp;lt;timecreated&amp;gt;12345432123&amp;lt;/timecreated&amp;gt;&lt;br /&gt;
    &amp;lt;timemodified&amp;gt;12345432123&amp;lt;/timemodified&amp;gt;&lt;br /&gt;
    &amp;lt;source&amp;gt;howtomakeatimemachine.flv&amp;lt;/source&amp;gt;&lt;br /&gt;
    &amp;lt;author&amp;gt;Fred Nurks&amp;lt;/author&amp;gt;&lt;br /&gt;
    &amp;lt;license&amp;gt;allrightsreserved&amp;lt;/license&amp;gt;&lt;br /&gt;
    &amp;lt;sortorder&amp;gt;0&amp;lt;/sortorder&amp;gt;&lt;br /&gt;
    &amp;lt;repositorytype&amp;gt;$@NULL@$&amp;lt;/repositorytype&amp;gt;&lt;br /&gt;
    &amp;lt;repositoryid&amp;gt;$@NULL@$&amp;lt;/repositoryid&amp;gt;&lt;br /&gt;
    &amp;lt;reference&amp;gt;$@NULL@$&amp;lt;/reference&amp;gt;&lt;br /&gt;
  &amp;lt;/file&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When editing, make sure all this is deleted, everything between the &amp;lt;file&amp;gt;&amp;lt;/file&amp;gt; tags.&lt;br /&gt;
&lt;br /&gt;
The second part of editing is locating the actual resouce if it is an image, a separate file or video then deleting it. Really large mbz files tend to have a lot of videos, often flv files, or uncompressed images, like tiffs. They can be found, and deleted easily, in the directory tree of the backup.&lt;br /&gt;
&lt;br /&gt;
You can then rezip the edited file, rename it to an mbz and, if you have edited it right, it should restore. You can use the original file to break down really large backups over and over into four or five smaller mbz files, as many as you like.&lt;br /&gt;
&lt;br /&gt;
It is recommended that you test the technique first on a smaller file, it is easier to follow and gets you used to xml structuring and so on. Say one course with a couple of pages, a number of different image types, a couple of videos will help you immensely.  &lt;br /&gt;
&lt;br /&gt;
You do not have to worry about permissions in Windows or Xos servers, or concern yourself with editing rights usually. However, you may be required to ensure you are the owner fo the files being edited.&lt;br /&gt;
  &lt;br /&gt;
  &#039;&#039;&#039;NOTE:&#039;&#039;&#039; Before re-zipping, check to make sure you have removed all references to the pages/files/resources you have deleted in the moodle-backup.xml file as well. here msay be none, but check anyway.&lt;br /&gt;
&lt;br /&gt;
== Using the new backup format (experimental) ==&lt;br /&gt;
&lt;br /&gt;
If you have large courses you may find it useful to turn on an experimental option introduced in Moodle 2.6, &#039;Enable new backup format&#039; (on the admin menu under Development/Experimental/Experimental settings).&lt;br /&gt;
&lt;br /&gt;
This option selects a different internal backup format. Without this option, you cannot back up courses larger than 4GB.&lt;br /&gt;
&lt;br /&gt;
You can change this option at any time. Whatever the option is set to, restore works for both the old and new format. The option only affects newly-created backups.&lt;br /&gt;
&lt;br /&gt;
When you use this option, backup files still have the .mbz extension and work the same way, but there are some differences:&lt;br /&gt;
&lt;br /&gt;
* There is no limit on total backup size. (We have tested with courses up to about 10GB.)&lt;br /&gt;
* Files may be slightly smaller.&lt;br /&gt;
* If you need to edit this type of backup file manually, you will need to rename the .mbz file to .tar.gz, instead of .zip. You may need to use different software to extract and recompress this type of file. We have tested using GNU tar on Windows and Linux.&lt;br /&gt;
&lt;br /&gt;
The new backup format is experimental, but is being used in some large sites and may be enabled as default in a future Moodle version. If you find problems with it, please report them in the Moodle tracker.&lt;br /&gt;
&lt;br /&gt;
==How can I extract original files from a Moodle backup file?==&lt;br /&gt;
If you really want to get original files from the backup file (an &amp;quot;.mbz&amp;quot; file) you downloaded (using the backup and restore feature), you can do so in much the same way as is suggested above. &lt;br /&gt;
&lt;br /&gt;
The backup file can actually be opened with any zip/unzip program you can download. Once you open the file, you need to extract:&lt;br /&gt;
&lt;br /&gt;
    The files.xml file.&lt;br /&gt;
    The files directory (folder).&lt;br /&gt;
&lt;br /&gt;
Next step would be to open the &amp;quot;files.xml&amp;quot; file in a text editor, and:&lt;br /&gt;
&lt;br /&gt;
    Search for the name of each file you want to get.&lt;br /&gt;
    Take note of the value of the corresponding contenthash tag.&lt;br /&gt;
    In the &amp;quot;files&amp;quot; folder you extracted, locate the file whose name is the same as the value of the contenthash and which will always be located in a folder whose name corresponds to the two first characters of the file name.&lt;br /&gt;
&lt;br /&gt;
For example, let&#039;s assume there is a &amp;quot;backup_courses-120730.mbz&amp;quot; file of which the &amp;quot;files.xml&amp;quot; file and the &amp;quot;files&amp;quot; folder have been extracted. There is a PDF file named &amp;quot;Leadership.pdf&amp;quot; that is required for another purpose.&lt;br /&gt;
&lt;br /&gt;
Open the files.xml file and:&lt;br /&gt;
&lt;br /&gt;
1. Search for the string &amp;quot;Leadership.pdf&amp;quot;, which in this case is found under the following &amp;amp;lt;file id...&amp;amp;gt; group tag:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;file id=&amp;quot;12345&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;contenthash&amp;amp;gt;fb6cf43a9b2d432403c70a2cb4c340dbb6225631&amp;amp;lt;/contenthash&amp;amp;gt;&lt;br /&gt;
                :&lt;br /&gt;
  &amp;amp;lt;filename&amp;amp;gt;Leadership.pdf&amp;amp;lt;/filename&amp;amp;gt;&lt;br /&gt;
                :&lt;br /&gt;
  &amp;amp;lt;license&amp;amp;gt;allrightsreserved&amp;amp;lt;/license&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;sortorder&amp;amp;gt;1&amp;amp;lt;/sortorder&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/file&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Take note of the corresponding contenthash value: fb6cf43a9b2d432403c70a2cb4c340dbb6225631.&lt;br /&gt;
&lt;br /&gt;
3. As the first two characters of the contenthash are &amp;quot;fb&amp;quot;, open the &amp;quot;fb&amp;quot; folder inside the &amp;quot;files&amp;quot; directory (which was previously extracted), and there is a file named &amp;quot;fb6cf43a9b...&amp;quot;. Rename that file as &amp;quot;Leadership.pdf&amp;quot;, and then move it to another location. Repeat this for all the files required, using the correct contenthash value of course.&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?f=128 Backup and Restore forum]&lt;br /&gt;
*[http://www.databasejournal.com/features/mysql/article.php/10897_3300511_2 databasejournal.com article on repairing database corruption in MySQL]&lt;br /&gt;
* [[Site backup]]&lt;br /&gt;
* [[Moodle migration]]&lt;br /&gt;
* [[Beginning Moodle 2.0 Administration]]&lt;br /&gt;
&lt;br /&gt;
Using Moodle forum discussions:&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=142720 Trying to restore user &#039;admin&#039; from backup file will cause conflict]&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=167471 Where is the Moodle 2.0 &amp;quot;Course Backup Filearea&amp;quot;?]&lt;br /&gt;
&lt;br /&gt;
[[Category:FAQ]]&lt;br /&gt;
&lt;br /&gt;
[[es:FAQ Backup]]&lt;br /&gt;
[[pl:Backup FAQ]]&lt;br /&gt;
[[fr:FAQ de sauvegarde]]&lt;br /&gt;
[[ja:バックアップFAQ]]&lt;br /&gt;
[[pt:FAQ sobre cópias de segurança]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Administration_via_command_line&amp;diff=106374</id>
		<title>Administration via command line</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Administration_via_command_line&amp;diff=106374"/>
		<updated>2013-08-16T10:53:33Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Installing Moodle}}&lt;br /&gt;
If you have shell access to your web server, you may find various CLI (command line interface) scripts useful during Moodle administration. Core admin CLI tools are located in the &amp;lt;code&amp;gt;admin/cli/*&amp;lt;/code&amp;gt; folder. Other plugins provide their CLI functionality via scripts in their own cli folder. For example, the enrol_db sync script is located in &amp;lt;code&amp;gt;enrol/db/cli/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To avoid problems with access control, you should run them as the owner of the web server process. It is especially important for CLI installation and upgrade as they create new files in moodledata directory and the web server has to have write access to them. In Linux distributions, the user that runs the web server is usually apache or wwrun or httpd or something similar. As a root, you will probably want to execute Moodle CLI scripts like this:&lt;br /&gt;
&lt;br /&gt;
    $ cd /path/to/your/moodle/dir&lt;br /&gt;
    $ sudo -u apache /usr/bin/php admin/cli/somescript.php --params&lt;br /&gt;
&lt;br /&gt;
Most of the scripts accept common --help (or -h) parameter to display the full usage information, for example:&lt;br /&gt;
&lt;br /&gt;
    $ sudo -u apache /usr/bin/php admin/cli/install.php --help&lt;br /&gt;
&lt;br /&gt;
== Upgrading via command line ==&lt;br /&gt;
&lt;br /&gt;
Moodle can be upgraded from the command line. As with the installation script, there is either interactive or non-interactive mode of the upgrade. The script itself does not put the site into the maintenance mode, you have to do it on your own. Also, the script does not backup any data (if you read this page, you probably have some own scripts to backup your moodledata and the database, right?)&lt;br /&gt;
&lt;br /&gt;
    $ sudo -u apache /usr/bin/php admin/cli/upgrade.php&lt;br /&gt;
&lt;br /&gt;
Upgrading via command line is a very comfortable way of Moodle upgrade if you use Git checkout of the Moodle source code (see [[Git for Administrators]]). See the following procedure how to upgrade your site within several seconds to the most recent version while preserving your eventual local customizations tracked in git repository:&lt;br /&gt;
&lt;br /&gt;
    $ cd /var/www/sites/moodle/htdocs/&lt;br /&gt;
    $ sudo -u apache /usr/bin/php admin/cli/maintenance.php --enable&lt;br /&gt;
    $ git pull&lt;br /&gt;
    $ sudo -u apache /usr/bin/php admin/cli/upgrade.php&lt;br /&gt;
    $ sudo -u apache /usr/bin/php admin/cli/maintenance.php --disable&lt;br /&gt;
&lt;br /&gt;
== Installation via command line ==&lt;br /&gt;
&lt;br /&gt;
Since version 2.0, Moodle can be installed from the command line. There are two modes of installation. In interactive mode, the install script asks you for all data needed to properly set up new Moodle site. In non-interactive mode, you must provide all required data as the script parameters and then the new site is installed silently. The parameters can be passed in the interactive mode, too. The provided values are then used as the default values during the interactive session.&lt;br /&gt;
&lt;br /&gt;
    $ sudo -u apache /usr/bin/php admin/cli/install.php --lang=cs&lt;br /&gt;
&lt;br /&gt;
== Maintenance mode ==&lt;br /&gt;
&lt;br /&gt;
To switch your site into the maintenance mode via CLI, you can use&lt;br /&gt;
&lt;br /&gt;
    $ sudo -u apache /usr/bin/php admin/cli/maintenance.php --enable&lt;br /&gt;
&lt;br /&gt;
To turn the maintenance mode off, just execute the same script with --disable parameter.&lt;br /&gt;
&lt;br /&gt;
== Offline mode ==&lt;br /&gt;
&lt;br /&gt;
In some situations, you may want to switch your Moodle site into offline mode so that it is not accessible via the web but you can not stop the web server completely (typically because there are other web pages and applications running there). If a file called &amp;lt;code&amp;gt;climaintenance.html&amp;lt;/code&amp;gt; exists in the root folder of moodledata directory, Moodle will automatically display the contents of that file instead of any other page.&lt;br /&gt;
&lt;br /&gt;
    $ cd /var/www/sites/moodle/moodledata/&lt;br /&gt;
    $ echo &#039;&amp;amp;lt;h1&amp;amp;gt;Sorry, maintenance in progress&amp;amp;lt;/h1&amp;amp;gt;&#039; &amp;amp;gt; climaintenance.html&lt;br /&gt;
&lt;br /&gt;
You can prepare a nice formatted HTML page to inform your users about the server being down and keep in the moodledata directory under a name like &amp;lt;code&amp;gt;climaintenance.off&amp;lt;/code&amp;gt; and rename it to the &amp;lt;code&amp;gt;climaintenance.html&amp;lt;/code&amp;gt; if needed.&lt;br /&gt;
&lt;br /&gt;
== Custom site defaults ==&lt;br /&gt;
&lt;br /&gt;
During the install and upgrade via CLI, Moodle sets the administration variables to the default values. You can use different defaults. See MDL-17850 for details. Shortly, all you need to do is to add a file &amp;lt;code&amp;gt;local/defaults.php&amp;lt;/code&amp;gt; into your Moodle installation. The format of the file is like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$defaults[&#039;pluginname&#039;][&#039;settingname&#039;] = &#039;settingvalue&#039;; // for plugins&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;settingname&#039;] = &#039;settingvalue&#039;;     // for core settings&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These defaults are used during install, upgrade and are also displayed as defaults at the Site administration pages.&lt;br /&gt;
&lt;br /&gt;
== Reset user password ==&lt;br /&gt;
&lt;br /&gt;
If you happen to forget your admin password (or you want to set a password for any other user of your Moodle system), you can use reset_password.php script. The script sets the correctly salted password for the given user.&lt;br /&gt;
&lt;br /&gt;
    $ sudo -u apache /usr/bin/php admin/cli/reset_password.php&lt;br /&gt;
&lt;br /&gt;
== MySQL storage engine conversion ==&lt;br /&gt;
&lt;br /&gt;
If you run your Moodle site with MySQL database backend and use the default MyISAM as the storage engine for your tables, you may want to convert them to use some more reliable engine like InnoDB (actually, you should want to switch to PostgreSQL ;-) anyway).&lt;br /&gt;
&lt;br /&gt;
    $ sudo -u apache /usr/bin/php admin/cli/mysql_engine.php --engine=InnoDB&lt;br /&gt;
&lt;br /&gt;
== Running cron via command line ==&lt;br /&gt;
&lt;br /&gt;
In versions 1.x, you could execute admin/cron.php either from command line or via the web. Since Moodle 2.0, only admin/cli/cron.php script can be run via command line.&lt;br /&gt;
&lt;br /&gt;
==Database transfer==&lt;br /&gt;
&lt;br /&gt;
A command line script for [[Database transfer]] may be found in &#039;&#039;admin/tool/dbtransfer/cli/migrate.php&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==Update user pictures via command line==&lt;br /&gt;
&lt;br /&gt;
   http://moodle.org/pluginfile.php/143/mod_forum/attachment/926929/updatepics.php &lt;br /&gt;
Tested for 2.3.1+&lt;br /&gt;
&lt;br /&gt;
Place this file in /admin/cli/updatepics.php&lt;br /&gt;
&lt;br /&gt;
How to use:&lt;br /&gt;
Run with &amp;quot;dir&amp;quot; option to specify which directory contains the files.&lt;br /&gt;
&lt;br /&gt;
   .../admin/cli/updatepics.php --dir=PATH_TO_DIR&lt;br /&gt;
&lt;br /&gt;
* File names must be identical to usernames&lt;br /&gt;
&lt;br /&gt;
==Manage MOD&#039;s,Blocks via command line==&lt;br /&gt;
&lt;br /&gt;
Here are two scripts to help you manage your plugins.&lt;br /&gt;
&lt;br /&gt;
   http://tracker.moodle.org/browse/MDL-35736&lt;br /&gt;
&lt;br /&gt;
How to use &amp;quot;manage_blocks.php&amp;quot;:&lt;br /&gt;
place in /admin/cli&lt;br /&gt;
&lt;br /&gt;
hide:&lt;br /&gt;
../admin/cli/manage_blocks.php --name=tag_youtube --hide&lt;br /&gt;
&lt;br /&gt;
show:&lt;br /&gt;
../admin/cli/manage_blocks.php --name=tag_youtube --show&lt;br /&gt;
&lt;br /&gt;
delete:&lt;br /&gt;
../admin/cli/manage_blocks.php --name=tag_youtube --delete&lt;br /&gt;
&lt;br /&gt;
protect:&lt;br /&gt;
../admin/cli/manage_blocks.php --name=tag_youtube --protect&lt;br /&gt;
&lt;br /&gt;
unprotect:&lt;br /&gt;
../admin/cli/manage_blocks.php --name=tag_youtube --unprotect&lt;br /&gt;
&lt;br /&gt;
(obviously - replace &amp;quot;tag_youtube&amp;quot; with the blockthat you want to remove...)&lt;br /&gt;
&lt;br /&gt;
How to use &amp;quot;manage_mods.php&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
hide:&lt;br /&gt;
../admin/cli/manage_blocks.php --name=game --hide&lt;br /&gt;
&lt;br /&gt;
show:&lt;br /&gt;
../admin/cli/manage_blocks.php --name=game --show&lt;br /&gt;
&lt;br /&gt;
delete:&lt;br /&gt;
../admin/cli/manage_blocks.php --name=game --delete&lt;br /&gt;
&lt;br /&gt;
*Important!&lt;br /&gt;
this script does not delete the relevant folder in /moodle/mod or /moodle/blocks&lt;br /&gt;
&lt;br /&gt;
you have to delete it yourself !&lt;br /&gt;
&lt;br /&gt;
==Update language pack via CLI==&lt;br /&gt;
&lt;br /&gt;
  http://tracker.moodle.org/browse/MDL-35735&lt;br /&gt;
&lt;br /&gt;
Place attached file in /admin/cli&lt;br /&gt;
and simply run it, without any command-line arguments :)&lt;br /&gt;
&lt;br /&gt;
==ReSort course list via CLI==&lt;br /&gt;
&lt;br /&gt;
  http://tracker.moodle.org/browse/MDL-36237&lt;br /&gt;
&lt;br /&gt;
Place attached file in /admin/cli&lt;br /&gt;
and simply run it, without any command-line arguments :)&lt;br /&gt;
&lt;br /&gt;
I import courses every night from an external system, and run this script afterwards&lt;br /&gt;
&lt;br /&gt;
==Purge caches via CLI==&lt;br /&gt;
&lt;br /&gt;
You can purge caches using this script:&lt;br /&gt;
&lt;br /&gt;
  php admin/cli/purge_caches.php&lt;br /&gt;
&lt;br /&gt;
[[fr:Administration en ligne de commande]]&lt;br /&gt;
[[de:Administration über Kommandozeile]]&lt;br /&gt;
[[ja:コマンドライン経由の管理]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Caching&amp;diff=105322</id>
		<title>Caching</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Caching&amp;diff=105322"/>
		<updated>2013-05-24T09:26:25Z</updated>

		<summary type="html">&lt;p&gt;Quen: Added section about cache lock instances&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Performance}}&lt;br /&gt;
&lt;br /&gt;
A cache is a collection of processed data that is kept on hand and re-used in order to avoid costly repeated database queries.&lt;br /&gt;
&lt;br /&gt;
Moodle 2.4 saw the implementation of MUC, the Moodle Universal Cache.   This new system allows certain functions of Moodle (eg string fetching) take advantage of different installed cache services (eg files, ram, memcached).&lt;br /&gt;
&lt;br /&gt;
In future versions of Moodle we will continue expanding the number of Moodle functions that use MUC, which will continue improving performance, but you can already start using it to improve your site.&lt;br /&gt;
&lt;br /&gt;
==General approach to performance testing==&lt;br /&gt;
&lt;br /&gt;
Here is the general strategy you should be taking:&lt;br /&gt;
&lt;br /&gt;
# Build a test environment that is as close to your real production instance as possible (eg hardware, software, networking, etc)&lt;br /&gt;
# Make sure to remove as many uncontrolled variables as you can from this environment (eg other services)&lt;br /&gt;
# Use a tool to place a realistic, but simulated and repeatable load upon you server. (eg jmeter or selenium).&lt;br /&gt;
# Decide on a way to measure performance of the server by capturing data (ram, load, time taken, etc)&lt;br /&gt;
# Run your load and measure a baseline performance result.&lt;br /&gt;
# Change one variable at a time, and re-run the load to see if performance gets better or worse.  Repeat as necessary.&lt;br /&gt;
# When you discover settings that result in a consistent performance improvement, apply to your production site.&lt;br /&gt;
&lt;br /&gt;
==How to use the caching settings==&lt;br /&gt;
&lt;br /&gt;
Since Moodle 2.4, Moodle has provided a caching plugin framework to give administrators the ability to control where Moodle stores cached data. For most Moodle sites the default configuration should be sufficient and it is not necessary to change the configuration. For larger Moodle sites with multiple servers, administrators may wish to use memcached, mongodb or other systems to store cache data. The cache plugin screen provides administrators with the ability to configure what cache data is stored where. &lt;br /&gt;
&lt;br /&gt;
=== Types of cache ===&lt;br /&gt;
&lt;br /&gt;
Moodle uses three types of cache to store cached data:&lt;br /&gt;
* Request cache - The request cache is available for the duration of every page request. It is not shared between users and is used and cleared on every Moodle request.&lt;br /&gt;
* Session cache - The session cache is available through a users session in Moodle.  It is not shared between users, but persists for a single user throughout their session (i.e. from when they logon til when they log off)&lt;br /&gt;
* Application cache - The application cache is a shared cache which is available for every request. It can be shared between users and the cached data can be kept indefinitely if required.&lt;br /&gt;
&lt;br /&gt;
==== Cache types and multiple-server systems ====&lt;br /&gt;
&lt;br /&gt;
If you have a system with multiple front-end web servers, the application cache must be shared between the servers. In other words, you cannot use fast local storage for the application cache, but must use shared storage or some other form of shared cache such as a shared memcache.&lt;br /&gt;
&lt;br /&gt;
The same applies to session cache, unless you use a &#039;sticky sessions&#039; mechanism to ensure that within a session, users always access the same front-end server.&lt;br /&gt;
&lt;br /&gt;
===Installed cache stores===&lt;br /&gt;
&lt;br /&gt;
This section of the administrator screen displays cache plugins which are installed on the system. It lists what the capabilities of each plugin, what type of cache they provide and provides allows a cache store to be added to the system.&lt;br /&gt;
&lt;br /&gt;
===Configured store instances===&lt;br /&gt;
 &lt;br /&gt;
This section of the administrator screen displays cache stores which have been added to the system. It gives the ability to change the cache configuration and purge the cached data.&lt;br /&gt;
&lt;br /&gt;
===Cache lock instances===&lt;br /&gt;
&lt;br /&gt;
Moodle supports different mechanisms for &#039;locking&#039; access to the various cache stores. At present there is only one option and it is not used, so it can safely be ignored.&lt;br /&gt;
&lt;br /&gt;
===Known cache definitions===&lt;br /&gt;
&lt;br /&gt;
Known cache definitions displays the caches which are in use by Moodle. Each item is an area of Moodle which is using caching. It gives the administrator the ability to configure an individual area of Moodle to use a different cache backend. For example, an administrator of a Moodle cluster may choose to make language string definitions be cached on a dedicated memcached server by using the memcached cache backend.&lt;br /&gt;
&lt;br /&gt;
==Stores used when no mapping is present==&lt;br /&gt;
&lt;br /&gt;
This section displays the default cache stores which should be used by Moodle for each type of Moodle cache. If a mapping for a cache definition does not exist then this default store will be used instead.&lt;br /&gt;
&lt;br /&gt;
==Other performance testing==&lt;br /&gt;
&lt;br /&gt;
Two links that might be useful to anyone considering testing performance on their own servers:&lt;br /&gt;
&lt;br /&gt;
* [http://www.iteachwithmoodle.com/2012/10/12/moodle-performance-testing-how-much-more-horsepower-do-each-new-versions-of-moodle-require/ Moodle performance testing: how much more horsepower do each new versions of Moodle require?]&lt;br /&gt;
* [http://www.iteachwithmoodle.com/2012/10/11/how-to-stress-test-your-moodle-server-using-loadstorm/ How to load test your Moodle server using Loadstorm]&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [https://moodle.org/mod/forum/discuss.php?d=217195 MUC is here, now what?] forum discussion&lt;br /&gt;
&lt;br /&gt;
Developer documentation:&lt;br /&gt;
* [[:dev:The Moodle Universal Cache (MUC)]]&lt;br /&gt;
* [[:dev:Cache API]]&lt;br /&gt;
* [[:dev:Cache API - Quick reference]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[de:Caching]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=error/repository/instancenotsaved&amp;diff=101157</id>
		<title>error/repository/instancenotsaved</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=error/repository/instancenotsaved&amp;diff=101157"/>
		<updated>2012-10-08T11:58:08Z</updated>

		<summary type="html">&lt;p&gt;Quen: Created page with &amp;quot;This message (which doesn&amp;#039;t seem to have a translation) occurs if there is a problem when creating a repository.  Depending on the repository type, there might be various possibl...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This message (which doesn&#039;t seem to have a translation) occurs if there is a problem when creating a repository.&lt;br /&gt;
&lt;br /&gt;
Depending on the repository type, there might be various possible causes for this error. I am not aware of any particular way to cause this error in a standard Moodle install.&lt;br /&gt;
&lt;br /&gt;
At a technical level, this would occur if a repository plugin returns false from its &#039;create&#039; function. If the error occurs inappropriately while developing a new repository plugin, this is the probable cause.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Media_embedding&amp;diff=99940</id>
		<title>Media embedding</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Media_embedding&amp;diff=99940"/>
		<updated>2012-08-10T10:14:12Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Working with media}}&lt;br /&gt;
==Embedding audio and video==&lt;br /&gt;
[[File:mp3 player.png|thumb|MP3 player]]&lt;br /&gt;
Audio and video may be embedded in a course in the following ways:&lt;br /&gt;
&lt;br /&gt;
* As a [[File module settings|file resource]] &lt;br /&gt;
* As a [[URL module settings|URL resource]] with embed as the display option&lt;br /&gt;
* In a [[Lesson settings|lesson popup]]&lt;br /&gt;
* In any text area using the Moodle media button in the [[Text editor|text editor]] or simply by typing the URL of media file&lt;br /&gt;
&lt;br /&gt;
The media file link is then replaced with an appropriate multimedia player which can play the resource.&lt;br /&gt;
&lt;br /&gt;
==Enabling media embedding==&lt;br /&gt;
&lt;br /&gt;
To enable the embedding of media files, an admin must enable&lt;br /&gt;
&lt;br /&gt;
# Appropriate media players (see below)&lt;br /&gt;
# The [[Multimedia plugins filter]] - unless you only want to allow embedding in File, URL and Lesson modules.&lt;br /&gt;
&lt;br /&gt;
==Site administration settings==&lt;br /&gt;
{{New features}}An administrator can enable selected media players in &#039;&#039;Settings &amp;gt; Site administration &amp;gt; Appearance &amp;gt; Media embedding&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===Available players===&lt;br /&gt;
* YouTube (displays videos hosted on youtube)&lt;br /&gt;
* Vimeo (displays videos hosted on vimeo)&lt;br /&gt;
* .mp3 - MPEG Audio Stream, Layer III&lt;br /&gt;
* .flv - Flash video&lt;br /&gt;
* .f4v - Flash video&lt;br /&gt;
* .swf - Macromedia Flash animation File (Adobe, Inc.) &lt;br /&gt;
:&#039;&#039;&#039;Note:&#039;&#039;&#039; Only used in trusted texts as it has potential security issues&lt;br /&gt;
* .ogg - HTML 5 audio&lt;br /&gt;
* .acc - HTML 5 audio&lt;br /&gt;
* .mp3 - HTML 5 audio&lt;br /&gt;
* .webm - HTML 5 video&lt;br /&gt;
* .m4v - HTML 5 video&lt;br /&gt;
* .ogv  - HTML 5 video&lt;br /&gt;
&lt;br /&gt;
===Legacy media players===&lt;br /&gt;
The following legacy media player formats are also available but not recommended for general usage:&lt;br /&gt;
* .mov - QuickTime player (requires QuickTime player or [http://en.wikipedia.org/wiki/Codec codec])&lt;br /&gt;
* .mp4 - QuickTime player (requires QuickTime player or codec)&lt;br /&gt;
* .m4a - QuickTime player (requires QuickTime player or codec)&lt;br /&gt;
* .mpg - MPEG animation - QuickTime player (requires QuickTime player or codec)&lt;br /&gt;
* .wmv - Windows media player (Microsoft)-not guaranteed to work in browsers other than IE and non-Windows systems&lt;br /&gt;
* .avi - Windows media player - not guaranteed to work in browsers other than IE and non-Windows systems&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[:dev:Media embedding]] developer documentation including information on how to change the size of the media player&lt;br /&gt;
&lt;br /&gt;
[[Category:Site administration]]&lt;br /&gt;
&lt;br /&gt;
[[de:Einbetten von Medien]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=mod/forumng/editpost&amp;diff=96128</id>
		<title>mod/forumng/editpost</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=mod/forumng/editpost&amp;diff=96128"/>
		<updated>2012-02-28T15:46:28Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The ForumNG edit post form (used mainly for starting discussions) is quite similar to the equivalent post in the [[mod/forum/view|standard forum]].&lt;br /&gt;
&lt;br /&gt;
This article is a stub. We would welcome improvements to ForumNG documentation, so please edit and make it better.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=mod/forumng/editpost&amp;diff=96127</id>
		<title>mod/forumng/editpost</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=mod/forumng/editpost&amp;diff=96127"/>
		<updated>2012-02-28T15:40:54Z</updated>

		<summary type="html">&lt;p&gt;Quen: Created page with &amp;quot;The ForumNG edit post form (used mainly for starting discussions) is quite similar to the forum edit post form.  This article is a stub. We would welcome i...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The ForumNG edit post form (used mainly for starting discussions) is quite similar to the [[mod/forum/editpost|forum edit post form]].&lt;br /&gt;
&lt;br /&gt;
This article is a stub. We would welcome improvements to ForumNG documentation, so please edit and make it better.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=mod/forumng/discuss&amp;diff=96126</id>
		<title>mod/forumng/discuss</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=mod/forumng/discuss&amp;diff=96126"/>
		<updated>2012-02-28T15:40:18Z</updated>

		<summary type="html">&lt;p&gt;Quen: Created page with &amp;quot;The ForumNG discussion page is quite similar to the forum discussion page.  This article is a stub. We would welcome improvements to ForumNG documentation, ...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The ForumNG discussion page is quite similar to the [[mod/forum/discuss|forum discussion page]].&lt;br /&gt;
&lt;br /&gt;
This article is a stub. We would welcome improvements to ForumNG documentation, so please edit and make it better.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=mod/forumng/view&amp;diff=96125</id>
		<title>mod/forumng/view</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=mod/forumng/view&amp;diff=96125"/>
		<updated>2012-02-28T15:39:43Z</updated>

		<summary type="html">&lt;p&gt;Quen: Created page with &amp;quot;The ForumNG view page is quite similar to the forum view page.  This article is a stub. We would welcome improvements to ForumNG documentation, so please edit ...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The ForumNG view page is quite similar to the [[mod/forum/view|forum view page]].&lt;br /&gt;
&lt;br /&gt;
This article is a stub. We would welcome improvements to ForumNG documentation, so please edit and make it better.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Performance_FAQ&amp;diff=94293</id>
		<title>Performance FAQ</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Performance_FAQ&amp;diff=94293"/>
		<updated>2011-12-02T14:04:42Z</updated>

		<summary type="html">&lt;p&gt;Quen: added lots of opinion dressed up as fact about performance metrics&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Performance}}&lt;br /&gt;
==How do you define &amp;quot;concurrent users&amp;quot;?==&lt;br /&gt;
As has been repeatedly stressed in the [http://moodle.org/mod/forum/view.php?f=94 Hardware and performance] forum, the load on the server at a particular time depends on the number of concurrent users, not on the total number of users neither on the number of users logged-in. The term &amp;quot;concurrent users&amp;quot; is used to mean those users for whom the server is actively doing something. It may by processing a webpage written in PHP, querying the database or simply transferring a file. (see also Wikipedia [http://en.wikipedia.org/wiki/Concurrency_(computer_science) Concurrency])&lt;br /&gt;
&lt;br /&gt;
=== Why is &amp;quot;concurrent users&amp;quot; not a useful metric? ===&lt;br /&gt;
&lt;br /&gt;
When considering a new site or new hardware, this metric is not very useful because you have no idea how many &#039;concurrent users&#039; (a better term might be &#039;concurrent requests&#039;) you will have. &lt;br /&gt;
&lt;br /&gt;
For example, if you have 10,000 registered users and you estimate that, during times of peak load, about 1,000 of those users will be using the system, there is no easy way to obtain the number of &#039;concurrent users&#039;.&lt;br /&gt;
&lt;br /&gt;
* It depends on what the users are doing (if they are contributing to a forum they will make fairly frequent requests but with long pauses to write posts; if they are downloading a PDF they will only make a single request but it will take a long time; if they are doing a quiz they will make very frequent requests).&lt;br /&gt;
&lt;br /&gt;
* It also depends on system performance. If your system can serve a typical page in 0.1 seconds then if you have 10 people making a request within a second this is an average of 1 concurrent user. If your system takes 1 second to serve a page then the same usage pattern results in an average of 10 concurrent users.&lt;br /&gt;
&lt;br /&gt;
In addition, the number of concurrent users is not a very accurate measure of demand because a &amp;quot;concurrent user&amp;quot; may be downloading a large file, which takes time to transfer based on the speed of their network connection. While this does place a demand on the server, a server may well be able to cope easily with sending out 10 large PDF files to 10 concurrent users with slow network connections, whereas it might struggle if 10 users were continuously making separate PHP requests to a complex page such as quiz.&lt;br /&gt;
&lt;br /&gt;
=== What would be a better metric? ===&lt;br /&gt;
&lt;br /&gt;
Peak requests per second (either from web logs, mdl_log lines, or similar) is probably a better way to roughly estimate demand on the server - but it is still very difficult to work this out for a new server with unknown usage patterns.&lt;br /&gt;
&lt;br /&gt;
==How do I benchmark a Moodle-site?==&lt;br /&gt;
You can of course benchmark parts of the system separately: the hardware as seen by the operating system (eg. CPU, disk access), web server performance, database server performance, execution on PHP operations, etc. For further details see [[Performance#Obtain_a_baseline_benchmark]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
But there is no easy formula to deduce the maximum number of concurrent users from those results. There is a PHP-script, the [http://moodle.org/mod/forum/discuss.php?d=57028 Performance perspectives - a little script], circulating amoung the Moodle-community which calculates a ballpark figure. The current version is attached to [http://moodle.org/mod/forum/discuss.php?d=57028&amp;amp;parent=772267|the posting on 25. March 2011].&lt;br /&gt;
&lt;br /&gt;
Warning: Note that running this script on a production server may have unwanted side-effects. You are strongly adviced to run it on a test-site.&lt;br /&gt;
&lt;br /&gt;
==What are PHP-accelerators?==&lt;br /&gt;
See Wikipedia [http://en.wikipedia.org/wiki/PHP_accelerator PHP accelerator].&lt;br /&gt;
&lt;br /&gt;
Available software are documented under [[Performance#PHP_performance]]. &lt;br /&gt;
&lt;br /&gt;
You find some user suggestions here [http://moodle.org/mod/forum/discuss.php?d=168965 Update on PHP-accelerators].&lt;br /&gt;
&lt;br /&gt;
==How do I cluster Moodle?==&lt;br /&gt;
&lt;br /&gt;
See [[Performance#Scalability]]&lt;br /&gt;
&lt;br /&gt;
[[Moodle_Clusters]]&lt;br /&gt;
&lt;br /&gt;
==How do I replicate Moodle?==&lt;br /&gt;
&lt;br /&gt;
[[Mirroring_Moodle]]&lt;br /&gt;
&lt;br /&gt;
[http://moodle.org/mod/forum/discuss.php?d=173408&amp;amp;parent=760514  How to Replicate MOODLE ??!!]&lt;br /&gt;
&lt;br /&gt;
==My site is very slow, what should I do?==&lt;br /&gt;
&lt;br /&gt;
First find out &amp;quot;how slow&amp;quot;. (The theme-trick here).&lt;br /&gt;
&lt;br /&gt;
The next question is, whether the performance is normal or something malfunctions. There are many things which can malfunction:&lt;br /&gt;
* hardware&lt;br /&gt;
* crashed filesystems, specially network filesystems&lt;br /&gt;
* memory leaks or other crashes in the system&lt;br /&gt;
* bug in Moodle&lt;br /&gt;
* corrupted database&lt;br /&gt;
* networking issues (DNS, firewalls, ...)&lt;br /&gt;
&lt;br /&gt;
Or your performance could be &amp;quot;normal&amp;quot; under the given circumstances:&lt;br /&gt;
* Are you on a dedicated server or a shared (virtual) server?&lt;br /&gt;
* How much RAM, processing power do you have?&lt;br /&gt;
* What is the software stack you use? (Unix or Windows, Apache or IIS, MySQL, PostgreSQL or SQL-Server, ...?)&lt;br /&gt;
* how many concurrent users can you support&lt;br /&gt;
* what modules/activities you use? Check [[Performance#Performance of different Moodle modules]]&lt;br /&gt;
&lt;br /&gt;
[[Performance#Obtain a baseline benchmark]] and compare it with the published figures.&lt;br /&gt;
&lt;br /&gt;
==What are the requirements for N users?==&lt;br /&gt;
&lt;br /&gt;
[http://moodle.org/mod/forum/discuss.php?d=111847 A moodle setup for 10K simultaneous users]&lt;br /&gt;
&lt;br /&gt;
==How many users will my installation support?==&lt;br /&gt;
&lt;br /&gt;
This is another way of asking the same question as above. Please move up.&lt;br /&gt;
&lt;br /&gt;
==What is the best webserver?==&lt;br /&gt;
&lt;br /&gt;
==Should I go for 64 bit or is 32 bit OK?==&lt;br /&gt;
&lt;br /&gt;
==What hosting provider do you recommend?==&lt;br /&gt;
http://moodle.org/mod/forum/discuss.php?d=99405&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
Using Moodle forum discussions&lt;br /&gt;
* [http://moodle.org/mod/forum/search.php?notwords=Re:&amp;amp;id=5&amp;amp;subject=performance All performance related discussions]&lt;br /&gt;
* [http://moodle.org/mod/forum/search.php?notwords=Re:&amp;amp;id=5&amp;amp;forumid=33&amp;amp;&amp;amp;subject=performance Performance related discussions in the General Developer forum]&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=102978 1000 concurrent users]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=103040 Tool to estimate server&#039;s maximum concurrent users]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=144718 Handling Moodle database load - a solution!]&lt;br /&gt;
&lt;br /&gt;
[[Category:FAQ]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Performance_FAQ&amp;diff=94292</id>
		<title>Performance FAQ</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Performance_FAQ&amp;diff=94292"/>
		<updated>2011-12-02T14:01:19Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* How do you define &amp;quot;concurrent users&amp;quot;? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Performance}}&lt;br /&gt;
==How do you define &amp;quot;concurrent users&amp;quot;?==&lt;br /&gt;
As has been repeatedly stressed in the [http://moodle.org/mod/forum/view.php?f=94 Hardware and performance] forum, the load on the server at a particular time depends on the number of concurrent users, not on the total number of users neither on the number of users logged-in. The term &amp;quot;concurrent users&amp;quot; is used to mean those users for whom the server is actively doing something. It may by processing a webpage written in PHP, querying the database or simply transferring a file. (see also Wikipedia [http://en.wikipedia.org/wiki/Concurrency_(computer_science) Concurrency])&lt;br /&gt;
&lt;br /&gt;
=== Why is &amp;quot;concurrent users&amp;quot; not a useful metric? ===&lt;br /&gt;
&lt;br /&gt;
When considering a new site or new hardware, this metric is not very useful because you have no idea how many &#039;concurrent users&#039; (a better term might be &#039;concurrent requests&#039;) you will have. &lt;br /&gt;
&lt;br /&gt;
For example, if you have 10,000 registered users and you estimate that, during times of peak load, about 1,000 of those users will be using the system, there is no easy way to obtain the number of &#039;concurrent users&#039;.&lt;br /&gt;
&lt;br /&gt;
* It depends on what the users are doing (if they are contributing to a forum they will make fairly frequent requests but with long pauses to write posts; if they are downloading a PDF they will only make a single request but it will take a long time; if they are doing a quiz they will make very frequent requests).&lt;br /&gt;
&lt;br /&gt;
* It also depends on system performance. If your system can serve a typical page in 0.1 seconds then if you have 10 people making a request within a second this is an average of 1 concurrent user. If your system takes 1 second to serve a page then the same usage pattern results in an average of 10 concurrent users.&lt;br /&gt;
&lt;br /&gt;
In addition, the number of concurrent users is not a very accurate measure of demand because a &amp;quot;concurrent user&amp;quot; may be downloading a large file, which takes time to transfer based on the speed of their network connection. While this does place a demand on the server, the demand of sitting there for 30 seconds transferring a large PDF file to somebody with a slow network connection is much less than the demand of handling, say, 30 one-second PHP requests over the same timeframe.&lt;br /&gt;
&lt;br /&gt;
==How do I benchmark a Moodle-site?==&lt;br /&gt;
You can of course benchmark parts of the system separately: the hardware as seen by the operating system (eg. CPU, disk access), web server performance, database server performance, execution on PHP operations, etc. For further details see [[Performance#Obtain_a_baseline_benchmark]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
But there is no easy formula to deduce the maximum number of concurrent users from those results. There is a PHP-script, the [http://moodle.org/mod/forum/discuss.php?d=57028 Performance perspectives - a little script], circulating amoung the Moodle-community which calculates a ballpark figure. The current version is attached to [http://moodle.org/mod/forum/discuss.php?d=57028&amp;amp;parent=772267|the posting on 25. March 2011].&lt;br /&gt;
&lt;br /&gt;
Warning: Note that running this script on a production server may have unwanted side-effects. You are strongly adviced to run it on a test-site.&lt;br /&gt;
&lt;br /&gt;
==What are PHP-accelerators?==&lt;br /&gt;
See Wikipedia [http://en.wikipedia.org/wiki/PHP_accelerator PHP accelerator].&lt;br /&gt;
&lt;br /&gt;
Available software are documented under [[Performance#PHP_performance]]. &lt;br /&gt;
&lt;br /&gt;
You find some user suggestions here [http://moodle.org/mod/forum/discuss.php?d=168965 Update on PHP-accelerators].&lt;br /&gt;
&lt;br /&gt;
==How do I cluster Moodle?==&lt;br /&gt;
&lt;br /&gt;
See [[Performance#Scalability]]&lt;br /&gt;
&lt;br /&gt;
[[Moodle_Clusters]]&lt;br /&gt;
&lt;br /&gt;
==How do I replicate Moodle?==&lt;br /&gt;
&lt;br /&gt;
[[Mirroring_Moodle]]&lt;br /&gt;
&lt;br /&gt;
[http://moodle.org/mod/forum/discuss.php?d=173408&amp;amp;parent=760514  How to Replicate MOODLE ??!!]&lt;br /&gt;
&lt;br /&gt;
==My site is very slow, what should I do?==&lt;br /&gt;
&lt;br /&gt;
First find out &amp;quot;how slow&amp;quot;. (The theme-trick here).&lt;br /&gt;
&lt;br /&gt;
The next question is, whether the performance is normal or something malfunctions. There are many things which can malfunction:&lt;br /&gt;
* hardware&lt;br /&gt;
* crashed filesystems, specially network filesystems&lt;br /&gt;
* memory leaks or other crashes in the system&lt;br /&gt;
* bug in Moodle&lt;br /&gt;
* corrupted database&lt;br /&gt;
* networking issues (DNS, firewalls, ...)&lt;br /&gt;
&lt;br /&gt;
Or your performance could be &amp;quot;normal&amp;quot; under the given circumstances:&lt;br /&gt;
* Are you on a dedicated server or a shared (virtual) server?&lt;br /&gt;
* How much RAM, processing power do you have?&lt;br /&gt;
* What is the software stack you use? (Unix or Windows, Apache or IIS, MySQL, PostgreSQL or SQL-Server, ...?)&lt;br /&gt;
* how many concurrent users can you support&lt;br /&gt;
* what modules/activities you use? Check [[Performance#Performance of different Moodle modules]]&lt;br /&gt;
&lt;br /&gt;
[[Performance#Obtain a baseline benchmark]] and compare it with the published figures.&lt;br /&gt;
&lt;br /&gt;
==What are the requirements for N users?==&lt;br /&gt;
&lt;br /&gt;
[http://moodle.org/mod/forum/discuss.php?d=111847 A moodle setup for 10K simultaneous users]&lt;br /&gt;
&lt;br /&gt;
==How many users will my installation support?==&lt;br /&gt;
&lt;br /&gt;
This is another way of asking the same question as above. Please move up.&lt;br /&gt;
&lt;br /&gt;
==What is the best webserver?==&lt;br /&gt;
&lt;br /&gt;
==Should I go for 64 bit or is 32 bit OK?==&lt;br /&gt;
&lt;br /&gt;
==What hosting provider do you recommend?==&lt;br /&gt;
http://moodle.org/mod/forum/discuss.php?d=99405&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
Using Moodle forum discussions&lt;br /&gt;
* [http://moodle.org/mod/forum/search.php?notwords=Re:&amp;amp;id=5&amp;amp;subject=performance All performance related discussions]&lt;br /&gt;
* [http://moodle.org/mod/forum/search.php?notwords=Re:&amp;amp;id=5&amp;amp;forumid=33&amp;amp;&amp;amp;subject=performance Performance related discussions in the General Developer forum]&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=102978 1000 concurrent users]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=103040 Tool to estimate server&#039;s maximum concurrent users]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=144718 Handling Moodle database load - a solution!]&lt;br /&gt;
&lt;br /&gt;
[[Category:FAQ]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Commit_cheat_sheet&amp;diff=82756</id>
		<title>Development:Commit cheat sheet</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Commit_cheat_sheet&amp;diff=82756"/>
		<updated>2011-04-14T10:37:35Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;You can consider this page as a list to check before you submit a patch for inclusion into Moodle.&lt;br /&gt;
&lt;br /&gt;
== Split your work into a logical set of patches ==&lt;br /&gt;
&lt;br /&gt;
Keep in mind that your commits will be reviewed before they are accepted. If the patch does one clear thing and does it well, the review process is fun. Git allows you to prepare patches on your branch into a sequence of logical steps. For example, when changing some API, divide the change into two steps. In the first commit, change the API. In the following commit, change all places that use the API.&lt;br /&gt;
&lt;br /&gt;
== Provide clear commit messages ==&lt;br /&gt;
&lt;br /&gt;
Consider the commit message as an email for the developer who would explore the change in the future. We recommend to follow common Git guidelines for formatting. Include the MDL issue number and eventually the area/component at the beginning of the subject line.&lt;br /&gt;
&lt;br /&gt;
    MDL-xxxxx code area: short description of the patch&amp;lt;br /&amp;gt;&lt;br /&gt;
    The subject line is followed by an empty line and then a paragraph or two of&lt;br /&gt;
    a longer description follows. This longer description is useful for issues&lt;br /&gt;
    with longer history of comments in the linked MDL so that it summarizes the&lt;br /&gt;
    patch without the need to go through the whole discussion.&amp;lt;br /&amp;gt;&lt;br /&gt;
    Obviously, avoid messages like &amp;quot;as agreed in the chat&amp;quot; as they will become&lt;br /&gt;
    useless after a relatively short time.&lt;br /&gt;
&lt;br /&gt;
Most of Git tools are optimised for this format and they can display the log of commits best then.&lt;br /&gt;
&lt;br /&gt;
== Names in the commit message ==&lt;br /&gt;
&lt;br /&gt;
Retain the authorship of the patch. If the patch was submitted by someone else - for example a community member who published it in the tracker or in a forum - use the --author parameter. Make sure that your &#039;&#039;real&#039;&#039; name and contact email are recorded in patch. We use real names written in capital letters like &amp;quot;John Smith&amp;quot;. Please do not use names like &amp;lt;strike&amp;gt;&amp;quot;john smith&amp;quot;, &amp;quot;John S&amp;quot;, &amp;quot;johnny7887&amp;quot;&amp;lt;/strike&amp;gt;. If you use --author parameter, apply the same rules for the name of the author. See almost any Git tutorial on how to set your name and email in the global Git configuration.&lt;br /&gt;
&lt;br /&gt;
== Provide clear and operational instructions to test your patch ==&lt;br /&gt;
&lt;br /&gt;
In the PULL request, please describe how the change can be tested. Please avoid vague phrases like &amp;quot;Make sure there is no regression in the core&amp;quot; or &amp;quot;Test all places where XXX is used&amp;quot;. Also, try to avoid requiring resources that are really difficult to gather, if possible - as in &amp;quot;Use production data from a server with 100.000+ students&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
In most cases, the instructions for testers should go into the PULL request to make the tester&#039;s life easier. This means you will need to copy and paste them into every PULL request (if there are multiple PULL requests for different branches, or if your first PULL request gets rejected).&lt;br /&gt;
&lt;br /&gt;
If the instructions are elsewhere (for example they were provided by the reporter in the linked MDL as a part of steps to reproduce), you could inform testers in the PULL request where they can find them. However it is preferable to copy/paste the instructions into each PULL. &lt;br /&gt;
&lt;br /&gt;
It helps if you state your estimation of the testing difficulty so that testers can pick issues for them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Easy&#039;&#039; (average community member should be able to test it) - can be tested pretty easily via the web interface only at a public test site&lt;br /&gt;
* &#039;&#039;Moderate&#039;&#039; (knowledgeable administrator should be able to test it) - requires local installation, for example to test some 1.9 -&amp;gt; 2.0 upgrade steps or some non-standard environment (for example MNet features, specific platform etc)&lt;br /&gt;
* &#039;&#039;Hard&#039;&#039; (development skills are required to test it) - for example may require data hacking at SQL level to simulate data corruption or modifying the code to reproduce the problem&lt;br /&gt;
&lt;br /&gt;
Example of instructions for testers:&lt;br /&gt;
&lt;br /&gt;
    INSTRUCTIONS FOR TESTING (difficulty: easy, requires teacher access to a course)&amp;lt;br /&amp;gt;&lt;br /&gt;
    1. Log in as a teacher and go to a course (you can use some of yours or restore the attached .mbz backup)&lt;br /&gt;
    2. Turn editing mode on&lt;br /&gt;
    3. TEST: Make sure that the control icons appear next to the activity titles&lt;br /&gt;
    4. Turn editing mode off&lt;br /&gt;
    5. TEST: Make sure that the control icons are not displayed now&lt;br /&gt;
&lt;br /&gt;
== Introducing new strings ==&lt;br /&gt;
&lt;br /&gt;
Firstly, think twice and try to think in a non-English language. Any string you introduce is supposed to be translated by translators who usually do it for free in their own time. Do not waste their time by using get_string() for debugging messages that are likely to almost never appear. It is warmly recommended to let Helen review your strings before you submit them. This way we can keep the terminology and the style consistent. When introducing new strings, keep them alphabetically sorted. Using a self-descriptive names of the string identifier and the placeholder properties helps the translators to guess the context. Compare the following&lt;br /&gt;
&lt;br /&gt;
    $string[&#039;grade&#039;] = &#039;Grade {$a}&#039;;&lt;br /&gt;
&lt;br /&gt;
with&lt;br /&gt;
&lt;br /&gt;
    $string[&#039;maxgradevalue&#039;] = &#039;Grade {$a-&amp;gt;value}&#039;;&lt;br /&gt;
&lt;br /&gt;
In the first case, it is pretty difficult to guess whether the &amp;quot;Grade&amp;quot; in the string is a noun (as in &amp;quot;Grade 12/30&amp;quot;) or a verb (as in &amp;quot;Grade submission&amp;quot;). In many languages, the translation depends on it. The second case is more self-descriptive as it indicates that the placeholder will contain a value.&lt;br /&gt;
&lt;br /&gt;
See [[Development:Help strings]] if you are introducing a new help string.&lt;br /&gt;
&lt;br /&gt;
== Include AMOS script in the commit if needed ==&lt;br /&gt;
&lt;br /&gt;
If you change the identifier of a string or split a string into two forks, provide a script for AMOS in the commit message. Since Moodle 2.0, the translations are kept on separated branches again. The AMOS plugin at http://lang.moodle.org tracks the changes in string files and automatically records modifications, additions and removals of strings. Therefore strings can be re-worded freely on stable branches and should be removed from the master branch if they are not needed any more.&lt;br /&gt;
&lt;br /&gt;
If you change the identifier of the string (that is the key in the $string array), move the string from one file to another or you are introducing a new string as a copy of some current one, you should provide instructions for AMOS so that the action can be applied in all language packs. That will save valuable translators&#039; time. Instructions for AMOS are being put into the commit message of a commit that modifies the original English string files. The commit message containing such a script may look like this:&lt;br /&gt;
&lt;br /&gt;
    MDL-xxxxx code area: short description of the patch&amp;lt;br /&amp;gt;&lt;br /&gt;
    It is recommended to leave a blank line between the commit message and the&lt;br /&gt;
    script block.&amp;lt;br /&amp;gt;&lt;br /&gt;
    AMOS BEGIN&lt;br /&gt;
     MOV [configfoobar,core_admin],[foobar_desc,core_admin]&lt;br /&gt;
     CPY [submission,mod_assignment],[submission,mod_workshop]&lt;br /&gt;
    AMOS END&lt;br /&gt;
&lt;br /&gt;
See [[Development:Languages/AMOS#AMOS_script]] for more details of the syntax. See [http://git.moodle.org/gw?p=moodle.git&amp;amp;a=search&amp;amp;h=HEAD&amp;amp;st=commit&amp;amp;s=AMOS+BEGIN the log history] real examples of usage.&lt;br /&gt;
&lt;br /&gt;
== Main version changes ==&lt;br /&gt;
&lt;br /&gt;
If your commit requires a change to the main version number in version.php (and corresponding upgrade in lib/db/upgrade.php), you should increment that version number by .01, and let integrators deal with merge conflicts (for example if multiple people that week submit several .01 updates).&lt;br /&gt;
&lt;br /&gt;
(Note there may be policies about avoiding the type of changes which require version.php updates, especially in stable branches.)&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development_talk:Anonymous_Users&amp;diff=82755</id>
		<title>Development talk:Anonymous Users</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development_talk:Anonymous_Users&amp;diff=82755"/>
		<updated>2011-04-14T09:56:51Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Forum table changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Are you sure it should be a user_alias_course table? The other option would be to do user_alias_context, which would be more flexible. Not sure if you need the flexibility, but increasingly in Moodle things are being linked to contexts.&lt;br /&gt;
&lt;br /&gt;
Good point Tim. I played with the code and it actually is a lot easier than I imagined, mostly thanks to seeing the code you did for the filters. Overall I think it does give maximum fexibility so will approach it this way. --[[User:Shane Elliott|Shane Elliott]] 09:41, 1 May 2010 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Forum table changes ==&lt;br /&gt;
&lt;br /&gt;
Spec doesn&#039;t seem to mention the necessary changes to forum tables; forum_posts needs an &#039;anonymous&#039; field, at least.&lt;br /&gt;
&lt;br /&gt;
Also I didn&#039;t really understand how to implement support for this in a module. For instance I guess I could define FEATURE_ANONYMOUS in my module _supports function and then it would automatically add the option to the mod_form for me, but it doesn&#039;t say that.&lt;br /&gt;
&lt;br /&gt;
Then should the anonymous option for that module be stored in course_modules? (I see the desire to do it with context but there is no standard UI for editing things in different contexts and we can&#039;t support it across all modules without module changes so there is no point offering it at course level... or is there...)&lt;br /&gt;
&lt;br /&gt;
If it&#039;s not in course_modules then as we are going to need it in lots of places potentially, should it be cached in modinfo?&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t have permission to view anonymous, but you do have permission to view the &#039;forum posts&#039; tab on the user profile, then this tab needs to not include anonymous messages...&lt;br /&gt;
&lt;br /&gt;
I think this is probably a lot more difficult (to do properly) than what&#039;s already included in the spec. :(&lt;br /&gt;
&lt;br /&gt;
You could code it a bit more easily as a feature specific to forum, though.&lt;br /&gt;
&lt;br /&gt;
[[User:sam marshall|sam marshall]] 17:56, 14 April 2011 (WST)&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development_talk:Anonymous_Users&amp;diff=82754</id>
		<title>Development talk:Anonymous Users</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development_talk:Anonymous_Users&amp;diff=82754"/>
		<updated>2011-04-14T09:56:37Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Forum table changes */ new section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Are you sure it should be a user_alias_course table? The other option would be to do user_alias_context, which would be more flexible. Not sure if you need the flexibility, but increasingly in Moodle things are being linked to contexts.&lt;br /&gt;
&lt;br /&gt;
Good point Tim. I played with the code and it actually is a lot easier than I imagined, mostly thanks to seeing the code you did for the filters. Overall I think it does give maximum fexibility so will approach it this way. --[[User:Shane Elliott|Shane Elliott]] 09:41, 1 May 2010 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Forum table changes ==&lt;br /&gt;
&lt;br /&gt;
Spec doesn&#039;t seem to mention the necessary changes to forum tables; forum_posts needs an &#039;anonymous&#039; field, at least.&lt;br /&gt;
&lt;br /&gt;
Also I didn&#039;t really understand how to implement support for this in a module. For instance I guess I could define FEATURE_ANONYMOUS in my module _supports function and then it would automatically add the option to the mod_form for me, but it doesn&#039;t say that.&lt;br /&gt;
&lt;br /&gt;
Then should the anonymous option for that module be stored in course_modules? (I see the desire to do it with context but there is no standard UI for editing things in different contexts and we can&#039;t support it across all modules without module changes so there is no point offering it at course level... or is there...)&lt;br /&gt;
&lt;br /&gt;
If it&#039;s not in course_modules then as we are going to need it in lots of places potentially, should it be cached in modinfo?&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t have permission to view anonymous, but you do have permission to view the &#039;forum posts&#039; tab on the user profile, then this tab needs to not include anonymous messages...&lt;br /&gt;
&lt;br /&gt;
I think this is probably a lot more difficult (to do properly) than what&#039;s already included in the spec. :(&lt;br /&gt;
&lt;br /&gt;
You could code it a bit more easily as a feature specific to forum, though.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Commit_cheat_sheet&amp;diff=82616</id>
		<title>Development:Commit cheat sheet</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Commit_cheat_sheet&amp;diff=82616"/>
		<updated>2011-04-07T10:15:40Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Provide clear and operational instructions to test your patch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;You can consider this page as a list to check before you submit a patch for inclusion into Moodle.&lt;br /&gt;
&lt;br /&gt;
== Split your work into a logical set of patches ==&lt;br /&gt;
&lt;br /&gt;
Keep in mind that your commits will be reviewed before they are accepted. If the patch does one clear thing and does it well, the review process is fun. Git allows you to prepare patches on your branch into a sequence of logical steps. For example, when changing some API, divide the change into two steps. In the first commit, change the API. In the following commit, change all places that use the API.&lt;br /&gt;
&lt;br /&gt;
== Provide clear commit messages ==&lt;br /&gt;
&lt;br /&gt;
Consider the commit message as an email for the developer who would explore the change in the future. We recommend to follow common Git guidelines for formatting. Include the MDL issue number and eventually the area/component at the beginning of the subject line.&lt;br /&gt;
&lt;br /&gt;
    MDL-xxxxx code area: short description of the patch&amp;lt;br /&amp;gt;&lt;br /&gt;
    The subject line is followed by an empty line and then a paragraph or two of&lt;br /&gt;
    a longer description follows. This longer description is useful for issues&lt;br /&gt;
    with longer history of comments in the linked MDL so that it summarizes the&lt;br /&gt;
    patch without the need to go through the whole discussion.&amp;lt;br /&amp;gt;&lt;br /&gt;
    Obviously, avoid messages like &amp;quot;as agreed in the chat&amp;quot; as they will become&lt;br /&gt;
    useless after a relatively short time.&lt;br /&gt;
&lt;br /&gt;
Most of Git tools are optimised for this format and they can display the log of commits best then.&lt;br /&gt;
&lt;br /&gt;
== Names in the commit message ==&lt;br /&gt;
&lt;br /&gt;
Retain the authorship of the patch. If the patch was submitted by someone else - for example a community member who published it in the tracker or in a forum - use the --author parameter. Make sure that your &#039;&#039;real&#039;&#039; name and contact email are recorded in patch. We use real names written in capital letters like &amp;quot;John Smith&amp;quot;. Please do not use names like &amp;lt;strike&amp;gt;&amp;quot;john smith&amp;quot;, &amp;quot;John S&amp;quot;, &amp;quot;johnny7887&amp;quot;&amp;lt;/strike&amp;gt;. If you use --author parameter, apply the same rules for the name of the author. See almost any Git tutorial on how to set your name and email in the global Git configuration.&lt;br /&gt;
&lt;br /&gt;
== Provide clear and operational instructions to test your patch ==&lt;br /&gt;
&lt;br /&gt;
In the PULL request, please describe how the change can be tested. Please avoid vague phrases like &amp;quot;Make sure there is no regression in the core&amp;quot; or &amp;quot;Test all places where XXX is used&amp;quot;. Also, try to avoid requiring resources that are really difficult to gather, if possible - as in &amp;quot;Use production data from a server with 100.000+ students&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
In most cases, the instructions for testers should go into the PULL request to make the tester&#039;s life easier. This means you will need to copy and paste them into every PULL request (if there are multiple PULL requests for different branches, or if your first PULL request gets rejected).&lt;br /&gt;
&lt;br /&gt;
If the instructions are elsewhere (for example they were provided by the reporter in the linked MDL as a part of steps to reproduce), you could inform testers in the PULL request where they can find them. However it is preferable to copy/paste the instructions into each PULL. &lt;br /&gt;
&lt;br /&gt;
It helps if you state your estimation of the testing difficulty so that testers can pick issues for them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Easy&#039;&#039; (average community member should be able to test it) - can be tested pretty easily via the web interface only at a public test site&lt;br /&gt;
* &#039;&#039;Moderate&#039;&#039; (knowledgeable administrator should be able to test it) - requires local installation, for example to test some 1.9 -&amp;gt; 2.0 upgrade steps or some non-standard environment (for example MNet features, specific platform etc)&lt;br /&gt;
* &#039;&#039;Hard&#039;&#039; (development skills are required to test it) - for example may require data hacking at SQL level to simulate data corruption or modifying the code to reproduce the problem&lt;br /&gt;
&lt;br /&gt;
Example of instructions for testers:&lt;br /&gt;
&lt;br /&gt;
    INSTRUCTIONS FOR TESTING (difficulty: easy, requires teacher access to a course)&amp;lt;br /&amp;gt;&lt;br /&gt;
    1. Log in as a teacher and go to a course (you can use some of yours or restore the attached .mbz backup)&lt;br /&gt;
    2. Turn editing mode on&lt;br /&gt;
    3. TEST: Make sure that the control icons appear next to the activity titles&lt;br /&gt;
    4. Turn editing mode off&lt;br /&gt;
    5. TEST: Make sure that the control icons are not displayed now&lt;br /&gt;
&lt;br /&gt;
== Introducing new strings ==&lt;br /&gt;
&lt;br /&gt;
Firstly, think twice and try to think in a non-English language. Any string you introduce is supposed to be translated by translators who usually do it for free in their own time. Do not waste their time by using get_string() for debugging messages that are likely to almost never appear. It is warmly recommended to let Helen review your strings before you submit them. This way we can keep the terminology and the style consistent. When introducing new strings, keep them alphabetically sorted. Using a self-descriptive names of the string identifier and the placeholder properties helps the translators to guess the context. Compare the following&lt;br /&gt;
&lt;br /&gt;
    $string[&#039;grade&#039;] = &#039;Grade {$a}&#039;;&lt;br /&gt;
&lt;br /&gt;
with&lt;br /&gt;
&lt;br /&gt;
    $string[&#039;maxgradevalue&#039;] = &#039;Grade {$a-&amp;gt;value}&#039;;&lt;br /&gt;
&lt;br /&gt;
In the first case, it is pretty difficult to guess whether the &amp;quot;Grade&amp;quot; in the string is a noun (as in &amp;quot;Grade 12/30&amp;quot;) or a verb (as in &amp;quot;Grade submission&amp;quot;). In many languages, the translation depends on it. The second case is more self-descriptive as it indicates that the placeholder will contain a value.&lt;br /&gt;
&lt;br /&gt;
See [[Development:Help strings]] if you are introducing a new help string.&lt;br /&gt;
&lt;br /&gt;
== Include AMOS script in the commit if needed ==&lt;br /&gt;
&lt;br /&gt;
If you change the identifier of a string or split a string into two forks, provide a script for AMOS in the commit message. Since Moodle 2.0, the translations are kept on separated branches again. The AMOS plugin at http://lang.moodle.org tracks the changes in string files and automatically records modifications, additions and removals of strings. Therefore strings can be re-worded freely on stable branches and should be removed from the master branch if they are not needed any more.&lt;br /&gt;
&lt;br /&gt;
If you change the identifier of the string (that is the key in the $string array), move the string from one file to another or you are introducing a new string as a copy of some current one, you should provide instructions for AMOS so that the action can be applied in all language packs. That will save valuable translators&#039; time. Instructions for AMOS are being put into the commit message of a commit that modifies the original English string files. The commit message containing such a script may look like this:&lt;br /&gt;
&lt;br /&gt;
    MDL-xxxxx code area: short description of the patch&amp;lt;br /&amp;gt;&lt;br /&gt;
    It is recommended to leave a blank line between the commit message and the&lt;br /&gt;
    script block.&amp;lt;br /&amp;gt;&lt;br /&gt;
    AMOS BEGIN&lt;br /&gt;
     MOV [configfoobar,core_admin],[foobar_desc,core_admin]&lt;br /&gt;
     CPY [submission,mod_assignment],[submission,mod_workshop]&lt;br /&gt;
    AMOS END&lt;br /&gt;
&lt;br /&gt;
See [[Development:Languages/AMOS#AMOS_script]] for more details of the syntax. See [http://git.moodle.org/gw?p=moodle.git&amp;amp;a=search&amp;amp;h=HEAD&amp;amp;st=commit&amp;amp;s=AMOS+BEGIN the log history] real examples of usage.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Commit_cheat_sheet&amp;diff=82615</id>
		<title>Development:Commit cheat sheet</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Commit_cheat_sheet&amp;diff=82615"/>
		<updated>2011-04-07T10:13:56Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Provide clear and operational instructions to test your patch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;You can consider this page as a list to check before you submit a patch for inclusion into Moodle.&lt;br /&gt;
&lt;br /&gt;
== Split your work into a logical set of patches ==&lt;br /&gt;
&lt;br /&gt;
Keep in mind that your commits will be reviewed before they are accepted. If the patch does one clear thing and does it well, the review process is fun. Git allows you to prepare patches on your branch into a sequence of logical steps. For example, when changing some API, divide the change into two steps. In the first commit, change the API. In the following commit, change all places that use the API.&lt;br /&gt;
&lt;br /&gt;
== Provide clear commit messages ==&lt;br /&gt;
&lt;br /&gt;
Consider the commit message as an email for the developer who would explore the change in the future. We recommend to follow common Git guidelines for formatting. Include the MDL issue number and eventually the area/component at the beginning of the subject line.&lt;br /&gt;
&lt;br /&gt;
    MDL-xxxxx code area: short description of the patch&amp;lt;br /&amp;gt;&lt;br /&gt;
    The subject line is followed by an empty line and then a paragraph or two of&lt;br /&gt;
    a longer description follows. This longer description is useful for issues&lt;br /&gt;
    with longer history of comments in the linked MDL so that it summarizes the&lt;br /&gt;
    patch without the need to go through the whole discussion.&amp;lt;br /&amp;gt;&lt;br /&gt;
    Obviously, avoid messages like &amp;quot;as agreed in the chat&amp;quot; as they will become&lt;br /&gt;
    useless after a relatively short time.&lt;br /&gt;
&lt;br /&gt;
Most of Git tools are optimised for this format and they can display the log of commits best then.&lt;br /&gt;
&lt;br /&gt;
== Names in the commit message ==&lt;br /&gt;
&lt;br /&gt;
Retain the authorship of the patch. If the patch was submitted by someone else - for example a community member who published it in the tracker or in a forum - use the --author parameter. Make sure that your &#039;&#039;real&#039;&#039; name and contact email are recorded in patch. We use real names written in capital letters like &amp;quot;John Smith&amp;quot;. Please do not use names like &amp;lt;strike&amp;gt;&amp;quot;john smith&amp;quot;, &amp;quot;John S&amp;quot;, &amp;quot;johnny7887&amp;quot;&amp;lt;/strike&amp;gt;. If you use --author parameter, apply the same rules for the name of the author. See almost any Git tutorial on how to set your name and email in the global Git configuration.&lt;br /&gt;
&lt;br /&gt;
== Provide clear and operational instructions to test your patch ==&lt;br /&gt;
&lt;br /&gt;
In the PULL request, please describe how the change can be tested. Please avoid vague phrases like &amp;quot;Make sure there is no regression in the core&amp;quot; or &amp;quot;Test all places where XXX is used&amp;quot;. Also, try to avoid requiring resources that are really difficult to gather, if possible - as in &amp;quot;Use production data from a server with 100.000+ students&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
In most cases, the instructions for testers should go into the PULL request to make the tester&#039;s life easier. This means you will need to copy and paste them into every PULL request (if there are multiple PULL requests for different branches, or if your first PULL request gets rejected). If these instructions are in the MDL&lt;br /&gt;
&lt;br /&gt;
If the instructions are elsewhere (for example they were provided by the reported in the linked MDL as a part of steps to reproduce), please inform testers in the PULL request where they can find them. However it is preferable to copy/paste the instructions into each PULL. &lt;br /&gt;
&lt;br /&gt;
It helps if you state your estimation of the testing difficulty so that testers can pick issues for them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Easy&#039;&#039; (average community member should be able to test it) - can be tested pretty easily via the web interface only at a public test site&lt;br /&gt;
* &#039;&#039;Moderate&#039;&#039; (knowledgeable administrator should be able to test it) - requires local installation, for example to test some 1.9 -&amp;gt; 2.0 upgrade steps or some non-standard environment (for example MNet features, specific platform etc)&lt;br /&gt;
* &#039;&#039;Hard&#039;&#039; (development skills are required to test it) - for example may require data hacking at SQL level to simulate data corruption or modifying the code to reproduce the problem&lt;br /&gt;
&lt;br /&gt;
Example of instructions for testers:&lt;br /&gt;
&lt;br /&gt;
    INSTRUCTIONS FOR TESTING (difficulty: easy, requires teacher access to a course)&amp;lt;br /&amp;gt;&lt;br /&gt;
    1. Log in as a teacher and go to a course (you can use some of yours or restore the attached .mbz backup)&lt;br /&gt;
    2. Turn editing mode on&lt;br /&gt;
    3. TEST: Make sure that the control icons appear next to the activity titles&lt;br /&gt;
    4. Turn editing mode off&lt;br /&gt;
    5. TEST: Make sure that the control icons are not displayed now&lt;br /&gt;
&lt;br /&gt;
== Introducing new strings ==&lt;br /&gt;
&lt;br /&gt;
Firstly, think twice and try to think in a non-English language. Any string you introduce is supposed to be translated by translators who usually do it for free in their own time. Do not waste their time by using get_string() for debugging messages that are likely to almost never appear. It is warmly recommended to let Helen review your strings before you submit them. This way we can keep the terminology and the style consistent. When introducing new strings, keep them alphabetically sorted. Using a self-descriptive names of the string identifier and the placeholder properties helps the translators to guess the context. Compare the following&lt;br /&gt;
&lt;br /&gt;
    $string[&#039;grade&#039;] = &#039;Grade {$a}&#039;;&lt;br /&gt;
&lt;br /&gt;
with&lt;br /&gt;
&lt;br /&gt;
    $string[&#039;maxgradevalue&#039;] = &#039;Grade {$a-&amp;gt;value}&#039;;&lt;br /&gt;
&lt;br /&gt;
In the first case, it is pretty difficult to guess whether the &amp;quot;Grade&amp;quot; in the string is a noun (as in &amp;quot;Grade 12/30&amp;quot;) or a verb (as in &amp;quot;Grade submission&amp;quot;). In many languages, the translation depends on it. The second case is more self-descriptive as it indicates that the placeholder will contain a value.&lt;br /&gt;
&lt;br /&gt;
See [[Development:Help strings]] if you are introducing a new help string.&lt;br /&gt;
&lt;br /&gt;
== Include AMOS script in the commit if needed ==&lt;br /&gt;
&lt;br /&gt;
If you change the identifier of a string or split a string into two forks, provide a script for AMOS in the commit message. Since Moodle 2.0, the translations are kept on separated branches again. The AMOS plugin at http://lang.moodle.org tracks the changes in string files and automatically records modifications, additions and removals of strings. Therefore strings can be re-worded freely on stable branches and should be removed from the master branch if they are not needed any more.&lt;br /&gt;
&lt;br /&gt;
If you change the identifier of the string (that is the key in the $string array), move the string from one file to another or you are introducing a new string as a copy of some current one, you should provide instructions for AMOS so that the action can be applied in all language packs. That will save valuable translators&#039; time. Instructions for AMOS are being put into the commit message of a commit that modifies the original English string files. The commit message containing such a script may look like this:&lt;br /&gt;
&lt;br /&gt;
    MDL-xxxxx code area: short description of the patch&amp;lt;br /&amp;gt;&lt;br /&gt;
    It is recommended to leave a blank line between the commit message and the&lt;br /&gt;
    script block.&amp;lt;br /&amp;gt;&lt;br /&gt;
    AMOS BEGIN&lt;br /&gt;
     MOV [configfoobar,core_admin],[foobar_desc,core_admin]&lt;br /&gt;
     CPY [submission,mod_assignment],[submission,mod_workshop]&lt;br /&gt;
    AMOS END&lt;br /&gt;
&lt;br /&gt;
See [[Development:Languages/AMOS#AMOS_script]] for more details of the syntax. See [http://git.moodle.org/gw?p=moodle.git&amp;amp;a=search&amp;amp;h=HEAD&amp;amp;st=commit&amp;amp;s=AMOS+BEGIN the log history] real examples of usage.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Restore_2.0_for_developers&amp;diff=82547</id>
		<title>Development:Restore 2.0 for developers</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Restore_2.0_for_developers&amp;diff=82547"/>
		<updated>2011-04-04T10:21:39Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Automatically triggering restore in code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Development:Backup 2.0}}{{Moodle_2.0}}{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
This page tries to explain, from a development perspective, &#039;&#039;&#039;how to implement&#039;&#039;&#039; the restore feature for various Moodle 2.x plugins, mainly, modules and blocks. &lt;br /&gt;
&lt;br /&gt;
In order to work with Restore 2.0 you must read the [https://docs.moodle.org/en/Development:Backup_2.0_for_developers Backup 2.0 documentation]. Everything will be crystal clear once you have read it. &lt;br /&gt;
&lt;br /&gt;
In this documentation you will learn about some points that you can not easily deduce from [https://docs.moodle.org/en/Development:Backup_2.0_for_developers Backup 2.0 documentation] concerning the Choice module.&lt;br /&gt;
&lt;br /&gt;
== Coding ==&lt;br /&gt;
the restore is composed by two main php files: restore_choice_stepslib.php and restore_choice_activity_task.class.php&lt;br /&gt;
=== restore_choice_stepslib.php ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Structure step to restore one choice activity&lt;br /&gt;
 */&lt;br /&gt;
class restore_choice_activity_structure_step extends restore_activity_structure_step {&lt;br /&gt;
&lt;br /&gt;
    protected function define_structure() {&lt;br /&gt;
&lt;br /&gt;
        $paths = array();&lt;br /&gt;
        $userinfo = $this-&amp;gt;get_setting_value(&#039;userinfo&#039;);&lt;br /&gt;
&lt;br /&gt;
        $paths[] = new restore_path_element(&#039;choice&#039;, &#039;/activity/choice&#039;);&lt;br /&gt;
        $paths[] = new restore_path_element(&#039;choice_option&#039;, &#039;/activity/choice/options/option&#039;);&lt;br /&gt;
        if ($userinfo) {&lt;br /&gt;
            $paths[] = new restore_path_element(&#039;choice_answer&#039;, &#039;/activity/choice/answers/answer&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Return the paths wrapped into standard activity structure&lt;br /&gt;
        return $this-&amp;gt;prepare_activity_structure($paths);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
        $data-&amp;gt;course = $this-&amp;gt;get_courseid();&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;timeopen = $this-&amp;gt;apply_date_offset($data-&amp;gt;timeopen);&lt;br /&gt;
        $data-&amp;gt;timeclose = $this-&amp;gt;apply_date_offset($data-&amp;gt;timeclose);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        // insert the choice record&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice&#039;, $data);&lt;br /&gt;
        // immediately after inserting &amp;quot;activity&amp;quot; record, call this&lt;br /&gt;
        $this-&amp;gt;apply_activity_instance($newitemid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice_option($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;choiceid = $this-&amp;gt;get_new_parentid(&#039;choice&#039;);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice_options&#039;, $data);&lt;br /&gt;
        $this-&amp;gt;set_mapping(&#039;choice_option&#039;, $oldid, $newitemid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice_answer($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;choiceid = $this-&amp;gt;get_new_parentid(&#039;choice&#039;);&lt;br /&gt;
        $data-&amp;gt;optionid = $this-&amp;gt;get_mappingid(&#039;choice_option&#039;, $oldid);&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;get_mappingid(&#039;user&#039;, $data-&amp;gt;userid);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice_answers&#039;, $data);&lt;br /&gt;
        // No need to save this mapping as far as nothing depend on it&lt;br /&gt;
        // (child paths, file areas nor links decoder)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function after_execute() {&lt;br /&gt;
        // Add choice related files, no need to match by itemname (just internally handled context)&lt;br /&gt;
        $this-&amp;gt;add_related_files(&#039;mod_choice&#039;, &#039;intro&#039;, null);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== restore_choice_activity_task.class.php ===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * choice restore task that provides all the settings and steps to perform one&lt;br /&gt;
 * complete restore of the activity&lt;br /&gt;
 */&lt;br /&gt;
class restore_choice_activity_task extends restore_activity_task {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define (add) particular settings this activity can have&lt;br /&gt;
     */&lt;br /&gt;
    protected function define_my_settings() {&lt;br /&gt;
        // No particular settings for this activity&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define (add) particular steps this activity can have&lt;br /&gt;
     */&lt;br /&gt;
    protected function define_my_steps() {&lt;br /&gt;
        // Choice only has one structure step&lt;br /&gt;
        $this-&amp;gt;add_step(new restore_choice_activity_structure_step(&#039;choice_structure&#039;, &#039;choice.xml&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the contents in the activity that must be&lt;br /&gt;
     * processed by the link decoder&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_decode_contents() {&lt;br /&gt;
        $contents = array();&lt;br /&gt;
&lt;br /&gt;
        $contents[] = new restore_decode_content(&#039;choice&#039;, array(&#039;intro&#039;), &#039;choice&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $contents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the decoding rules for links belonging&lt;br /&gt;
     * to the activity to be executed by the link decoder&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_decode_rules() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        $rules[] = new restore_decode_rule(&#039;CHOICEVIEWBYID&#039;, &#039;/mod/choice/view.php?id=$1&#039;, &#039;course_module&#039;);&lt;br /&gt;
        $rules[] = new restore_decode_rule(&#039;CHOICEINDEX&#039;, &#039;/mod/choice/index.php?id=$1&#039;, &#039;course&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the restore log rules that will be applied&lt;br /&gt;
     * by the {@link restore_logs_processor} when restoring&lt;br /&gt;
     * choice logs. It must return one array&lt;br /&gt;
     * of {@link restore_log_rule} objects&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_restore_log_rules() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;add&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;update&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;choose&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;choose again&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;report&#039;, &#039;report.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the restore log rules that will be applied&lt;br /&gt;
     * by the {@link restore_logs_processor} when restoring&lt;br /&gt;
     * course logs. It must return one array&lt;br /&gt;
     * of {@link restore_log_rule} objects&lt;br /&gt;
     *&lt;br /&gt;
     * Note this rules are applied when restoring course logs&lt;br /&gt;
     * by the restore final task, but are defined here at&lt;br /&gt;
     * activity level. All them are rules not linked to any module instance (cmid = 0)&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_restore_log_rules_for_course() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        // Fix old wrong uses (missing extension)&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view all&#039;, &#039;index?id={course}&#039;, null,&lt;br /&gt;
                                        null, null, &#039;index.php?id={course}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view all&#039;, &#039;index.php?id={course}&#039;, null);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;define_restore_log_rules&#039;&#039;&#039;: it covers all the &amp;quot;mandatory&amp;quot; log actions, performing {course_module} and {choice} mappings properly. Note that names between curly brackets instruct the log processor to look in the mapping tables for item = name between brackets. Of course, other modules like forum, has a lot of rules, with more complex mappings and so on. Choice is pretty basic.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;define_restore_log_rules_for_course&#039;&#039;&#039;: choice module has the 1st rule because, while implementing restore, we discovered one bug causing URLs to be written in the logs table WITHOUT the .php in the URL, hence choice has that extra rule (that uses the 5-8 params, to REWRITE the URL to be inserted on restore). But that was one problem with choices only, afaik, so you probably would not need it in your own module.&lt;br /&gt;
&lt;br /&gt;
== Automatically triggering restore in code ==&lt;br /&gt;
&lt;br /&gt;
As with backup it is possible to automatically trigger a restore for a course (or activity or anything else you can back up). Here is how to restore a course backup into a new course:&lt;br /&gt;
&lt;br /&gt;
# Put the backup files in a specific folder: $CFG-&amp;gt;dataroot/temp/backup/$folder, where $folder is a random or unused unique id.&lt;br /&gt;
# Decide on the course shortname, fullname, and category id for restore (course must not already exist).&lt;br /&gt;
# Run the following code:&lt;br /&gt;
&lt;br /&gt;
        // Transaction&lt;br /&gt;
        $transaction = $DB-&amp;gt;start_delegated_transaction();&lt;br /&gt;
 &lt;br /&gt;
        // Create new course&lt;br /&gt;
        $courseid = restore_dbops::create_new_course($fullname, $shortname, $categoryid);&lt;br /&gt;
 &lt;br /&gt;
        // Restore backup into course&lt;br /&gt;
        $controller = new restore_controller($folder, $courseid, &lt;br /&gt;
                backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER-&amp;gt;id,&lt;br /&gt;
                backup::TARGET_NEW_COURSE);&lt;br /&gt;
        $controller-&amp;gt;execute_precheck();&lt;br /&gt;
        $controller-&amp;gt;execute_plan();&lt;br /&gt;
 &lt;br /&gt;
        // Commit&lt;br /&gt;
        $transaction-&amp;gt;allow_commit();&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
&lt;br /&gt;
* The transaction is used so that if backup fails, the course isn&#039;t created.&lt;br /&gt;
* The MODE_SAMESITE option is used if restoring a backup which comes from the same site, otherwise MODE_GENERAL might be used.&lt;br /&gt;
&lt;br /&gt;
== Code that runs after restore ==&lt;br /&gt;
&lt;br /&gt;
If you are writing a restore task for an activity module, your code is all executed while the system is restoring that module. For example, the &amp;lt;tt&amp;gt;after_execute&amp;lt;/tt&amp;gt;  method in your &amp;lt;tt&amp;gt;restore_mymodule_activity_structure_step&amp;lt;/tt&amp;gt; class is called immediately after all the other restore functions in that step are finished.&lt;br /&gt;
&lt;br /&gt;
=== The problem ===&lt;br /&gt;
&lt;br /&gt;
Usually this is correct, but in some situations you may need to have an activity module restore that depends in some way on another activity module or on a course section other than the one the module belongs to. Depending on the order within the course, in some cases the other module or section has not yet been restored, so this is a problem.&lt;br /&gt;
&lt;br /&gt;
The code that may not work as a result is code that tries to convert from an &#039;old&#039; course-module ID or section ID into a &#039;new&#039; one:&lt;br /&gt;
&lt;br /&gt;
 $newcmid = $this-&amp;gt;get_mappingid(&#039;course_module&#039;, $oldcmid);&lt;br /&gt;
 $newsectionid = $this-&amp;gt;get_mappingid(&#039;course_section&#039;, $oldsectionid);&lt;br /&gt;
&lt;br /&gt;
These functions will return false if the relevant course-module or section has not yet been restored.&lt;br /&gt;
&lt;br /&gt;
=== The solution ===&lt;br /&gt;
&lt;br /&gt;
You can have a method called &amp;lt;tt&amp;gt;after_restore&amp;lt;/tt&amp;gt; inside your TASK (not step) class, &amp;lt;tt&amp;gt;restore_mymodule_activity_task&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 public function after_restore() {&lt;br /&gt;
     // Do something at end of restore&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
In this method you can carry out necessary finishing tasks such as updating course-module and section IDs (using code like the above).&lt;br /&gt;
&lt;br /&gt;
=== Warning ===&lt;br /&gt;
&lt;br /&gt;
You should do as little as possible in this method, and be aware that using it may mean your activity module does not function correctly in some cases.&lt;br /&gt;
&lt;br /&gt;
* It is possible to backup and restore a single module, or a section, rather than a whole course. &lt;br /&gt;
* This function (backing up a single module) may also be used to support other upcoming features such as the &#039;copy activity&#039; feature.&lt;br /&gt;
&lt;br /&gt;
If somebody backs up your activity as a single activity, then obviously any such dependencies will not be included. There is currently no way to indicate that your module has any dependencies. So the backup and restore will go ahead but, once again, the &amp;lt;tt&amp;gt;get_mappingid&amp;lt;/tt&amp;gt; function will return false.&lt;br /&gt;
&lt;br /&gt;
At present the best way to handle this is to add a message to the restore log (which will not currently be displayed anywhere, but this might change):&lt;br /&gt;
&lt;br /&gt;
 $this-&amp;gt;get_logger()-&amp;gt;process(&amp;quot;Failed to restore dependency in mymodule &#039;$name&#039;. &amp;quot; .&lt;br /&gt;
         &amp;quot;Backup and restore will not work correctly unless you include the dependent module.&amp;quot;,&lt;br /&gt;
         backup::LOG_ERROR);&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Installing_Moodle_from_Git_repository&amp;diff=82456</id>
		<title>Installing Moodle from Git repository</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Installing_Moodle_from_Git_repository&amp;diff=82456"/>
		<updated>2011-03-31T12:48:52Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Installing other versions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This page is a work in progress&#039;&#039;&#039;&lt;br /&gt;
== Command line (all platforms) ==&lt;br /&gt;
&lt;br /&gt;
Using the cd command, move to the directory that you wish to install Moodle inside. The Moodle code will be placed in a subdirectory of the directory you&#039;re currently in. For example, if you are in /tmp when you run the command, Moodle will be installed in /tmp/moodle.&lt;br /&gt;
&lt;br /&gt;
Clone the offical Moodle repository via git, taking the 2.0 stable branch, and placing it into a subdirectory called moodle.&lt;br /&gt;
&lt;br /&gt;
 git clone -b MOODLE_20_STABLE git://git.moodle.org/moodle.git moodle&lt;br /&gt;
&lt;br /&gt;
If you have firewall issues with the git protocol, the above may not work, in which case you can try cloning the official github repository via http&lt;br /&gt;
&lt;br /&gt;
 git clone -b MOODLE_20_STABLE https://github.com/moodle/moodle.git moodle&lt;br /&gt;
&lt;br /&gt;
Whichever command works it should display a result like the following. Installing will probably take around ten minutes.&lt;br /&gt;
&lt;br /&gt;
 Initialized empty Git repository in ~/moodle/.git/&lt;br /&gt;
 remote: Counting objects: 448351, done.&lt;br /&gt;
 remote: Compressing objects: 100% (103262/103262), done.&lt;br /&gt;
 remote: Total 448351 (delta 336929), reused 446784 (delta 335924)&lt;br /&gt;
 Receiving objects: 100% (448351/448351), 128.54 MiB | 10.94 MiB/s, done.&lt;br /&gt;
 Resolving deltas: 100% (336929/336929), done.&lt;br /&gt;
&lt;br /&gt;
Move in to the new moodle directory that you just created.&lt;br /&gt;
&lt;br /&gt;
 cd moodle&lt;br /&gt;
&lt;br /&gt;
From here, you can edit config.php and install as per the [[Installing_Moodle#Setting-up_your_web_server|Installation Instructions]]&lt;br /&gt;
&lt;br /&gt;
Upgrading to the latest Moodle 2.0 version is now one simple command. Change into the moodle directory and then run:&lt;br /&gt;
&lt;br /&gt;
 git pull origin MOODLE_20_STABLE&lt;br /&gt;
&lt;br /&gt;
== Installing other versions ==&lt;br /&gt;
&lt;br /&gt;
You can install another version by replacing MOODLE_20_STABLE with e.g. MOODLE_19_STABLE. If you want the latest unfinished development version, use &#039;master&#039; (note lower-case) instead of MOODLE_20_STABLE.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Installing_Moodle_from_Git_repository&amp;diff=82455</id>
		<title>Installing Moodle from Git repository</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Installing_Moodle_from_Git_repository&amp;diff=82455"/>
		<updated>2011-03-31T12:48:20Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Installing other versions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This page is a work in progress&#039;&#039;&#039;&lt;br /&gt;
== Command line (all platforms) ==&lt;br /&gt;
&lt;br /&gt;
Using the cd command, move to the directory that you wish to install Moodle inside. The Moodle code will be placed in a subdirectory of the directory you&#039;re currently in. For example, if you are in /tmp when you run the command, Moodle will be installed in /tmp/moodle.&lt;br /&gt;
&lt;br /&gt;
Clone the offical Moodle repository via git, taking the 2.0 stable branch, and placing it into a subdirectory called moodle.&lt;br /&gt;
&lt;br /&gt;
 git clone -b MOODLE_20_STABLE git://git.moodle.org/moodle.git moodle&lt;br /&gt;
&lt;br /&gt;
If you have firewall issues with the git protocol, the above may not work, in which case you can try cloning the official github repository via http&lt;br /&gt;
&lt;br /&gt;
 git clone -b MOODLE_20_STABLE https://github.com/moodle/moodle.git moodle&lt;br /&gt;
&lt;br /&gt;
Whichever command works it should display a result like the following. Installing will probably take around ten minutes.&lt;br /&gt;
&lt;br /&gt;
 Initialized empty Git repository in ~/moodle/.git/&lt;br /&gt;
 remote: Counting objects: 448351, done.&lt;br /&gt;
 remote: Compressing objects: 100% (103262/103262), done.&lt;br /&gt;
 remote: Total 448351 (delta 336929), reused 446784 (delta 335924)&lt;br /&gt;
 Receiving objects: 100% (448351/448351), 128.54 MiB | 10.94 MiB/s, done.&lt;br /&gt;
 Resolving deltas: 100% (336929/336929), done.&lt;br /&gt;
&lt;br /&gt;
Move in to the new moodle directory that you just created.&lt;br /&gt;
&lt;br /&gt;
 cd moodle&lt;br /&gt;
&lt;br /&gt;
From here, you can edit config.php and install as per the [[Installing_Moodle#Setting-up_your_web_server|Installation Instructions]]&lt;br /&gt;
&lt;br /&gt;
Upgrading to the latest Moodle 2.0 version is now one simple command. Change into the moodle directory and then run:&lt;br /&gt;
&lt;br /&gt;
 git pull origin MOODLE_20_STABLE&lt;br /&gt;
&lt;br /&gt;
== Installing other versions ==&lt;br /&gt;
&lt;br /&gt;
You can install another version by replacing MOODLE_20_STABLE with e.g. MOODLE_19_STABLE. If you want the latest development version, use &#039;master&#039; (note lower-case) instead of MOODLE_20_STABLE.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Installing_Moodle_from_Git_repository&amp;diff=82454</id>
		<title>Installing Moodle from Git repository</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Installing_Moodle_from_Git_repository&amp;diff=82454"/>
		<updated>2011-03-31T12:46:24Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This page is a work in progress&#039;&#039;&#039;&lt;br /&gt;
== Command line (all platforms) ==&lt;br /&gt;
&lt;br /&gt;
Using the cd command, move to the directory that you wish to install Moodle inside. The Moodle code will be placed in a subdirectory of the directory you&#039;re currently in. For example, if you are in /tmp when you run the command, Moodle will be installed in /tmp/moodle.&lt;br /&gt;
&lt;br /&gt;
Clone the offical Moodle repository via git, taking the 2.0 stable branch, and placing it into a subdirectory called moodle.&lt;br /&gt;
&lt;br /&gt;
 git clone -b MOODLE_20_STABLE git://git.moodle.org/moodle.git moodle&lt;br /&gt;
&lt;br /&gt;
If you have firewall issues with the git protocol, the above may not work, in which case you can try cloning the official github repository via http&lt;br /&gt;
&lt;br /&gt;
 git clone -b MOODLE_20_STABLE https://github.com/moodle/moodle.git moodle&lt;br /&gt;
&lt;br /&gt;
Whichever command works it should display a result like the following. Installing will probably take around ten minutes.&lt;br /&gt;
&lt;br /&gt;
 Initialized empty Git repository in ~/moodle/.git/&lt;br /&gt;
 remote: Counting objects: 448351, done.&lt;br /&gt;
 remote: Compressing objects: 100% (103262/103262), done.&lt;br /&gt;
 remote: Total 448351 (delta 336929), reused 446784 (delta 335924)&lt;br /&gt;
 Receiving objects: 100% (448351/448351), 128.54 MiB | 10.94 MiB/s, done.&lt;br /&gt;
 Resolving deltas: 100% (336929/336929), done.&lt;br /&gt;
&lt;br /&gt;
Move in to the new moodle directory that you just created.&lt;br /&gt;
&lt;br /&gt;
 cd moodle&lt;br /&gt;
&lt;br /&gt;
From here, you can edit config.php and install as per the [[Installing_Moodle#Setting-up_your_web_server|Installation Instructions]]&lt;br /&gt;
&lt;br /&gt;
Upgrading to the latest Moodle 2.0 version is now one simple command. Change into the moodle directory and then run:&lt;br /&gt;
&lt;br /&gt;
 git pull origin MOODLE_20_STABLE&lt;br /&gt;
&lt;br /&gt;
== Installing other versions ==&lt;br /&gt;
&lt;br /&gt;
You can install another version by replacing MOODLE_20 with e.g. MOODLE_19. If you want the latest development version, use &#039;master&#039; (note lower-case) instead of MOODLE_20.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Restore_2.0_for_developers&amp;diff=82141</id>
		<title>Development:Restore 2.0 for developers</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Restore_2.0_for_developers&amp;diff=82141"/>
		<updated>2011-03-21T14:07:38Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Automatically triggering restore in code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Development:Backup 2.0}}{{Moodle_2.0}}{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
This page tries to explain, from a development perspective, &#039;&#039;&#039;how to implement&#039;&#039;&#039; the restore feature for various Moodle 2.x plugins, mainly, modules and blocks. &lt;br /&gt;
&lt;br /&gt;
In order to work with Restore 2.0 you must read the [https://docs.moodle.org/en/Development:Backup_2.0_for_developers Backup 2.0 documentation]. Everything will be crystal clear once you have read it. &lt;br /&gt;
&lt;br /&gt;
In this documentation you will learn about some points that you can not easily deduce from [https://docs.moodle.org/en/Development:Backup_2.0_for_developers Backup 2.0 documentation] concerning the Choice module.&lt;br /&gt;
&lt;br /&gt;
== Coding ==&lt;br /&gt;
the restore is composed by two main php files: restore_choice_stepslib.php and restore_choice_activity_task.class.php&lt;br /&gt;
=== restore_choice_stepslib.php ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Structure step to restore one choice activity&lt;br /&gt;
 */&lt;br /&gt;
class restore_choice_activity_structure_step extends restore_activity_structure_step {&lt;br /&gt;
&lt;br /&gt;
    protected function define_structure() {&lt;br /&gt;
&lt;br /&gt;
        $paths = array();&lt;br /&gt;
        $userinfo = $this-&amp;gt;get_setting_value(&#039;userinfo&#039;);&lt;br /&gt;
&lt;br /&gt;
        $paths[] = new restore_path_element(&#039;choice&#039;, &#039;/activity/choice&#039;);&lt;br /&gt;
        $paths[] = new restore_path_element(&#039;choice_option&#039;, &#039;/activity/choice/options/option&#039;);&lt;br /&gt;
        if ($userinfo) {&lt;br /&gt;
            $paths[] = new restore_path_element(&#039;choice_answer&#039;, &#039;/activity/choice/answers/answer&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Return the paths wrapped into standard activity structure&lt;br /&gt;
        return $this-&amp;gt;prepare_activity_structure($paths);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
        $data-&amp;gt;course = $this-&amp;gt;get_courseid();&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;timeopen = $this-&amp;gt;apply_date_offset($data-&amp;gt;timeopen);&lt;br /&gt;
        $data-&amp;gt;timeclose = $this-&amp;gt;apply_date_offset($data-&amp;gt;timeclose);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        // insert the choice record&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice&#039;, $data);&lt;br /&gt;
        // immediately after inserting &amp;quot;activity&amp;quot; record, call this&lt;br /&gt;
        $this-&amp;gt;apply_activity_instance($newitemid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice_option($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;choiceid = $this-&amp;gt;get_new_parentid(&#039;choice&#039;);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice_options&#039;, $data);&lt;br /&gt;
        $this-&amp;gt;set_mapping(&#039;choice_option&#039;, $oldid, $newitemid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice_answer($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;choiceid = $this-&amp;gt;get_new_parentid(&#039;choice&#039;);&lt;br /&gt;
        $data-&amp;gt;optionid = $this-&amp;gt;get_mappingid(&#039;choice_option&#039;, $oldid);&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;get_mappingid(&#039;user&#039;, $data-&amp;gt;userid);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice_answers&#039;, $data);&lt;br /&gt;
        // No need to save this mapping as far as nothing depend on it&lt;br /&gt;
        // (child paths, file areas nor links decoder)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function after_execute() {&lt;br /&gt;
        // Add choice related files, no need to match by itemname (just internally handled context)&lt;br /&gt;
        $this-&amp;gt;add_related_files(&#039;mod_choice&#039;, &#039;intro&#039;, null);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== restore_choice_activity_task.class.php ===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * choice restore task that provides all the settings and steps to perform one&lt;br /&gt;
 * complete restore of the activity&lt;br /&gt;
 */&lt;br /&gt;
class restore_choice_activity_task extends restore_activity_task {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define (add) particular settings this activity can have&lt;br /&gt;
     */&lt;br /&gt;
    protected function define_my_settings() {&lt;br /&gt;
        // No particular settings for this activity&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define (add) particular steps this activity can have&lt;br /&gt;
     */&lt;br /&gt;
    protected function define_my_steps() {&lt;br /&gt;
        // Choice only has one structure step&lt;br /&gt;
        $this-&amp;gt;add_step(new restore_choice_activity_structure_step(&#039;choice_structure&#039;, &#039;choice.xml&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the contents in the activity that must be&lt;br /&gt;
     * processed by the link decoder&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_decode_contents() {&lt;br /&gt;
        $contents = array();&lt;br /&gt;
&lt;br /&gt;
        $contents[] = new restore_decode_content(&#039;choice&#039;, array(&#039;intro&#039;), &#039;choice&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $contents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the decoding rules for links belonging&lt;br /&gt;
     * to the activity to be executed by the link decoder&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_decode_rules() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        $rules[] = new restore_decode_rule(&#039;CHOICEVIEWBYID&#039;, &#039;/mod/choice/view.php?id=$1&#039;, &#039;course_module&#039;);&lt;br /&gt;
        $rules[] = new restore_decode_rule(&#039;CHOICEINDEX&#039;, &#039;/mod/choice/index.php?id=$1&#039;, &#039;course&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the restore log rules that will be applied&lt;br /&gt;
     * by the {@link restore_logs_processor} when restoring&lt;br /&gt;
     * choice logs. It must return one array&lt;br /&gt;
     * of {@link restore_log_rule} objects&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_restore_log_rules() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;add&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;update&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;choose&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;choose again&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;report&#039;, &#039;report.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the restore log rules that will be applied&lt;br /&gt;
     * by the {@link restore_logs_processor} when restoring&lt;br /&gt;
     * course logs. It must return one array&lt;br /&gt;
     * of {@link restore_log_rule} objects&lt;br /&gt;
     *&lt;br /&gt;
     * Note this rules are applied when restoring course logs&lt;br /&gt;
     * by the restore final task, but are defined here at&lt;br /&gt;
     * activity level. All them are rules not linked to any module instance (cmid = 0)&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_restore_log_rules_for_course() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        // Fix old wrong uses (missing extension)&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view all&#039;, &#039;index?id={course}&#039;, null,&lt;br /&gt;
                                        null, null, &#039;index.php?id={course}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view all&#039;, &#039;index.php?id={course}&#039;, null);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;define_restore_log_rules&#039;&#039;&#039;: it covers all the &amp;quot;mandatory&amp;quot; log actions, performing {course_module} and {choice} mappings properly. Note that names between curly brackets instruct the log processor to look in the mapping tables for item = name between brackets. Of course, other modules like forum, has a lot of rules, with more complex mappings and so on. Choice is pretty basic.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;define_restore_log_rules_for_course&#039;&#039;&#039;: choice module has the 1st rule because, while implementing restore, we discovered one bug causing URLs to be written in the logs table WITHOUT the .php in the URL, hence choice has that extra rule (that uses the 5-8 params, to REWRITE the URL to be inserted on restore). But that was one problem with choices only, afaik, so you probably would not need it in your own module.&lt;br /&gt;
&lt;br /&gt;
== Automatically triggering restore in code ==&lt;br /&gt;
&lt;br /&gt;
As with backup it is possible to automatically trigger a restore for a course (or activity or anything else you can back up). Here is how to restore a course backup into a new course:&lt;br /&gt;
&lt;br /&gt;
# Put the backup files in a specific folder: $CFG-&amp;gt;dataroot/temp/backup/$folder, where $folder is a random or unused unique id.&lt;br /&gt;
# Decide on the course shortname, fullname, and category id for restore (course must not already exist).&lt;br /&gt;
# Run the following code:&lt;br /&gt;
&lt;br /&gt;
        // Transaction&lt;br /&gt;
        $transaction = $DB-&amp;gt;start_delegated_transaction();&lt;br /&gt;
 &lt;br /&gt;
        // Create new course&lt;br /&gt;
        $courseid = restore_dbops::create_new_course($fullname, $shortname, $categoryid);&lt;br /&gt;
 &lt;br /&gt;
        // Restore backup into course&lt;br /&gt;
        $controller = new restore_controller($folder, $courseid, &lt;br /&gt;
                backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER-&amp;gt;id,&lt;br /&gt;
                backup::TARGET_NEW_COURSE);&lt;br /&gt;
        $controller-&amp;gt;execute_precheck();&lt;br /&gt;
        $controller-&amp;gt;execute_plan();&lt;br /&gt;
 &lt;br /&gt;
        // Commit&lt;br /&gt;
        $transaction-&amp;gt;allow_commit();&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
&lt;br /&gt;
* The transaction is used so that if backup fails, the course isn&#039;t created.&lt;br /&gt;
* The MODE_SAMESITE option is used if restoring a backup which comes from the same site, otherwise MODE_GENERAL might be used.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Restore_2.0_for_developers&amp;diff=82140</id>
		<title>Development:Restore 2.0 for developers</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Restore_2.0_for_developers&amp;diff=82140"/>
		<updated>2011-03-21T14:06:51Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Automatically triggering restore in code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Development:Backup 2.0}}{{Moodle_2.0}}{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
This page tries to explain, from a development perspective, &#039;&#039;&#039;how to implement&#039;&#039;&#039; the restore feature for various Moodle 2.x plugins, mainly, modules and blocks. &lt;br /&gt;
&lt;br /&gt;
In order to work with Restore 2.0 you must read the [https://docs.moodle.org/en/Development:Backup_2.0_for_developers Backup 2.0 documentation]. Everything will be crystal clear once you have read it. &lt;br /&gt;
&lt;br /&gt;
In this documentation you will learn about some points that you can not easily deduce from [https://docs.moodle.org/en/Development:Backup_2.0_for_developers Backup 2.0 documentation] concerning the Choice module.&lt;br /&gt;
&lt;br /&gt;
== Coding ==&lt;br /&gt;
the restore is composed by two main php files: restore_choice_stepslib.php and restore_choice_activity_task.class.php&lt;br /&gt;
=== restore_choice_stepslib.php ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Structure step to restore one choice activity&lt;br /&gt;
 */&lt;br /&gt;
class restore_choice_activity_structure_step extends restore_activity_structure_step {&lt;br /&gt;
&lt;br /&gt;
    protected function define_structure() {&lt;br /&gt;
&lt;br /&gt;
        $paths = array();&lt;br /&gt;
        $userinfo = $this-&amp;gt;get_setting_value(&#039;userinfo&#039;);&lt;br /&gt;
&lt;br /&gt;
        $paths[] = new restore_path_element(&#039;choice&#039;, &#039;/activity/choice&#039;);&lt;br /&gt;
        $paths[] = new restore_path_element(&#039;choice_option&#039;, &#039;/activity/choice/options/option&#039;);&lt;br /&gt;
        if ($userinfo) {&lt;br /&gt;
            $paths[] = new restore_path_element(&#039;choice_answer&#039;, &#039;/activity/choice/answers/answer&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Return the paths wrapped into standard activity structure&lt;br /&gt;
        return $this-&amp;gt;prepare_activity_structure($paths);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
        $data-&amp;gt;course = $this-&amp;gt;get_courseid();&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;timeopen = $this-&amp;gt;apply_date_offset($data-&amp;gt;timeopen);&lt;br /&gt;
        $data-&amp;gt;timeclose = $this-&amp;gt;apply_date_offset($data-&amp;gt;timeclose);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        // insert the choice record&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice&#039;, $data);&lt;br /&gt;
        // immediately after inserting &amp;quot;activity&amp;quot; record, call this&lt;br /&gt;
        $this-&amp;gt;apply_activity_instance($newitemid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice_option($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;choiceid = $this-&amp;gt;get_new_parentid(&#039;choice&#039;);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice_options&#039;, $data);&lt;br /&gt;
        $this-&amp;gt;set_mapping(&#039;choice_option&#039;, $oldid, $newitemid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice_answer($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;choiceid = $this-&amp;gt;get_new_parentid(&#039;choice&#039;);&lt;br /&gt;
        $data-&amp;gt;optionid = $this-&amp;gt;get_mappingid(&#039;choice_option&#039;, $oldid);&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;get_mappingid(&#039;user&#039;, $data-&amp;gt;userid);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice_answers&#039;, $data);&lt;br /&gt;
        // No need to save this mapping as far as nothing depend on it&lt;br /&gt;
        // (child paths, file areas nor links decoder)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function after_execute() {&lt;br /&gt;
        // Add choice related files, no need to match by itemname (just internally handled context)&lt;br /&gt;
        $this-&amp;gt;add_related_files(&#039;mod_choice&#039;, &#039;intro&#039;, null);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== restore_choice_activity_task.class.php ===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * choice restore task that provides all the settings and steps to perform one&lt;br /&gt;
 * complete restore of the activity&lt;br /&gt;
 */&lt;br /&gt;
class restore_choice_activity_task extends restore_activity_task {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define (add) particular settings this activity can have&lt;br /&gt;
     */&lt;br /&gt;
    protected function define_my_settings() {&lt;br /&gt;
        // No particular settings for this activity&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define (add) particular steps this activity can have&lt;br /&gt;
     */&lt;br /&gt;
    protected function define_my_steps() {&lt;br /&gt;
        // Choice only has one structure step&lt;br /&gt;
        $this-&amp;gt;add_step(new restore_choice_activity_structure_step(&#039;choice_structure&#039;, &#039;choice.xml&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the contents in the activity that must be&lt;br /&gt;
     * processed by the link decoder&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_decode_contents() {&lt;br /&gt;
        $contents = array();&lt;br /&gt;
&lt;br /&gt;
        $contents[] = new restore_decode_content(&#039;choice&#039;, array(&#039;intro&#039;), &#039;choice&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $contents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the decoding rules for links belonging&lt;br /&gt;
     * to the activity to be executed by the link decoder&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_decode_rules() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        $rules[] = new restore_decode_rule(&#039;CHOICEVIEWBYID&#039;, &#039;/mod/choice/view.php?id=$1&#039;, &#039;course_module&#039;);&lt;br /&gt;
        $rules[] = new restore_decode_rule(&#039;CHOICEINDEX&#039;, &#039;/mod/choice/index.php?id=$1&#039;, &#039;course&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the restore log rules that will be applied&lt;br /&gt;
     * by the {@link restore_logs_processor} when restoring&lt;br /&gt;
     * choice logs. It must return one array&lt;br /&gt;
     * of {@link restore_log_rule} objects&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_restore_log_rules() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;add&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;update&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;choose&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;choose again&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;report&#039;, &#039;report.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the restore log rules that will be applied&lt;br /&gt;
     * by the {@link restore_logs_processor} when restoring&lt;br /&gt;
     * course logs. It must return one array&lt;br /&gt;
     * of {@link restore_log_rule} objects&lt;br /&gt;
     *&lt;br /&gt;
     * Note this rules are applied when restoring course logs&lt;br /&gt;
     * by the restore final task, but are defined here at&lt;br /&gt;
     * activity level. All them are rules not linked to any module instance (cmid = 0)&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_restore_log_rules_for_course() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        // Fix old wrong uses (missing extension)&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view all&#039;, &#039;index?id={course}&#039;, null,&lt;br /&gt;
                                        null, null, &#039;index.php?id={course}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view all&#039;, &#039;index.php?id={course}&#039;, null);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;define_restore_log_rules&#039;&#039;&#039;: it covers all the &amp;quot;mandatory&amp;quot; log actions, performing {course_module} and {choice} mappings properly. Note that names between curly brackets instruct the log processor to look in the mapping tables for item = name between brackets. Of course, other modules like forum, has a lot of rules, with more complex mappings and so on. Choice is pretty basic.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;define_restore_log_rules_for_course&#039;&#039;&#039;: choice module has the 1st rule because, while implementing restore, we discovered one bug causing URLs to be written in the logs table WITHOUT the .php in the URL, hence choice has that extra rule (that uses the 5-8 params, to REWRITE the URL to be inserted on restore). But that was one problem with choices only, afaik, so you probably would not need it in your own module.&lt;br /&gt;
&lt;br /&gt;
== Automatically triggering restore in code ==&lt;br /&gt;
&lt;br /&gt;
As with backup it is possible to automatically trigger a restore for a course (or activity or anything else you can back up). Here is how to restore a course:&lt;br /&gt;
&lt;br /&gt;
# Put the backup files in a specific folder: $CFG-&amp;gt;dataroot/temp/backup/$folder, where $folder is a random or unused unique id.&lt;br /&gt;
# Decide on the course shortname, fullname, and category id for restore (course must not already exist).&lt;br /&gt;
# Run the following code:&lt;br /&gt;
&lt;br /&gt;
        // Transaction&lt;br /&gt;
        $transaction = $DB-&amp;gt;start_delegated_transaction();&lt;br /&gt;
 &lt;br /&gt;
        // Create new course&lt;br /&gt;
        $courseid = restore_dbops::create_new_course($fullname, $shortname, $categoryid);&lt;br /&gt;
 &lt;br /&gt;
        // Restore backup into course&lt;br /&gt;
        $controller = new restore_controller($folder, $courseid, &lt;br /&gt;
                backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER-&amp;gt;id,&lt;br /&gt;
                backup::TARGET_NEW_COURSE);&lt;br /&gt;
        $controller-&amp;gt;execute_precheck();&lt;br /&gt;
        $controller-&amp;gt;execute_plan();&lt;br /&gt;
 &lt;br /&gt;
        // Commit&lt;br /&gt;
        $transaction-&amp;gt;allow_commit();&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
&lt;br /&gt;
* The transaction is used so that if backup fails, the course isn&#039;t created.&lt;br /&gt;
* The MODE_SAMESITE option is used if restoring a backup which comes from the same site, otherwise MODE_GENERAL might be used.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Restore_2.0_for_developers&amp;diff=82139</id>
		<title>Development:Restore 2.0 for developers</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Restore_2.0_for_developers&amp;diff=82139"/>
		<updated>2011-03-21T14:06:24Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Development:Backup 2.0}}{{Moodle_2.0}}{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
This page tries to explain, from a development perspective, &#039;&#039;&#039;how to implement&#039;&#039;&#039; the restore feature for various Moodle 2.x plugins, mainly, modules and blocks. &lt;br /&gt;
&lt;br /&gt;
In order to work with Restore 2.0 you must read the [https://docs.moodle.org/en/Development:Backup_2.0_for_developers Backup 2.0 documentation]. Everything will be crystal clear once you have read it. &lt;br /&gt;
&lt;br /&gt;
In this documentation you will learn about some points that you can not easily deduce from [https://docs.moodle.org/en/Development:Backup_2.0_for_developers Backup 2.0 documentation] concerning the Choice module.&lt;br /&gt;
&lt;br /&gt;
== Coding ==&lt;br /&gt;
the restore is composed by two main php files: restore_choice_stepslib.php and restore_choice_activity_task.class.php&lt;br /&gt;
=== restore_choice_stepslib.php ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Structure step to restore one choice activity&lt;br /&gt;
 */&lt;br /&gt;
class restore_choice_activity_structure_step extends restore_activity_structure_step {&lt;br /&gt;
&lt;br /&gt;
    protected function define_structure() {&lt;br /&gt;
&lt;br /&gt;
        $paths = array();&lt;br /&gt;
        $userinfo = $this-&amp;gt;get_setting_value(&#039;userinfo&#039;);&lt;br /&gt;
&lt;br /&gt;
        $paths[] = new restore_path_element(&#039;choice&#039;, &#039;/activity/choice&#039;);&lt;br /&gt;
        $paths[] = new restore_path_element(&#039;choice_option&#039;, &#039;/activity/choice/options/option&#039;);&lt;br /&gt;
        if ($userinfo) {&lt;br /&gt;
            $paths[] = new restore_path_element(&#039;choice_answer&#039;, &#039;/activity/choice/answers/answer&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Return the paths wrapped into standard activity structure&lt;br /&gt;
        return $this-&amp;gt;prepare_activity_structure($paths);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
        $data-&amp;gt;course = $this-&amp;gt;get_courseid();&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;timeopen = $this-&amp;gt;apply_date_offset($data-&amp;gt;timeopen);&lt;br /&gt;
        $data-&amp;gt;timeclose = $this-&amp;gt;apply_date_offset($data-&amp;gt;timeclose);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        // insert the choice record&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice&#039;, $data);&lt;br /&gt;
        // immediately after inserting &amp;quot;activity&amp;quot; record, call this&lt;br /&gt;
        $this-&amp;gt;apply_activity_instance($newitemid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice_option($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;choiceid = $this-&amp;gt;get_new_parentid(&#039;choice&#039;);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice_options&#039;, $data);&lt;br /&gt;
        $this-&amp;gt;set_mapping(&#039;choice_option&#039;, $oldid, $newitemid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function process_choice_answer($data) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $data = (object)$data;&lt;br /&gt;
        $oldid = $data-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
        $data-&amp;gt;choiceid = $this-&amp;gt;get_new_parentid(&#039;choice&#039;);&lt;br /&gt;
        $data-&amp;gt;optionid = $this-&amp;gt;get_mappingid(&#039;choice_option&#039;, $oldid);&lt;br /&gt;
        $data-&amp;gt;userid = $this-&amp;gt;get_mappingid(&#039;user&#039;, $data-&amp;gt;userid);&lt;br /&gt;
        $data-&amp;gt;timemodified = $this-&amp;gt;apply_date_offset($data-&amp;gt;timemodified);&lt;br /&gt;
&lt;br /&gt;
        $newitemid = $DB-&amp;gt;insert_record(&#039;choice_answers&#039;, $data);&lt;br /&gt;
        // No need to save this mapping as far as nothing depend on it&lt;br /&gt;
        // (child paths, file areas nor links decoder)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    protected function after_execute() {&lt;br /&gt;
        // Add choice related files, no need to match by itemname (just internally handled context)&lt;br /&gt;
        $this-&amp;gt;add_related_files(&#039;mod_choice&#039;, &#039;intro&#039;, null);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== restore_choice_activity_task.class.php ===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * choice restore task that provides all the settings and steps to perform one&lt;br /&gt;
 * complete restore of the activity&lt;br /&gt;
 */&lt;br /&gt;
class restore_choice_activity_task extends restore_activity_task {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define (add) particular settings this activity can have&lt;br /&gt;
     */&lt;br /&gt;
    protected function define_my_settings() {&lt;br /&gt;
        // No particular settings for this activity&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define (add) particular steps this activity can have&lt;br /&gt;
     */&lt;br /&gt;
    protected function define_my_steps() {&lt;br /&gt;
        // Choice only has one structure step&lt;br /&gt;
        $this-&amp;gt;add_step(new restore_choice_activity_structure_step(&#039;choice_structure&#039;, &#039;choice.xml&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the contents in the activity that must be&lt;br /&gt;
     * processed by the link decoder&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_decode_contents() {&lt;br /&gt;
        $contents = array();&lt;br /&gt;
&lt;br /&gt;
        $contents[] = new restore_decode_content(&#039;choice&#039;, array(&#039;intro&#039;), &#039;choice&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $contents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the decoding rules for links belonging&lt;br /&gt;
     * to the activity to be executed by the link decoder&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_decode_rules() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        $rules[] = new restore_decode_rule(&#039;CHOICEVIEWBYID&#039;, &#039;/mod/choice/view.php?id=$1&#039;, &#039;course_module&#039;);&lt;br /&gt;
        $rules[] = new restore_decode_rule(&#039;CHOICEINDEX&#039;, &#039;/mod/choice/index.php?id=$1&#039;, &#039;course&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the restore log rules that will be applied&lt;br /&gt;
     * by the {@link restore_logs_processor} when restoring&lt;br /&gt;
     * choice logs. It must return one array&lt;br /&gt;
     * of {@link restore_log_rule} objects&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_restore_log_rules() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;add&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;update&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;choose&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;choose again&#039;, &#039;view.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;report&#039;, &#039;report.php?id={course_module}&#039;, &#039;{choice}&#039;);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the restore log rules that will be applied&lt;br /&gt;
     * by the {@link restore_logs_processor} when restoring&lt;br /&gt;
     * course logs. It must return one array&lt;br /&gt;
     * of {@link restore_log_rule} objects&lt;br /&gt;
     *&lt;br /&gt;
     * Note this rules are applied when restoring course logs&lt;br /&gt;
     * by the restore final task, but are defined here at&lt;br /&gt;
     * activity level. All them are rules not linked to any module instance (cmid = 0)&lt;br /&gt;
     */&lt;br /&gt;
    static public function define_restore_log_rules_for_course() {&lt;br /&gt;
        $rules = array();&lt;br /&gt;
&lt;br /&gt;
        // Fix old wrong uses (missing extension)&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view all&#039;, &#039;index?id={course}&#039;, null,&lt;br /&gt;
                                        null, null, &#039;index.php?id={course}&#039;);&lt;br /&gt;
        $rules[] = new restore_log_rule(&#039;choice&#039;, &#039;view all&#039;, &#039;index.php?id={course}&#039;, null);&lt;br /&gt;
&lt;br /&gt;
        return $rules;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;define_restore_log_rules&#039;&#039;&#039;: it covers all the &amp;quot;mandatory&amp;quot; log actions, performing {course_module} and {choice} mappings properly. Note that names between curly brackets instruct the log processor to look in the mapping tables for item = name between brackets. Of course, other modules like forum, has a lot of rules, with more complex mappings and so on. Choice is pretty basic.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;define_restore_log_rules_for_course&#039;&#039;&#039;: choice module has the 1st rule because, while implementing restore, we discovered one bug causing URLs to be written in the logs table WITHOUT the .php in the URL, hence choice has that extra rule (that uses the 5-8 params, to REWRITE the URL to be inserted on restore). But that was one problem with choices only, afaik, so you probably would not need it in your own module.&lt;br /&gt;
&lt;br /&gt;
== Automatically triggering restore in code ==&lt;br /&gt;
&lt;br /&gt;
As with backup it is possible to automatically trigger a restore for a course (or activity or anything else you can back up). Here is how to restore a course:&lt;br /&gt;
&lt;br /&gt;
# Put the backup files in a specific folder: $CFG-&amp;gt;dataroot/temp/backup/$folder, where $folder is a random or unused unique id.&lt;br /&gt;
# Decide on the course shortname, fullname, and category id for restore (course must not already exist).&lt;br /&gt;
# Run the following code:&lt;br /&gt;
&lt;br /&gt;
        // Transaction&lt;br /&gt;
        $transaction = $DB-&amp;gt;start_delegated_transaction();&lt;br /&gt;
&lt;br /&gt;
        // Create new course&lt;br /&gt;
        $courseid = restore_dbops::create_new_course($fullname, $shortname, $categoryid);&lt;br /&gt;
&lt;br /&gt;
        // Restore backup into course&lt;br /&gt;
        $controller = new restore_controller($folder, $courseid, &lt;br /&gt;
                backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER-&amp;gt;id,&lt;br /&gt;
                backup::TARGET_NEW_COURSE);&lt;br /&gt;
        $controller-&amp;gt;execute_precheck();&lt;br /&gt;
        $controller-&amp;gt;execute_plan();&lt;br /&gt;
&lt;br /&gt;
        // Commit&lt;br /&gt;
        $transaction-&amp;gt;allow_commit();&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
&lt;br /&gt;
* The transaction is used so that if backup fails, the course isn&#039;t created.&lt;br /&gt;
* The MODE_SAMESITE option is used if restoring a backup which comes from the same site, otherwise MODE_GENERAL might be used.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Themes_2.0&amp;diff=81949</id>
		<title>Development:Themes 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Themes_2.0&amp;diff=81949"/>
		<updated>2011-03-15T13:15:06Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Unobvious Things */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Themes}}{{Moodle 2.0}}Welcome to the new world of themes within Moodle 2.0!&lt;br /&gt;
&lt;br /&gt;
This document explains how themes work in Moodle and is intended to help you create or modify most themes for Moodle 2.0.&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
&lt;br /&gt;
Moodle themes are responsible for much of the &amp;quot;look&amp;quot; of a Moodle site.  They provide the CSS for colours, layouts, fonts and so on, and can also change the structural XHTML code below that.  &lt;br /&gt;
&lt;br /&gt;
A theme can either contain all the definitions (completely stand alone) or it can extend an existing theme with one or more customizations. &lt;br /&gt;
&lt;br /&gt;
Most theme developers use the second method and simply add a few new CSS or layout definitions to their new theme. If a definition or element is not found in the new theme, it looks to the &amp;quot;parent&amp;quot; (or up the hierarchy of themes) to find one.  It an easy way to achieve just about any look you want for a theme.&lt;br /&gt;
&lt;br /&gt;
==What&#039;s new in 2.0==&lt;br /&gt;
&lt;br /&gt;
The theme system was completely redesigned in Moodle 2.0.  Known issues have been addressed and new features have been added to meet community requests.&lt;br /&gt;
&lt;br /&gt;
Unfortunately it was not possible to maintain backward compatibility, so all Moodle 1.x themes need to be recreated for Moodle 2.0.&lt;br /&gt;
&lt;br /&gt;
Major changes include:&lt;br /&gt;
* Clearer and more consistent CSS classes and IDs throughout all pages in Moodle&lt;br /&gt;
* Introduction of layout files (templates) describing overall layout HTML for many different types of pages in Moodle.&lt;br /&gt;
* Introduction of renderers, which produce the smaller &amp;quot;parts&amp;quot; of a HTML page.  Advanced themes can choose to override these too if they choose.&lt;br /&gt;
* Introduction of standard methods for adding Javascript to themes.&lt;br /&gt;
* Easier control over icons and images in Moodle.&lt;br /&gt;
* The old &amp;quot;standard&amp;quot; theme has been split into two themes:&lt;br /&gt;
**&#039;&#039;&#039;base&#039;&#039;&#039; - contains absolutely basic layout, and&lt;br /&gt;
**&#039;&#039;&#039;standard&#039;&#039;&#039; - which adds CSS to the base theme to make it look like the old standard theme.&lt;br /&gt;
* Performance tuning: In normal production mode CSS files are combined into a single optimised file, and both CSS and JavaScript files are minimised to ensure there are no wasted connections or traffic.  Files are heavily cached, but also versioned, so that users never need to clear their caches.&lt;br /&gt;
&lt;br /&gt;
==The structure of a theme==&lt;br /&gt;
&lt;br /&gt;
Some important things to know when building good themes:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;config.php&#039;&#039;&#039; - this file is required in every theme.  It defines configuration settings and definitions required to make the theme work in Moodle. These include theme, file, region, default region and options. &lt;br /&gt;
# &#039;&#039;&#039;Layouts and layout files&#039;&#039;&#039; -  in config.php there is one definition for each page type (see [[#theme_layouts_table|Appendix A: Theme layouts]] for a list of over 12 types).  Each page type definition tells Moodle which layout file will be used, what block regions this page type should display and so on.  The layout file contains the HTML and the minimum PHP required to display basic structure of pages. (If you know Moodle 1.9, it&#039;s like a combination of header.html and footer.html).&lt;br /&gt;
# &#039;&#039;&#039;The base theme&#039;&#039;&#039; - is not intended to be used for production sites.  It sets up the simplest possible generic layout and includes only CSS essential to that layout &#039;&#039;or&#039;&#039; to Moodle as a whole.  It tries not to make any unnecessary rules and makes as few assumptions as possible.  It&#039;s the perfect base on which to start designing a theme, as there are very few colours, borders, margins, and alignments to override.  You can just start adding what you need.&lt;br /&gt;
&lt;br /&gt;
===Files and folders===&lt;br /&gt;
A theme&#039;s files are placed in a folder with under moodle/theme folder and have subfolders. They are laid out like this:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Directory&lt;br /&gt;
! File&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| /config.php&lt;br /&gt;
| Contains all of the configuration and definitions for each theme&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| /lib.php &lt;br /&gt;
| Contains speciality classes and functions that are used by theme&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| /renderers.php &lt;br /&gt;
| Contains any custom renderers for the theme.&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| /settings.php &lt;br /&gt;
| Contains custom theme settings. These local settings are defined by the theme allowing the theme user to easily alter something about the way it looks or operates. (eg a background colour, or a header image)&lt;br /&gt;
|-&lt;br /&gt;
| /javascript/ &lt;br /&gt;
| &lt;br /&gt;
| All specialty JavaScript files the theme requires should be located in here.&lt;br /&gt;
|-&lt;br /&gt;
| /lang/ &lt;br /&gt;
| &lt;br /&gt;
| Any special language files the theme requires should be located in here.&lt;br /&gt;
|-&lt;br /&gt;
| /layout/ &lt;br /&gt;
| &lt;br /&gt;
| Contains the layout files for the theme.&lt;br /&gt;
|-&lt;br /&gt;
| /pix/ &lt;br /&gt;
| &lt;br /&gt;
| Contains any images the theme makes use of either in CSS or in the layout files.&lt;br /&gt;
|-&lt;br /&gt;
|  /pix&lt;br /&gt;
| /favicon.ico &lt;br /&gt;
| The favicon to display for this theme.&lt;br /&gt;
|-&lt;br /&gt;
| /pix&lt;br /&gt;
| /screenshot.jpg &lt;br /&gt;
| A screenshot of the theme to be displayed in on the theme selection screen.&lt;br /&gt;
|-&lt;br /&gt;
| /style &lt;br /&gt;
| &lt;br /&gt;
| Default location for CSS files.&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|/*.css&lt;br /&gt;
|CSS files the theme requires&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There are also several other places that stylesheets can be included from (see the CSS how and why section below).&lt;br /&gt;
&lt;br /&gt;
==Theme options==&lt;br /&gt;
All theme options are set within the config.php file for the theme.  The settings that are most used are: parents, sheets, layouts, and javascripts. Have a look at the &#039;&#039;&#039;[[#theme_options_table|theme options table]]&#039;&#039;&#039; for a complete list of theme options which include lesser used specialised or advanced settings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Basic theme config example===&lt;br /&gt;
Lets have a look at a basic theme configuration file and the different bits that make it up:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$THEME-&amp;gt;name = &#039;newtheme&#039;;&lt;br /&gt;
&lt;br /&gt;
$THEME-&amp;gt;parents = array(&lt;br /&gt;
    &#039;base&#039;&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
$THEME-&amp;gt;sheets = array(&lt;br /&gt;
    &#039;admin&#039;,&lt;br /&gt;
    &#039;blocks&#039;,&lt;br /&gt;
    &#039;calendar&#039;,&lt;br /&gt;
    &#039;course&#039;,&lt;br /&gt;
    &#039;grade&#039;,&lt;br /&gt;
    &#039;message&#039;,&lt;br /&gt;
    &#039;question&#039;,&lt;br /&gt;
    &#039;user&#039;&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
$THEME-&amp;gt;layouts = array(&lt;br /&gt;
    &#039;base&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;theme&#039; =&amp;gt; &#039;newtheme&#039;,&lt;br /&gt;
        &#039;file&#039; =&amp;gt; &#039;general.php&#039;,&lt;br /&gt;
        &#039;regions&#039; =&amp;gt; array(),&lt;br /&gt;
    ),&lt;br /&gt;
    &#039;standard&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;theme&#039; =&amp;gt; &#039;newtheme&#039;,&lt;br /&gt;
        &#039;file&#039; =&amp;gt; &#039;general.php&#039;,&lt;br /&gt;
        &#039;regions&#039; =&amp;gt; array(&#039;side-pre&#039;, &#039;side-post&#039;),&lt;br /&gt;
        &#039;defaultregion&#039; =&amp;gt; &#039;side-post&#039;,&lt;br /&gt;
    )&lt;br /&gt;
    //.......&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
$THEME-&amp;gt;javascripts_footer = array(&lt;br /&gt;
    &#039;navigation&#039;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Basic theme example settings explained===&lt;br /&gt;
First up you will notice everything is added to $THEME. This is the theme&#039;s configuration object, it is created by Moodle using default settings and is then updated by whatever settings you add to it.&lt;br /&gt;
&lt;br /&gt;
The first setting, $THEME-&amp;gt;name is the theme&#039;s name. This should simply be whatever your theme&#039;s name is, most likely whatever you named your theme directory.&lt;br /&gt;
&lt;br /&gt;
$THEME-&amp;gt;parents defines the themes that the theme will extend. In this case it is extending only the base theme.&lt;br /&gt;
&lt;br /&gt;
After this is the $THEME-&amp;gt;sheets array containing the names of the CSS stylesheets to include for this theme. Note that it is just the name of the stylesheet and does not contain the directory or the file extension. Moodle assumes that the theme&#039;s stylesheets will be located in the styles directory of the theme and have .css as an extension.&lt;br /&gt;
&lt;br /&gt;
Next we see the $THEME-&amp;gt;layouts definition. In this example, two layouts have been defined to override the layouts from the base theme. For more information see the [[#Layouts|layouts]] section below.&lt;br /&gt;
&lt;br /&gt;
The final setting is to include a JavaScript file, as $THEME-&amp;gt;javascripts_footer. Much like stylesheets, you only need to provide the files name. Moodle will assume it is in your themes JavaScript directory and be a .js file.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;: When you first begin writing themes, make sure you take a look at the configuration files of the other themes that get shipped with Moodle. You will get a good picture of how everything works, and what is going on in a theme, simply by reading it and taking notice of what it is including or excluding.&lt;br /&gt;
&lt;br /&gt;
==CSS==&lt;br /&gt;
===Locations of CSS files===&lt;br /&gt;
First lets look at where CSS can be included from within Moodle:&lt;br /&gt;
; \theme\themename\styles\*.css : This is the default location for all of the stylesheets that are used by a theme and the place which should be used by a theme designer.&lt;br /&gt;
&lt;br /&gt;
New theme developers should note that the order in which CSS files are found and included creates a hierarchy.  This order ensures that the rules, within a theme&#039;s style sheets, take precedence over identical rules in other files that may have been introduced before.  This can both extend another files definitions (see parent array in the config file) and also ensures that the current theme&#039;s CSS rules/definitions have the last say.&lt;br /&gt;
&lt;br /&gt;
There are other locations that can be used (although very rarely) to include CSS in a page. A developer of a php file can manually specify a stylesheet from anywhere within Moodle, like the database. Usually, if code is doing this, it is because there is a non-theme config or plugin setting that contains information requires special CSS information.  As a theme designer you should be aware of, but not have to worry about, these locations of CSS files.  Here are some examples:&lt;br /&gt;
&lt;br /&gt;
; {pluginpath}\styles.css e.g. \block\blockname\styles.css or \mod\modname\styles.css : Every plugin can have its own styles.css file. This file should only contain the required CSS rules for the module and should not add anything to the look of the plugin such as colours, font sizes, or margins other than those that are truly required.&amp;lt;br /&amp;gt;Theme specific styles for a plugin should be located within the themes styles directory.&lt;br /&gt;
; {pluginpath}\styles_themename.css : This should only ever be used by plugin developers. It allows them to write CSS that is designed for a specific theme without having to make changes to that theme. You will notice that this is never used within Moodle and is designed to be used only by contributed code.&lt;br /&gt;
&lt;br /&gt;
As theme designers, we will only use the first method of introducing CSS: adding rules to a stylesheet file located in the themes styles directory.&lt;br /&gt;
&lt;br /&gt;
===Moodle&#039;s core CSS organisation===&lt;br /&gt;
The next thing to look at is the organisation of CSS and rules within a theme. Although as a theme designer it is entirely up to you as to how you create and organise your CSS. Please note that within the themes provided in the standard install by Moodle there is a very clear organisation of CSS.&lt;br /&gt;
&lt;br /&gt;
First is the  pagelayout.css file. This contains the CSS required to give the layouts their look and feel.  It doesn&#039;t contain any rules that affect the content generated by Moodle.&lt;br /&gt;
&lt;br /&gt;
Next is the core.css file. If you open up core you will notice that it contains all manner of general (usually simple) rules that don&#039;t relate to a specific section of Moodle but to Moodle as a whole.&lt;br /&gt;
&lt;br /&gt;
There can also be rules that relate to specific sections.  However, this is done only when there are only a handful of rules for that section. These small clusters of rules are grouped together and separated by comments identifying for which section each relates.&lt;br /&gt;
&lt;br /&gt;
Finally there are all the other CSS files, you will notice that there is a file for each section of Moodle that has a significant collection of rules.&lt;br /&gt;
&lt;br /&gt;
:For those who are familiar with Moodle 1.9 theme&#039;s, this organisation will be a big change. In 1.9, CSS was organised by its nature (for example: colours, layout, other).&lt;br /&gt;
&lt;br /&gt;
===How to write effective CSS rules within Moodle===&lt;br /&gt;
In Moodle 2.0, writing good CSS rules is an incredibly important.&lt;br /&gt;
&lt;br /&gt;
Due to performance requirements and browser limitations, all of the CSS files are combined into a single CSS file that gets included every time. This means that rules need to be written in such a way as to minimise the chances of a collision leading to unwanted styles being applied. Whilst writing good CSS is something most designers strive for we have implemented several new body classes and put more emphasis on developers for using classes more appropriately.&lt;br /&gt;
&lt;br /&gt;
====The body tag====&lt;br /&gt;
As of Moodle 2.0 the ID tag that gets applied to the body will always be a representation of the URI. For example if you are looking at a forum posting and the URI is &#039;/mod/forum/view.php&#039; then the body tags ID will be &#039;#page-mod-forum-view&#039;.&lt;br /&gt;
&lt;br /&gt;
As well as the body&#039;s ID attribute the URI is also exploded to form several CSS classes that get added to the body tag, so in the above example &#039;/mod/forum/view&#039; you would end up with the following classes being added to the body tag &#039;.path-mod&#039;, &#039;.path-mod-forum&#039;. Note that &#039;.path-mod-forum-view&#039; is not added as a class, this is intentionally left out to lessen confusion and duplication as rules can relate directly to the page by using the ID and do not require the final class.&lt;br /&gt;
&lt;br /&gt;
The body ID and body classes described above will form the bread and butter for many of the CSS rules you will need to write for your theme, however there are also several other very handy classes that get added to the body tag that will be beneficial to you once you start your journey down the rabbit hole that is themeing. Some of the more interesting classes are listed below.&lt;br /&gt;
&lt;br /&gt;
* If JavaScript is enabled then &#039;jsenabled&#039; will be added as a class to the body tag allowing you to style based on JavaScript being enabled or not.&lt;br /&gt;
* Either &#039;dir-rtl&#039; or &#039;dir-ltr&#039; will be added to the body as a class depending on the direction of the language pack: rtl = right to left, ltr = left to right. This allows you to determine your text-alignment based on language if required.&lt;br /&gt;
* A class will be added to represent the language pack currently in use, by default en_utf8 is used by Moodle and will result in the class &#039;lang-en_utf8&#039; being added to the body tag.&lt;br /&gt;
* The wwwroot for Moodle will also be converted to a class and added to the body tag allowing you to stylise your theme based on the URL through which it was reached. e.g. http://sam.moodle.local/moodle/ will become &#039;.sam-moodle-local—moodle&#039;&lt;br /&gt;
* If the current user is not logged then &#039;.notloggedin&#039; will be added to the body tag.&lt;br /&gt;
&lt;br /&gt;
What does all of this look like in practise? Well using the above example /mod/forum/view.php you would get at least the following body tag:&lt;br /&gt;
&amp;lt;code html4strict&amp;gt;&amp;lt;body id=”page-mod-forum-view” class=”path-mod path-mod-forum” /&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Writing your rules====&lt;br /&gt;
The best use of body ids and classes and writing selectors will reduce problems.&lt;br /&gt;
&lt;br /&gt;
There are many specific classes used within Moodle. We try to put them everywhere where anyone may want to apply their own styles. It is important to recognise that no one developer can be aware of the all of the class names that have been used all throughout Moodle, let alone within all of the different contributed bits and pieces available for Moodle.  It is up to the theme developer to write good rules and minimise the chances of a collision between rules because in this case good CSS is FAR more effective.&lt;br /&gt;
&lt;br /&gt;
When starting to write rules make sure that you have a good understanding of where you want those rules to be applied, it is a good idea to make the most of the body classes mentioned above.&lt;br /&gt;
If you want to write a rule for a specific page make use of the body tag&#039;s ID, e.g.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code css&amp;gt;#page-mod-forum-view .forumpost {border:1px solid orange;)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want to write a rule that will be applied all throughout the forum.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code css&amp;gt;.path-mod-forum .forumpost {border:1px solid orange;}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The other very important thing to take into consideration is the structure leading up to the tag you want to style. Browsers apply conflicting styles with priority on the more specific selectors. It can be very beneficial to keep this in mind and write full selectors that rely on the structure of the tags leading to the tag you wish to style.&lt;br /&gt;
&lt;br /&gt;
By making use of body id&#039;s and classes and writing selectors to take into account the leading structure you can greatly minimise the chance of a collision both with Moodle now and in the future.&lt;br /&gt;
&lt;br /&gt;
==Layouts==&lt;br /&gt;
All themes are required to define the layouts they wish to be responsible for as well as create; however, many layout files are required by those layouts. If the theme is overriding another theme then it is a case of deciding which layouts this new theme should override. If the theme is a completely fresh start then you will need to define a layout for each of the different possibilities. For both situations these layouts should be defined within config.php.&lt;br /&gt;
&lt;br /&gt;
It is also important to note that a new theme that will base itself on another theme (overriding it) does not need to define any layouts or use any layout files if there are no changes that it wishes to make to the layouts of the existing theme. The standard theme in Moodle is a good example of this as it extends the base theme simply adding CSS to achieve its look and feel.&lt;br /&gt;
&lt;br /&gt;
So layouts... as mentioned earlier layouts are defined in config.php within $THEME-&amp;gt;layouts. The following is an example of one such layout definition:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$THEME-&amp;gt;layouts = array(&lt;br /&gt;
    // Standard layout with blocks, this is recommended for most pages with general information&lt;br /&gt;
    &#039;standard&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;theme&#039; =&amp;gt; &#039;base&#039;,&lt;br /&gt;
        &#039;file&#039; =&amp;gt; &#039;general.php&#039;,&lt;br /&gt;
        &#039;regions&#039; =&amp;gt; array(&#039;side-pre&#039;, &#039;side-post&#039;),&lt;br /&gt;
        &#039;defaultregion&#039; =&amp;gt; &#039;side-post&#039;&lt;br /&gt;
    )&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The first thing Moodle looks at is the name of the layout, in this case it is `standard` (the array key in PHP), it then looks at the settings for the layout, this is the theme, file, regions, and default region. There are also a couple of other options that can be set by a layout.&lt;br /&gt;
&lt;br /&gt;
; theme : is the theme the layout file exists in. That&#039;s right you can make use of layouts from other installed themes. &#039;&#039;Optional&#039;&#039;&lt;br /&gt;
; file : is the name of the layout file this layout wants to use. &#039;&#039;Required&#039;&#039;&lt;br /&gt;
; regions : is the different block regions (places you can put blocks) within the theme. &#039;&#039;Required&#039;&#039;&lt;br /&gt;
; defaultregion : is the default location when adding new blocks. &#039;&#039;&#039;Required if regions is non-empty, otherwise optional&#039;&#039;&#039;&lt;br /&gt;
; options : an array of layout specific options described in detail below. &#039;&#039;&#039;Optional&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;theme&#039;&#039;&#039; is optional. Normally the the layout file is looked for in the current theme, or, if it is not there, in the parent theme. However, you can use a layout file from any other theme by giving the theme name here.&lt;br /&gt;
&lt;br /&gt;
You can define whatever regions you like. You just need to pick an name for each one. Most themes just use one or both of &#039;&#039;&#039;side_pre&#039;&#039;&#039; and &#039;&#039;&#039;side_post&#039;&#039;&#039;, which is like &#039;left side&#039; and &#039;right side&#039;, except in right to left languages, when they are reversed. If you say in config.php that your the layout provides regions called &#039;fred&#039; and &#039;barney&#039;, then you must call $OUTPUT-&amp;gt;blocks_for_region(&#039;fred&#039;) and $OUTPUT-&amp;gt;blocks_for_region(&#039;barney&#039;) somewhere in the layout file.&lt;br /&gt;
&lt;br /&gt;
The final setting &#039;&#039;&#039;options&#039;&#039;&#039; is a special case that only needs to be set if you want to make use of it. This setting allows the theme designer to specify special options that they would like to create that can be later accessed within the layout file. This allows the theme to make design decisions during the definition and react upon those decisions in what ever layout file is being used.&lt;br /&gt;
&lt;br /&gt;
One such place this has been used is infact within the base theme. If you take a look first at theme/base/config.php you will notice that several layouts specify options &#039;&#039;&#039;nonavbar&#039;&#039;&#039; and &#039;&#039;&#039;nofooter&#039;&#039;&#039; which can both be set to either true or false. Then if we take a look at theme/base/layout/general.php you will spot lines like the following:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$hasnavbar = (empty($PAGE-&amp;gt;layout_options[&#039;nonavbar&#039;]) &amp;amp;&amp;amp; $PAGE-&amp;gt;has_navbar());&lt;br /&gt;
$hasfooter = (empty($PAGE-&amp;gt;layout_options[&#039;nofooter&#039;]));&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
............&lt;br /&gt;
&amp;lt;?php if ($hasnavbar) { ?&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;navbar clearfix&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;breadcrumb&amp;quot;&amp;gt;&amp;lt;?php echo $OUTPUT-&amp;gt;navbar(); ?&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;navbutton&amp;quot;&amp;gt; &amp;lt;?php echo $PAGE-&amp;gt;button; ?&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
What you are seeing here is the use of those settings from the layout within the layout file. In this case it is being used to toggle the display of the navigation bar and page footer.&lt;br /&gt;
&lt;br /&gt;
==Layout files==&lt;br /&gt;
A layout file is a file that contains the core HTML structure for a layout including the header, footer, content and block regions.&lt;br /&gt;
For those of you who are familiar with themes in Moodle 1.9 this is simply header.html and footer.html combined.&lt;br /&gt;
Of course it is not all HTML, there are bits of HTML and content that Moodle needs to put into the page, within each layout file this will be done by a couple of VERY simple PHP calls to get bits and pieces including content.&lt;br /&gt;
&lt;br /&gt;
The following is a very simple layout file to illustrate the different bits that make it up:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php echo $OUTPUT-&amp;gt;doctype() ?&amp;gt;&lt;br /&gt;
&amp;lt;html &amp;lt;?php echo $OUTPUT-&amp;gt;htmlattributes() ?&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;&amp;lt;?php echo $PAGE-&amp;gt;title ?&amp;gt;&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;?php echo $OUTPUT-&amp;gt;standard_head_html() ?&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body id=&amp;quot;&amp;lt;?php echo $PAGE-&amp;gt;bodyid ?&amp;gt;&amp;quot; class=&amp;quot;&amp;lt;?php echo $PAGE-&amp;gt;bodyclasses; ?&amp;gt;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo $OUTPUT-&amp;gt;standard_top_of_body_html() ?&amp;gt;&lt;br /&gt;
&amp;lt;table id=&amp;quot;page&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;tr&amp;gt;&lt;br /&gt;
        &amp;lt;td colspan=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;h1 class=&amp;quot;headermain&amp;quot;&amp;gt;&amp;lt;?php echo $PAGE-&amp;gt;heading ?&amp;gt;&amp;lt;/h1&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;headermenu&amp;quot;&amp;gt;&amp;lt;?php echo $OUTPUT-&amp;gt;login_info(); echo $PAGE-&amp;gt;headingmenu; ?&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;/tr&amp;gt;&lt;br /&gt;
    &amp;lt;tr&amp;gt;&lt;br /&gt;
        &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;?php echo $OUTPUT-&amp;gt;blocks_for_region(&#039;side-pre&#039;) ?&amp;gt;&lt;br /&gt;
        &amp;lt;/td&amp;gt;&lt;br /&gt;
        &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;?php echo core_renderer::MAIN_CONTENT_TOKEN ?&amp;gt;&lt;br /&gt;
        &amp;lt;/td&amp;gt;&lt;br /&gt;
        &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;?php echo $OUTPUT-&amp;gt;blocks_for_region(&#039;side-post&#039;) ?&amp;gt;&lt;br /&gt;
        &amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;/tr&amp;gt;&lt;br /&gt;
    &amp;lt;tr&amp;gt;&lt;br /&gt;
        &amp;lt;td colspan=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;p class=&amp;quot;helplink&amp;quot;&amp;gt;&amp;lt;?php echo page_doc_link(get_string(&#039;moodledocslink&#039;)) ?&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;lt;?php&lt;br /&gt;
            echo $OUTPUT-&amp;gt;login_info();&lt;br /&gt;
            echo $OUTPUT-&amp;gt;home_link();&lt;br /&gt;
            echo $OUTPUT-&amp;gt;standard_footer_html();&lt;br /&gt;
            ?&amp;gt;&lt;br /&gt;
        &amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;?php echo $OUTPUT-&amp;gt;standard_end_of_body_html() ?&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lets assume you know a enough HTML to understand the basic structure above, what you probably don&#039;t understand are the PHP calls so lets look at them.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php echo $OUTPUT-&amp;gt;doctype() ?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
This occurs at the VERY top of the page, it must be the first bit of output and is responsible for adding the (X)HTML document type definition to the page. This of course is determined by the settings of the site and is one of the things that the theme designer has no control over.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;html &amp;lt;?php echo $OUTPUT-&amp;gt;htmlattributes() ?&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Here we have started writing the opening html tag and have asked Moodle to give us the HTML attributes that should be applied to it. This again is determined by several settings within the actual HTML install.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php echo $PAGE-&amp;gt;title ?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
This gets us the title for the page.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php echo $OUTPUT-&amp;gt;standard_head_html() ?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
This very important call gets us the standard head HTML that needs to be within the HEAD tag of the page. This is where CSS and JavaScript requirements for the top of the page will be output as well as any special script or style tags.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;body id=&amp;quot;&amp;lt;?php echo $PAGE-&amp;gt;bodyid; ?&amp;gt;&amp;quot; class=&amp;quot;&amp;lt;?php echo $PAGE-&amp;gt;bodyclasses; ?&amp;gt;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Much like the html tag above we have started writing the body tag and have asked for Moodle to get us the desired ID and classes that should be applied to it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;h1 class=&amp;quot;headermain&amp;quot;&amp;gt;&amp;lt;?php echo $PAGE-&amp;gt;heading; ?&amp;gt;&amp;lt;/h1&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;headermenu&amp;quot;&amp;gt;&amp;lt;?php echo $OUTPUT-&amp;gt;login_info(); echo $PAGE-&amp;gt;headingmenu; ?&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Here we are creating the header for the page. In this case we want the heading for the page, we want to display the login information which will be the current users username or a link to log in if they are not logged in, and we want the heading menu if there is one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php echo $OUTPUT-&amp;gt;blocks_for_region(&#039;side-pre&#039;) ?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Here we get the HTML to display the blocks that have been added to the page. In this case we have asked for all blocks that have been added to the area labelled &#039;&#039;side-pre&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php echo core_renderer::MAIN_CONTENT_TOKEN ?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
This is one of the most important calls within the file, it determines where the actual content for the page gets inserted.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php echo $OUTPUT-&amp;gt;blocks_for_region(&#039;side-post&#039;) ?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Here we get the HTML to display the blocks that have been added to the page. In this case we have asked for all blocks that have been added to the area labelled &#039;&#039;side-post&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
echo $OUTPUT-&amp;gt;login_info();&lt;br /&gt;
echo $OUTPUT-&amp;gt;home_link();&lt;br /&gt;
echo $OUTPUT-&amp;gt;standard_footer_html();&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
This final bit of code gets the content for the footer of the page. It gets the login information which is the same as in the header, a home link, and the standard footer HTML which like the standard head HTML contains all of the script and style tags required by the page and requested to go in the footer.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Note&#039;&#039;&#039;&#039;&#039;: Within Moodle 2.0 most of the JavaScript for the page will be included in the footer. This greatly helps reduce the loading time of the page.&lt;br /&gt;
&lt;br /&gt;
When writing layout files think about the different layouts and how the HTML that each makes use of will differ. You will most likely find you do not need a different layout file for each layout, most likely you will be able to reuse the layout files you create across several layouts. You can of course make use of layout options as well to further reduce the number of layout files you need to produce.&lt;br /&gt;
&lt;br /&gt;
Of course as mentioned above if you are customising an existing theme then you may not need to create any layouts or layout files at all.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$OUTPUT&#039;&#039;&#039; is an instance of the &#039;&#039;&#039;core_renderer&#039;&#039;&#039; class which is defined in lib/outputrenderers.php. Each method is clearly documented there, along with which is appropriate for use within the layout files.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$PAGE&#039;&#039;&#039; is an instance of the &#039;&#039;&#039;moodle_page&#039;&#039;&#039; class defined in lib/pagelib.php. Most of the things you will want to use are the properties that are all documented at the top of the file. If you are not familiar with PHP properties, you access them like $PAGE-&amp;gt;activityname, just like fields of an ordinary PHP object. (However, behind the scenes, some magic is going on, and the value you get is produced by calling a function. Also, you cannot change these values, they are read-only. However, you don&#039;t need to understand all that if you are just using these properties in your theme.)&lt;br /&gt;
&lt;br /&gt;
==Making use of images==&lt;br /&gt;
Right at the start when listing the features of the new themes system one of the features mentioned was the ability to override any of the standard images within Moodle from within your theme. At this point we will look at both how to make use of your own images within your theme, and secondly how to override the images being used by Moodle.&lt;br /&gt;
So first up a bit about images within Moodle,&lt;br /&gt;
&lt;br /&gt;
# Images you want to use within your theme &#039;&#039;&#039;need&#039;&#039;&#039; to be located within your theme&#039;s pix directory.&lt;br /&gt;
# You can use sub directories within the pix directory of your theme.&lt;br /&gt;
# Images used by Moodle&#039;s core are located within the pix directory of Moodle.&lt;br /&gt;
# Modules, blocks and other plugins should also store there images within a pix directory.&lt;br /&gt;
&lt;br /&gt;
So making use of your own images first up. Lets assume you have added two image files to the pix directory of your theme.&lt;br /&gt;
&lt;br /&gt;
* /theme/yourthemename/pix/imageone.jpg&lt;br /&gt;
* /theme/yourthemename/pix/subdir/imagetwo.png&lt;br /&gt;
&lt;br /&gt;
Notice that one image is a JPEG image, and the second is a PNG. Also the second image is in a subdirectory.&lt;br /&gt;
&lt;br /&gt;
The following code snippet illustrates how to make use of your images within HTML, such as if you wanted to use them within a layout file.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;img src=&amp;quot;&amp;lt;?php echo $OUTPUT-&amp;gt;pix_url(&#039;imageone&#039;, &#039;theme&#039;);?&amp;gt;&amp;quot; alt=&amp;quot;&amp;quot; /&amp;gt; &lt;br /&gt;
&amp;lt;img src=&amp;quot;&amp;lt;?php echo $OUTPUT-&amp;gt;pix_url(&#039;subdir/imagetwo&#039;, &#039;theme&#039;);?&amp;gt;&amp;quot; alt=&amp;quot;&amp;quot; /&amp;gt; &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
In this case rather than writing out the URL to the image we use a method of Moodle&#039;s output library. Its not too important how that functions works but it is important that we use it as it is what allows images within Moodle to be over-rideable.&lt;br /&gt;
&lt;br /&gt;
The following is how you would use the images from within CSS as background images.&lt;br /&gt;
&amp;lt;code css&amp;gt;&lt;br /&gt;
.divone {background-image:url([[pix:theme|imageone]]);}&lt;br /&gt;
.divtwo {background-image:url([[pix:theme|subdir/imagetwo]]);}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
If this case we have to use some special notations that Moodle looks for. Whenever Moodle hands out a CSS file it first searches for all &#039;&#039;[[something]]&#039;&#039; tags and replaces them with what is required.&lt;br /&gt;
&lt;br /&gt;
The final thing to notice with both of the cases above is that at no point do we include the images file extension. &lt;br /&gt;
The reason for this leads us into the next topic, how to override images.&lt;br /&gt;
&lt;br /&gt;
From within a theme you can VERY easily override any standard image within Moodle by simply adding the replacement image to the theme&#039;s pix directory in the same sub directory structure as it is in Moodle.&lt;br /&gt;
So for instance we wanted to override the following two images:&lt;br /&gt;
# /pix/moodlelogo.gif&lt;br /&gt;
# /pix/i/user.gif&lt;br /&gt;
We would simply need to add our replacement images to the theme in the following locations&lt;br /&gt;
# /theme/themename/pix/moodlelogo.gif&lt;br /&gt;
# /theme/themename/pix/i/user.gif&lt;br /&gt;
How easy is that!&lt;br /&gt;
&lt;br /&gt;
Now the other very cool thing to mention is that Moodle looks for not just replacements of the same image type (jpg, gif, etc...) but also replacements in any image format. This is why above when working with our images we never specified the images file extension.&lt;br /&gt;
This means that the following would also work:&lt;br /&gt;
# /theme/themename/pix/moodlelogo.png&lt;br /&gt;
# /theme/themename/pix/i/user.bmp&lt;br /&gt;
&lt;br /&gt;
For a more detailed description of how this all works see the page on [[Development:Themes_2.0_How_to_use_images_within_your_theme|using images within your theme]]&lt;br /&gt;
&lt;br /&gt;
==Unobvious Things==&lt;br /&gt;
===Getting Your Theme to Appear Correctly in Theme Selector===&lt;br /&gt;
If you follow the examples on this page to the letter, when you go to the Theme Selector page you may be discouraged to find that your theme does not appear like the other themes do. In fact, instead of your theme&#039;s name, you will see something along the lines of &amp;lt;nowiki&amp;gt;[[pluginname]]&amp;lt;/nowiki&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
To correct this, you must add the /lang/en/theme_THEMENAME.php file, where THEMENAME is the name of the theme folder. Inside that file, add the string &amp;quot;$string[&#039;pluginname&#039;] = &#039;THEMENAME&#039;; &amp;quot;. Make THEMENAME the name of your theme, however you want it displayed in the Theme selector.&lt;br /&gt;
&lt;br /&gt;
The screenshot for the theme should be about 500x400 px.&lt;br /&gt;
&lt;br /&gt;
===Required theme divs===&lt;br /&gt;
&lt;br /&gt;
Some parts of Moodle may rely on particular divs, for example the div with id &#039;page-header&#039;.&lt;br /&gt;
&lt;br /&gt;
Consequently all themes must include at least the divs (with the same ids) that are present in the &#039;base&#039; theme. &lt;br /&gt;
&lt;br /&gt;
Missing out these elements may result in unexpected behaviour within specific modules or other plugins.&lt;br /&gt;
&lt;br /&gt;
==Appendix A==&lt;br /&gt;
===Theme options as of April 28th, 2010===&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot; id=&amp;quot;theme_options_table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting&lt;br /&gt;
! Effect&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;csspostprocess&#039;&#039;&#039;&lt;br /&gt;
|  Allows the user to provide the name of a function that all CSS should be passed to before being delivered.&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;editor_sheets&#039;&#039;&#039;&lt;br /&gt;
|  An array of stylesheets to include within the body of the editor.&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;enable_dock&#039;&#039;&#039;&lt;br /&gt;
|  If set to true the side dock is enabled for blocks&lt;br /&gt;
|-&lt;br /&gt;
| $THEME-&amp;gt;&#039;&#039;&#039;hidefromselector&#039;&#039;&#039;&lt;br /&gt;
| Used to hide a theme from the theme selector (unless theme designer mode is on). Accepts true or false.&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;filter_mediaplugin_colors&#039;&#039;&#039;&lt;br /&gt;
|  Used to control the colours used in the small media player for the filters&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;javascripts&#039;&#039;&#039;&lt;br /&gt;
|  An array containing the names of JavaScript files located in /javascript/ to include in the theme. (gets included in the head)&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;javascripts_footer&#039;&#039;&#039;&lt;br /&gt;
|  As above but will be included in the page footer.&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;larrow&#039;&#039;&#039;&lt;br /&gt;
|  Overrides the left arrow image used throughout Moodle&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;layouts&#039;&#039;&#039;&lt;br /&gt;
|  An array setting the layouts for the theme&lt;br /&gt;
|-&lt;br /&gt;
| $THEME-&amp;gt;&#039;&#039;&#039;name&#039;&#039;&#039;&lt;br /&gt;
| Name of the theme. Most likely the name of the directory in which this file resides.&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;parents&#039;&#039;&#039;&lt;br /&gt;
|  An array of themes to inherit from&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;parents_exclude_javascripts&#039;&#039;&#039;&lt;br /&gt;
|  An array of JavaScript files NOT to inherit from the themes parents&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;parents_exclude_sheets&#039;&#039;&#039;&lt;br /&gt;
|  An array of stylesheets not to inherit from the themes parents&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;plugins_exclude_sheets&#039;&#039;&#039;&lt;br /&gt;
|  An array of plugin sheets to ignore and not include.&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;rarrow&#039;&#039;&#039;&lt;br /&gt;
|  Overrides the right arrow image used throughout Moodle&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;renderfactory&#039;&#039;&#039;&lt;br /&gt;
|  Sets a custom render factory to use with the theme, used when working with custom renderers.&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;resource_mp3player_colors&#039;&#039;&#039;&lt;br /&gt;
|  Controls the colours for the MP3 player&lt;br /&gt;
|-&lt;br /&gt;
|  $THEME-&amp;gt;&#039;&#039;&#039;sheets&#039;&#039;&#039;&lt;br /&gt;
|  An array of stylesheets to include for this theme. Should be located in the theme&#039;s style directory.&lt;br /&gt;
|}&lt;br /&gt;
===The different layouts as of August 17th, 2010===&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot; id=&amp;quot;theme_layouts_table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Layout&lt;br /&gt;
! Purpose&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;
| Main course page.&lt;br /&gt;
|- &lt;br /&gt;
| coursecategory&lt;br /&gt;
| Use for browsing through course categories.&lt;br /&gt;
|- &lt;br /&gt;
| incourse&lt;br /&gt;
| Default layout while browsing a course, typical for modules.&lt;br /&gt;
|- &lt;br /&gt;
| frontpage&lt;br /&gt;
| The site home page.&lt;br /&gt;
|- &lt;br /&gt;
| admin&lt;br /&gt;
| Administration pages and scripts.&lt;br /&gt;
|- &lt;br /&gt;
| mydashboard&lt;br /&gt;
| My dashboard page.&lt;br /&gt;
|- &lt;br /&gt;
| mypublic&lt;br /&gt;
| My public page.&lt;br /&gt;
|- &lt;br /&gt;
| login&lt;br /&gt;
| The login page.&lt;br /&gt;
|-&lt;br /&gt;
| popup&lt;br /&gt;
| Pages that appear in pop-up windows - no navigation, no blocks, no header.&lt;br /&gt;
|-&lt;br /&gt;
| frametop&lt;br /&gt;
| Used for legacy frame layouts only. No blocks and minimal footer.&lt;br /&gt;
|-&lt;br /&gt;
| embedded&lt;br /&gt;
| Embeded pages, like iframe/object embedded in moodleform - it needs as much space as possible&lt;br /&gt;
|-&lt;br /&gt;
| maintenance&lt;br /&gt;
| Used during upgrade and install. This must not have any blocks, and it is good idea if it does not have links to other places - for example there should not be a home link in the footer.&lt;br /&gt;
|-&lt;br /&gt;
| print&lt;br /&gt;
| Used when the page is being displayed specifically for printing.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [http://www.youtube.com/watch?v=OvaU54uh-qA New themes in Moodle 2.0 video]&lt;br /&gt;
* [[Development:Themes 2.0 creating your first theme]] - A quick step by step guide to creating your first theme.&lt;br /&gt;
* [[Development:Themes 2.0 overriding a renderer]] - A tutorial on creating a custom renderer and changing the HTML Moodle produces.&lt;br /&gt;
* [[Development:Themes 2.0 How to use images within your theme]] - Explains how to use and override images within your theme.&lt;br /&gt;
* [[Development:Themes 2.0 adding a settings page]] - Looks at how to add a setting page making your theme easily customisable.&lt;br /&gt;
* [[Development:Themes 2.0 extending the custom menu]] - Customising the custom menu.&lt;br /&gt;
* [[Development:Styling and customising the dock]] - How to style and customise the dock.&lt;br /&gt;
* [[Development:Theme changes in 2.0]]&lt;br /&gt;
* [[Development:Using jQuery with Moodle 2.0]]&lt;br /&gt;
&lt;br /&gt;
[[de:Designs 2.0]]&lt;br /&gt;
[[es:Temas 2.0]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Backup_2.0_theme_data&amp;diff=81799</id>
		<title>Development:Backup 2.0 theme data</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Backup_2.0_theme_data&amp;diff=81799"/>
		<updated>2011-03-08T12:09:10Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Development:Backup 2.0}}{{Moodle_2.0}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Moodle 2, themes can have data tables and files which can be backed up with a course. (To create data tables in a theme, use the standard &amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; folder.)&lt;br /&gt;
&lt;br /&gt;
None of the standard themes have any course-related data so it is not obvious how to code the backup and restore for them. This page gives an example theme as a case study which you can use to implement your own backup code for a theme which contains per-course data.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note: Theme data backup requires Moodle 2.0.3 or later.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Example: the &#039;ou&#039; theme ==&lt;br /&gt;
&lt;br /&gt;
Our theme called the &#039;ou&#039; theme (i.e. in the folder theme/ou) contains the following per-course data which needs to be backed up:&lt;br /&gt;
&lt;br /&gt;
* Each course using the theme can select a different colour (referred to as &#039;variant&#039; of the theme). This is so we can have different courses in any of 10 different colours without having to make 10 different themes.&lt;br /&gt;
&lt;br /&gt;
* Each course can also select a custom image which is used in the header on some screens. This is so we can have cute kitten pictures on our test servers.&lt;br /&gt;
&lt;br /&gt;
The variant is stored in a table called &amp;lt;tt&amp;gt;theme_ou_courseoptions&amp;lt;/tt&amp;gt;. This table has 3 fields: &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;courseid&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;variant&amp;lt;/tt&amp;gt;. (If we ever need to add more theme data, we can add fields to it.)&lt;br /&gt;
&lt;br /&gt;
The image file is stored using the Moodle file API at the course context, in the &amp;lt;tt&amp;gt;theme_ou&amp;lt;/tt&amp;gt; component and the &amp;lt;tt&amp;gt;image&amp;lt;/tt&amp;gt; file area.&lt;br /&gt;
&lt;br /&gt;
=== Backup ===&lt;br /&gt;
&lt;br /&gt;
The backup code is in the file &amp;lt;tt&amp;gt;theme/ou/backup/moodle2/backup_theme_ou_plugin.class.php&amp;lt;/tt&amp;gt;. Leaving out the headers, this file looks like:&lt;br /&gt;
&lt;br /&gt;
 class backup_theme_ou_plugin extends backup_theme_plugin {&lt;br /&gt;
 &lt;br /&gt;
     /**&lt;br /&gt;
      * Returns the theme information to attach to course element&lt;br /&gt;
      */&lt;br /&gt;
     protected function define_course_plugin_structure() {&lt;br /&gt;
         // Define virtual plugin element&lt;br /&gt;
         $plugin = $this-&amp;gt;get_plugin_element(null, $this-&amp;gt;get_theme_condition(), &#039;ou&#039;);&lt;br /&gt;
 &lt;br /&gt;
         // Create plugin container element with standard name&lt;br /&gt;
         $pluginwrapper = new backup_nested_element($this-&amp;gt;get_recommended_name());&lt;br /&gt;
 &lt;br /&gt;
         // Add wrapper to plugin&lt;br /&gt;
         $plugin-&amp;gt;add_child($pluginwrapper);&lt;br /&gt;
 &lt;br /&gt;
         // Set up theme&#039;s own structure and add to wrapper&lt;br /&gt;
         $studyplan = new backup_nested_element(&#039;ou&#039;, array(&#039;id&#039;), array(&lt;br /&gt;
             &#039;variant&#039;));&lt;br /&gt;
         $pluginwrapper-&amp;gt;add_child($studyplan);&lt;br /&gt;
 &lt;br /&gt;
         // Use database to get source&lt;br /&gt;
         $studyplan-&amp;gt;set_source_table(&#039;theme_ou_courseoptions&#039;,&lt;br /&gt;
                 array(&#039;courseid&#039; =&amp;gt; backup::VAR_COURSEID));&lt;br /&gt;
 &lt;br /&gt;
         // Include files which have theme_ou and area image and no itemid&lt;br /&gt;
         $studyplan-&amp;gt;annotate_files(&#039;theme_ou&#039;, &#039;image&#039;, null);&lt;br /&gt;
 &lt;br /&gt;
         return $plugin;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
As you can see this works in a similar way to backup for modules and other plugins. The first line:&lt;br /&gt;
&lt;br /&gt;
 $this-&amp;gt;get_plugin_element(null, $this-&amp;gt;get_theme_condition(), &#039;ou&#039;);&lt;br /&gt;
&lt;br /&gt;
means that the theme data will only be backed up if the current theme for the course is actually the &#039;ou&#039; theme. In other words, if you change the course theme to &#039;standard&#039;, this backup data will not be created. (It is possible to change this behaviour if you want to backup theme data even when a different theme is currently selected for the course; leave out the last two parameters.)&lt;br /&gt;
&lt;br /&gt;
You can see how you would change the code to use a different theme name, database table, fields, or file area. It&#039;s also possible to add a more complicated data structured in the same way as for other plugins. You could even store user data - however, unless the user data is course-specific, it would probably be more appropriate to store user theme options in user preferences rather than within theme-managed tables.&lt;br /&gt;
&lt;br /&gt;
==== Testing ====&lt;br /&gt;
&lt;br /&gt;
To test the backup, carry it out and then look inside the resulting zip file. In the &amp;lt;tt&amp;gt;course&amp;lt;/tt&amp;gt; folder there should be a file called &amp;lt;tt&amp;gt;course.xml&amp;lt;/tt&amp;gt;. If theme data is backed up, it will include a section like the following:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;plugin_theme_ou_course&amp;gt;&lt;br /&gt;
  &amp;lt;ou id=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;variant&amp;gt;purple&amp;lt;/variant&amp;gt;&lt;br /&gt;
  &amp;lt;/ou&amp;gt;&lt;br /&gt;
 &amp;lt;/plugin_theme_ou_course&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, if the file backup worked, you should be able to find any files from within the specified file area inside the &#039;files&#039; section of the backup (it will help if you know the hash code of the file; otherwise, note the size and make sure the test course doesn&#039;t have many other files to look through).&lt;br /&gt;
&lt;br /&gt;
If you switch the course to a different theme, this data will not be backed up.&lt;br /&gt;
&lt;br /&gt;
=== Restore ===&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, restore code goes in the file &amp;lt;tt&amp;gt;theme/ou/backup/moodle2/restore_theme_ou_plugin.class.php&amp;lt;/tt&amp;gt;. Leaving out the headers, this file is:&lt;br /&gt;
&lt;br /&gt;
 class restore_theme_ou_plugin extends restore_theme_plugin {&lt;br /&gt;
 &lt;br /&gt;
     /**&lt;br /&gt;
      * Returns the paths to be handled by the plugin at course level&lt;br /&gt;
      */&lt;br /&gt;
     protected function define_course_plugin_structure() {&lt;br /&gt;
         $paths = array();&lt;br /&gt;
 &lt;br /&gt;
         // Because of using get_recommended_name() it is able to find the&lt;br /&gt;
         // correct path just by using the part inside the element name (which&lt;br /&gt;
         // only has a /ou element).&lt;br /&gt;
         $elepath = $this-&amp;gt;get_pathfor(&#039;/ou&#039;);&lt;br /&gt;
 &lt;br /&gt;
         // The &#039;ou&#039; here defines that it will use the process_ou function&lt;br /&gt;
         // to restore its element.&lt;br /&gt;
         $paths[] = new restore_path_element(&#039;ou&#039;, $elepath);&lt;br /&gt;
 &lt;br /&gt;
         return $paths;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     /**&lt;br /&gt;
      * Called after this runs for a course.&lt;br /&gt;
      */&lt;br /&gt;
     function after_execute_course() {&lt;br /&gt;
         // Need to restore file&lt;br /&gt;
         $this-&amp;gt;add_related_files(&#039;theme_ou&#039;, &#039;image&#039;, null);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     /**&lt;br /&gt;
      * Process the &#039;ou&#039; element&lt;br /&gt;
      */&lt;br /&gt;
     public function process_ou($data) {&lt;br /&gt;
         global $DB;&lt;br /&gt;
 &lt;br /&gt;
         // Get data record ready to insert in database&lt;br /&gt;
         $data = (object)$data;&lt;br /&gt;
         $data-&amp;gt;courseid = $this-&amp;gt;task-&amp;gt;get_courseid();&lt;br /&gt;
 &lt;br /&gt;
         // See if there is an existing record for this course&lt;br /&gt;
         $existingid = $DB-&amp;gt;get_field(&#039;theme_ou_courseoptions&#039;, &#039;id&#039;,&lt;br /&gt;
                 array(&#039;courseid&#039;=&amp;gt;$data-&amp;gt;courseid));&lt;br /&gt;
         if ($existingid) {&lt;br /&gt;
             $data-&amp;gt;id = $existingid;&lt;br /&gt;
             $DB-&amp;gt;update_record(&#039;theme_ou_courseoptions&#039;, data);&lt;br /&gt;
         } else {&lt;br /&gt;
             $DB-&amp;gt;insert_record(&#039;theme_ou_courseoptions&#039;, $data);&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         // No need to record the old/new id as nothing ever refers to&lt;br /&gt;
         // the id of this table.&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Again this is just the same as restoring a module. To change this to support your own theme, you would change the word &#039;ou&#039; to your own theme&#039;s name, change references to the &amp;lt;tt&amp;gt;theme_ou_courseoptions&amp;lt;/tt&amp;gt; table to your own data table (or otherwise change what exactly it does when restoring), and change the file area details in the &amp;lt;tt&amp;gt;add_related_files&amp;lt;/tt&amp;gt; call.&lt;br /&gt;
&lt;br /&gt;
Unlike backup, the restore code always runs if the data was included in the backup, even if the course actually has a different theme once it is restored (e.g. the site default theme has changed). This should not cause a particular problem; at worst you&#039;ll get a superfluous database row on rare occasions.&lt;br /&gt;
&lt;br /&gt;
==== Testing ====&lt;br /&gt;
&lt;br /&gt;
It is probably obvious how to test restore :) Once you&#039;ve verified that backup is producing the correct XML data, just check that restore recreates the necessary data for you. &lt;br /&gt;
&lt;br /&gt;
In our case, we know restore works if the restored course is purple and has a cute kitten picture.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Backup_2.0_theme_data&amp;diff=81798</id>
		<title>Development:Backup 2.0 theme data</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Backup_2.0_theme_data&amp;diff=81798"/>
		<updated>2011-03-08T12:03:52Z</updated>

		<summary type="html">&lt;p&gt;Quen: New page: {{Template:Development:Backup 2.0}}{{Moodle_2.0}}  == Introduction ==  In Moodle 2, themes can have data tables and files which can be backed up with a course. (To create data tables in a ...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Development:Backup 2.0}}{{Moodle_2.0}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Moodle 2, themes can have data tables and files which can be backed up with a course. (To create data tables in a theme, use the standard &amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; folder.)&lt;br /&gt;
&lt;br /&gt;
None of the standard themes have any course-related data so it is not obvious how to code the backup and restore for them. This page gives an example theme as a case study which you can use to implement your own backup code for a theme which contains per-course data.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note: Theme data backup requires Moodle 2.0.3 or later.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Example: the &#039;ou&#039; theme ==&lt;br /&gt;
&lt;br /&gt;
Our theme called the &#039;ou&#039; theme (i.e. in the folder theme/ou) contains the following per-course data which needs to be backed up:&lt;br /&gt;
&lt;br /&gt;
* Each course using the theme can select a different colour (referred to as &#039;variant&#039; of the theme). This is so we can have different courses in any of 10 different colours without having to make 10 different themes.&lt;br /&gt;
&lt;br /&gt;
* Each course can also select a custom image which is used in the header on some screens. This is so we can have cute kitten pictures on our test servers.&lt;br /&gt;
&lt;br /&gt;
The variant is stored in a table called &amp;lt;tt&amp;gt;theme_ou_courseoptions&amp;lt;/tt&amp;gt;. This table has 3 fields: &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;courseid&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;variant&amp;lt;/tt&amp;gt;. (If we ever need to add more theme data, we can add fields to it.)&lt;br /&gt;
&lt;br /&gt;
The image file is stored using the Moodle file API at the course context, in the &amp;lt;tt&amp;gt;theme_ou&amp;lt;/tt&amp;gt; component and the &amp;lt;tt&amp;gt;image&amp;lt;/tt&amp;gt; file area.&lt;br /&gt;
&lt;br /&gt;
=== Backup ===&lt;br /&gt;
&lt;br /&gt;
The backup code is in the file &amp;lt;tt&amp;gt;theme/ou/backup/moodle2/backup_theme_ou_plugin.class.php&amp;lt;/tt&amp;gt;. Leaving out the headers, this file looks like:&lt;br /&gt;
&lt;br /&gt;
 class backup_theme_ou_plugin extends backup_theme_plugin {&lt;br /&gt;
 &lt;br /&gt;
     /**&lt;br /&gt;
      * Returns the theme information to attach to course element&lt;br /&gt;
      */&lt;br /&gt;
     protected function define_course_plugin_structure() {&lt;br /&gt;
         // Define virtual plugin element&lt;br /&gt;
         $plugin = $this-&amp;gt;get_plugin_element(null, $this-&amp;gt;get_theme_condition(), &#039;ou&#039;);&lt;br /&gt;
 &lt;br /&gt;
         // Create plugin container element with standard name&lt;br /&gt;
         $pluginwrapper = new backup_nested_element($this-&amp;gt;get_recommended_name());&lt;br /&gt;
 &lt;br /&gt;
         // Add wrapper to plugin&lt;br /&gt;
         $plugin-&amp;gt;add_child($pluginwrapper);&lt;br /&gt;
 &lt;br /&gt;
         // Set up theme&#039;s own structure and add to wrapper&lt;br /&gt;
         $studyplan = new backup_nested_element(&#039;ou&#039;, array(&#039;id&#039;), array(&lt;br /&gt;
             &#039;variant&#039;));&lt;br /&gt;
         $pluginwrapper-&amp;gt;add_child($studyplan);&lt;br /&gt;
 &lt;br /&gt;
         // Use database to get source&lt;br /&gt;
         $studyplan-&amp;gt;set_source_table(&#039;theme_ou_courseoptions&#039;,&lt;br /&gt;
                 array(&#039;courseid&#039; =&amp;gt; backup::VAR_COURSEID));&lt;br /&gt;
 &lt;br /&gt;
         // Include files which have theme_ou and area image and no itemid&lt;br /&gt;
         $studyplan-&amp;gt;annotate_files(&#039;theme_ou&#039;, &#039;image&#039;, null);&lt;br /&gt;
 &lt;br /&gt;
         return $plugin;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
As you can see this works in a similar way to backup for modules and other plugins. The first line:&lt;br /&gt;
&lt;br /&gt;
 $this-&amp;gt;get_plugin_element(null, $this-&amp;gt;get_theme_condition(), &#039;ou&#039;);&lt;br /&gt;
&lt;br /&gt;
means that the theme data will only be backed up if the current theme for the course is actually the &#039;ou&#039; theme. In other words, if you change the course theme to &#039;standard&#039;, this backup data will not be created. (It is possible to change this behaviour if you want to backup theme data even when a different theme is currently selected for the course; leave out the last two parameters.)&lt;br /&gt;
&lt;br /&gt;
You can see how you would change the code to use a different theme name, database table, fields, or file area. It&#039;s also possible to add a more complicated data structured in the same way as for other plugins. You could even store user data - however, unless the user data is course-specific, it would probably be more appropriate to store user theme options in user preferences rather than within theme-managed tables.&lt;br /&gt;
&lt;br /&gt;
==== Testing ====&lt;br /&gt;
&lt;br /&gt;
To test the backup, carry it out and then look inside the resulting zip file. In the &amp;lt;tt&amp;gt;course&amp;lt;/tt&amp;gt; folder there should be a file called &amp;lt;tt&amp;gt;course.xml&amp;lt;/tt&amp;gt;. If theme data is backed up, it will include a section like the following:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;plugin_theme_ou_course&amp;gt;&lt;br /&gt;
  &amp;lt;ou id=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;variant&amp;gt;purple&amp;lt;/variant&amp;gt;&lt;br /&gt;
  &amp;lt;/ou&amp;gt;&lt;br /&gt;
 &amp;lt;/plugin_theme_ou_course&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, if the file backup worked, you should be able to find any files from within the specified file area inside the &#039;files&#039; section of the backup (it will help if you know the hash code of the file; otherwise, note the size and make sure the test course doesn&#039;t have many other files to look through).&lt;br /&gt;
&lt;br /&gt;
If you switch the course to a different theme, this data will not be backed up.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Backup_2.0_for_developers&amp;diff=81796</id>
		<title>Development:Backup 2.0 for developers</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Backup_2.0_for_developers&amp;diff=81796"/>
		<updated>2011-03-08T11:46:03Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Development:Backup 2.0}}{{Moodle_2.0}}{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
This page tries to explain, from a development perspective, &#039;&#039;&#039;how to implement&#039;&#039;&#039; the backup feature for various Moodle 2.x plugins, mainly, modules and blocks. &lt;br /&gt;
&lt;br /&gt;
Note that, at the time of writing this, the backup &amp;amp; restore subsystem itself is &#039;&#039;&#039;under development&#039;&#039;&#039;, so still there are some missing bits, specially about the way to handle &#039;&#039;&#039;subplugins&#039;&#039;&#039; (question types, data fields...) under the new backup infrastructure, so any module using such artifacts, like data, assignment, quiz, workshop, aren&#039;t  good candidates right now. This will be addressed (and this Docs updated) once we have determined how each subplugin is expected to work (from a DB / backup perspective).&lt;br /&gt;
&lt;br /&gt;
Everything in backup &#039;&#039;&#039;is about tasks and steps&#039;&#039;&#039; (see [[Development:Backup 2.0 general architecture|Backup 2.0 general architecture]] for more information), all them conforming one backup plan. Each module instance and each block instance will be backup by one backup task instance that is, basically, one collection of backup steps. How steps are organized within the task will dictate to the backup system what to do and in which order.&lt;br /&gt;
&lt;br /&gt;
Another important point is that Moodle backup 2.0, supports &#039;&#039;&#039;multiple backup formats&#039;&#039;&#039; (&amp;quot;moodle2&amp;quot;, &amp;quot;imscc&amp;quot;...) each one having its own tasks/steps, completely unrelated between them. In any case, for now, we are focusing all the explanations below in the &amp;quot;moodle2&amp;quot; format, that is the one required in order to keep any module/block &#039;&#039;&#039;transportable between Moodle instances&#039;&#039;&#039; without any loss of data. Surely, for the rest of formats, we&#039;ll end with other documents describing them, as far as each one can have its own particularities.&lt;br /&gt;
&lt;br /&gt;
Talking about backup steps we must differentiate &#039;&#039;&#039;two type of steps&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Execution steps&#039;&#039;&#039;: That, simply, execute arbitrary PHP code. They are useful to prepare different structures, create directories, whatever have to be done not involving the generation of XML files. Normally you won&#039;t need them.&lt;br /&gt;
* &#039;&#039;&#039;Structure steps&#039;&#039;&#039;: That, using one PHP API (detailed below) define completely the XML structure to be exported and its contents. Hopefully, &amp;quot;normal&amp;quot; modules only will have to use one step of this type in order to have the backup functionality 100% implemented.&lt;br /&gt;
&lt;br /&gt;
Said that, in the next steps we are going through all the steps necessary to create the backup of one simple module and one block, in order to get all the possibilities covered.&lt;br /&gt;
&lt;br /&gt;
== How to backup one module ==&lt;br /&gt;
&lt;br /&gt;
For this section, we have selected one simple module (choice) that requires practically all the backup &amp;quot;machinery&amp;quot; to be used, so it will get explained as we progress in the development. The only point not covered here are the &amp;quot;subplugin&amp;quot; facilities, covered later in a separate section.&lt;br /&gt;
&lt;br /&gt;
=== Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
In order to achieve the backup implementation for the module, some prerequisites should be fulfilled. They are just recommendations but, specially while getting used to backup, it&#039;s good to follow them. Let&#039;s see:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Learn about the module&#039;&#039;&#039;. If you aren&#039;t the creator of the module, spend some time playing with the module, creating and using it, exploring each one of its functionalities. By doing this you will end with some &amp;quot;real&amp;quot; data in the module instances that will be really useful when testing / debugging how the module backup is being generated.&lt;br /&gt;
# &#039;&#039;&#039;Draw one schema of the module&#039;&#039;&#039; DB structures. While you are playing with the module, look continuously to the DB, how records are saved and which are the relations between the module&#039;s tables. As far a backup is, basically, one &amp;quot;selective dump&amp;quot; of those tables, knowing the maximum about them is highly recommended. At the end, you must end with one tree structure will be the basis for the generated XML file.&lt;br /&gt;
# &#039;&#039;&#039;Annotate which tables contain user-related info&#039;&#039;&#039; and which ones don&#039;t. One of the core functionalities that must be present on each module is the ability to include user related information or skipping it, so you will need that information later.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tip&#039;&#039;&#039;: If the module already existed before Moodle 2.0, it can be a &#039;&#039;&#039;good idea&#039;&#039;&#039; to take a look to its 1.9 backuplib.php file, as far as the structure is already defined there and can help to understand the organization better and, at the same time, &#039;&#039;&#039;keeping the XML structure as similar as possible&#039;&#039;&#039;, that will, definitively, &#039;&#039;&#039;make things easier&#039;&#039;&#039; when converting 1.9 backup files to the new backup 2.0 format.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: It&#039;s important to highlight that the structure schema, once decided, should be as stable as possible along the time, because any change in the structure makes restore really way-more-more complex to be implemented. There isn&#039;t problems adding / deleting fields, nor adding new elements to the structure. But &#039;&#039;&#039;the structure (tree) itself must persist as stable as possible&#039;&#039;&#039;, so please, be careful when deciding it.&lt;br /&gt;
&lt;br /&gt;
So, applying these pre-requisites to our candidate module (choice), here it&#039;s the corresponding schema. We&#039;ll use it along the whole process.&lt;br /&gt;
&lt;br /&gt;
=== Schema ===&lt;br /&gt;
&lt;br /&gt;
In the schema, you must try to put as much information as possible, so that will produce the coding process later to be quicker and easier while keeping the final results free from errors and missing bits. So, once more, don&#039;t start coding immediately, instead spend some time with the requisites above, understanding how the module works and designing the final structure that represents it better.&lt;br /&gt;
&lt;br /&gt;
==== The (correct) candidate ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Backup20 choice alt.png‎|left|Alternative choice backup structure]]&lt;br /&gt;
&lt;br /&gt;
The tree on the left shows the ER/DB structure of the choice module, where one choice have one or more options and each option is answered by users one or more times (note: ignore cardinality accuracy in the previous phrase).&lt;br /&gt;
&lt;br /&gt;
And that&#039;s the best schema representing the structure of the module, with each element properly nested so, we won&#039;t have any problem with restore as far as the order required by restore (1, 2, 3) are naturally given. &lt;br /&gt;
&lt;br /&gt;
Looks easy, cool, but follow reading… you will get surprised! :-)&amp;lt;br style=&amp;quot;clear: both&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== The (chosen) candidate ====&lt;br /&gt;
&lt;br /&gt;
Instead we are going to use the (complete this time) diagram below. See the rationale about that after the image.&lt;br /&gt;
&lt;br /&gt;
[[Image:Backup20 choice.png‎|center|Used choice backup structure]]&lt;br /&gt;
&lt;br /&gt;
The main reason to use this tree (instead of the &amp;quot;correct&amp;quot;) one is that this is the structure that has been used by Moodle 1.9 backup since ages ago for the choice module and, of course have done its work ok. As commented above we must &#039;&#039;&#039;try to reduce the number of structural changes&#039;&#039;&#039; in one module backup in order to keep the restore operations working along the time (the same is applicable for the conversion of 1.9 backup files to the new 2.0 format). Finally, this is a good example about how one module &#039;&#039;&#039;can have different XML representations&#039;&#039;&#039; and we need to try to get that best one (this is not the case) on each case. So, once more, &#039;&#039;&#039;spending some time analyzing&#039;&#039;&#039; the activity is worth it.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s analyze the schema with some detail:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Detecting user information&#039;&#039;&#039;: We must be able to define the entities (tables) in the diagram that are used to store user information. One of the core features of the backup subsystem since its early days have been the ability to produce backup with and without user information. So we need that info. Hence, the &amp;quot;no user info&amp;quot; in the choice and choice_options elements (they are configuration, user-independent), while the choice_answers is marked as &amp;quot;user info&amp;quot; (contains user&#039;s answers to the choice).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Determining the correct order of backup&#039;&#039;&#039;: This, while simple, is critical too (especially from a restore perspective). As far as the restore reads progressively the xml file and performs actions in that order, we &#039;&#039;&#039;must guarantee that the &amp;quot;read order&amp;quot; is the correct&#039;&#039;&#039; one, fulfilling any possible dependency. Back to our schema it&#039;s clear that we need to backup the &amp;quot;choice&amp;quot; element at first, and then the &amp;quot;choice_options&amp;quot; and &amp;quot;choice_answers&amp;quot; ones. More yet, the &amp;quot;choice_options&amp;quot; must be backup before &amp;quot;choice_answers&amp;quot; as far as the later needs to save the values of the former (the &amp;quot;optionid&amp;quot; information). Be noted, that, as commented some paragraphs above, the &amp;quot;correct&amp;quot; alternative really gave us that order information easily. Doesn&#039;t matter as far as we have been able to establish it also in the &amp;quot;chosen&amp;quot; schema.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Attributes and elements&#039;&#039;&#039;: Now it&#039;s time to decide which fields will be considered attributes in the resulting XML file and which ones will be child elements (tags). The rule is simple: &#039;&#039;&#039;All the &amp;quot;id&amp;quot; fields must (should) be defined as attributes&#039;&#039;&#039;. Note this is just one arbitrary rule without much rationale behind it as far as, from a restore perspective, everything (attributes and child tags) will be handled in the same way (object attributes). So, in our schema, all the &amp;quot;id&amp;quot; fields have been marked as &amp;quot;attr&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Not needed elements&#039;&#039;&#039;: If we have designed properly the schema, we&#039;ll detect that some fields aren&#039;t necessary, as far as such information is already included in some parent element. In our schema, the field &amp;quot;choiceid&amp;quot;, pointing to the &amp;quot;choice-&amp;gt;id&amp;quot;, both in the options and in the answer elements have been marked as &amp;quot;not needed&amp;quot; as far as their parent &amp;quot;choice&amp;quot; already contains it. Something similar happens with the &amp;quot;course&amp;quot; field in the choice element, it doesn&#039;t need to be included in the backup file as far as something above it (course element, out from the module scope) already has it defined. Finally, there is one element marked as &amp;quot;needed&amp;quot; that shows us, once more, that the schema we are using is not the best. In the &amp;quot;chosen&amp;quot; one, as far as &amp;quot;choice_answers&amp;quot; isn&#039;t nested under &amp;quot;choice_options&amp;quot; we must keep that field in the backup, or restore won&#039;t know to which option each answer belongs to. Instead, with the &amp;quot;correct&amp;quot; schema, where answers are nested under options, that field is not needed. Summarizing, &#039;&#039;&#039;any field, but those already existing in parent elements must be included in backup&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Detecting file areas used by the module&#039;&#039;&#039;: Along the module we can be using various file areas in different elements and fields. We need to know exactly which file area is handled by which element and the (optional) itemid information used for that file area. In general, &#039;&#039;&#039;anything being one text field, or anything looking like one attachment&#039;&#039;&#039; has high chances to have one (hidden) file area associated. In our schema, we have one file area detected (choice_intro) corresponding to the introduction of the module and available to put any images or whatever in that field. Also, in general, all the &amp;quot;xxx_intro&amp;quot; file areas use to have no itemid, as far as the module&#039;s context is enough to define them without ambiguities. So, we mark the choice-&amp;gt;intro as &amp;quot;choice_intro&amp;quot; file area and &amp;quot;no itemid&amp;quot;). From our expertise playing with the module we know there aren&#039;t more file areas at all.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span id=&amp;quot;annotate_is_important&amp;quot;&amp;gt;&#039;&#039;&#039;Annotating some important bits&#039;&#039;&#039;:&amp;lt;/span&amp;gt; Due to the modularity of the backup and in order to know exactly which information must be saved (because it&#039;s used) and which one can be skipped, &#039;&#039;&#039;it&#039;s important to annotate some important elements along the whole backup&#039;&#039;&#039; process. So, back to our schema, we have marked the &amp;quot;choice_answer-&amp;gt;userid&amp;quot; field as &amp;quot;annotation&amp;quot; (so backup will, automatically, add all the information for that user). Here it&#039;s the list of elements that we must not forget to annotate (or we could end with non-restorable backups). Note that we must, always, be annotating &amp;quot;id&amp;quot; values and not other types of data:&lt;br /&gt;
** &#039;&#039;&#039;user&#039;&#039;&#039;: Any field pointing to one user-&amp;gt;id present along the schema (as said above, our schema has one).&lt;br /&gt;
** &#039;&#039;&#039;grouping&#039;&#039;&#039;: Any field pointing to one grouping-&amp;gt;id&lt;br /&gt;
** &#039;&#039;&#039;group&#039;&#039;&#039;: Any field pointing to one group-&amp;gt;id&lt;br /&gt;
** &#039;&#039;&#039;role&#039;&#039;&#039;: Any field pointing to one role-&amp;gt;id&lt;br /&gt;
** &#039;&#039;&#039;scale&#039;&#039;&#039;: Any field pointing to one scale-&amp;gt;id&lt;br /&gt;
** &#039;&#039;&#039;outcome&#039;&#039;&#039;: Any field pointing to one outcome-&amp;gt;id&lt;br /&gt;
&lt;br /&gt;
And this is all the information we need to know, before starting to code. Surely, once used to backup and restore, you will be able to start coding sooner, but &#039;&#039;&#039;don&#039;t forget about the importance of choosing one good and stable structure&#039;&#039;&#039; before anything else. It&#039;s really the critical part of any module&#039;s backup.&lt;br /&gt;
&lt;br /&gt;
Said that, let&#039;s see how to code all this information in order to get one cool backup for our beloved module.&lt;br /&gt;
&lt;br /&gt;
=== Coding ===&lt;br /&gt;
&lt;br /&gt;
Already here? Have you read, al least once, all the explanations and comments in the previous sections? Yes? Sure? I can imagine it&#039;s hard to read so much text, just imagine how hard is to write it! Go, go, go and read it!&lt;br /&gt;
&lt;br /&gt;
Jokes apart, in this section we are going to code the module&#039;s (choice) backup code. As you&#039;ll see soon, all the information gathered in previous steps is really important and will make the coding task easier and less prone to errors.&lt;br /&gt;
&lt;br /&gt;
==== Setting up the environment ====&lt;br /&gt;
&lt;br /&gt;
By default, any backup operation will end with one .zip file stored in some course / section / activity area. That means that, each time you execute one backup, you&#039;ll need to go across the web interface to that file area, download the generated .zip file, uncompress it and then, see how things have been generated. And you will be executing a bunch of backup files until you get everything working as expected, so the whole develop / test process isn&#039;t really &amp;quot;agile&amp;quot;. To improve things a bit, there is one $CFG setting that you should consider using &#039;&#039;&#039;in your development environment&#039;&#039;&#039;. Just put this in your config.php:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;keeptempdirectoriesonbackup = true;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this setting enabled, backup won&#039;t delete the temporary directory where everything is calculated, so you will be able to access to it directly, skipping all the ui / download / unzip steps above. Those temp directories are under $CFG-&amp;gt;dataroot/temp/backup. Note that, for each backup invocation, a new directory is created.&lt;br /&gt;
&lt;br /&gt;
Also, in order to be able to execute backups quickly (instead of navigating along the UI), you can, simply, put one script like this in your $CFG-&amp;gt;dirroot directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
require_once(&#039;config.php&#039;);&lt;br /&gt;
require_once($CFG-&amp;gt;dirroot . &#039;/backup/util/includes/backup_includes.php&#039;);&lt;br /&gt;
&lt;br /&gt;
$course_module_to_backup = XX; // Set this to one existing choice cmid in your dev site&lt;br /&gt;
$user_doing_the_backup   = YY; // Set this to the id of your admin accouun&lt;br /&gt;
&lt;br /&gt;
$bc = new backup_controller(backup::TYPE_1ACTIVITY, $course_module_to_backup, backup::FORMAT_MOODLE,&lt;br /&gt;
                            backup::INTERACTIVE_NO, backup::MODE_GENERAL, $user_doing_the_backup);&lt;br /&gt;
$bc-&amp;gt;execute_plan();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
just set proper values for XX and YY above and execute it from your browser. If you get one error about one not found class (with the name of your module) everything is ok.&lt;br /&gt;
&lt;br /&gt;
==== Required stuff ====&lt;br /&gt;
&lt;br /&gt;
The first thing you need to make is to, explicitly, declare that your module (choice in our example) is going to support the MOODLE2 backup format, to do so, you need to go to mod/choice/lib.php and, in the choice_supports() function add one new feature by adding this line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
case FEATURE_BACKUP_MOODLE2:          return true;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you execute another backup (with the script provided above, you will continue getting one error, as far as we haven&#039;t still coded anything related to backup, that&#039;s ok.&lt;br /&gt;
&lt;br /&gt;
Next step is to create the directory where all the backup code for the choice module will be. Just create the directory(s) mod/choice/backup/moodle2&lt;br /&gt;
&lt;br /&gt;
At this point, we have all the required stuff ready and all the pending tasks will be done under that recently created directory.&lt;br /&gt;
&lt;br /&gt;
==== Settings, Steps and Tasks ====&lt;br /&gt;
&lt;br /&gt;
In the introduction of the tutorial, we commented about backup being structured into tasks, each one being one collection of steps (and potentially using some custom settings). Those are, exactly, the objects that we need to create to achieve the backup functionality in our module.&lt;br /&gt;
&lt;br /&gt;
First of all, lets&#039; create the settings file, where all the custom settings to be used by our module will be defined and implemented. It must be named &#039;&#039;&#039;mod/choice/backup/moodle2/backup_choice_settingslib.php&#039;&#039;&#039; and their contents are really meaningful:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
// This file is part of Moodle - http://moodle.org/&lt;br /&gt;
//&lt;br /&gt;
// Moodle is free software: you can redistribute it and/or modify&lt;br /&gt;
// it under the terms of the GNU General Public License as published by&lt;br /&gt;
// the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
// (at your option) any later version.&lt;br /&gt;
//&lt;br /&gt;
// Moodle is distributed in the hope that it will be useful,&lt;br /&gt;
// but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
// GNU General Public License for more details.&lt;br /&gt;
//&lt;br /&gt;
// You should have received a copy of the GNU General Public License&lt;br /&gt;
// along with Moodle.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @package moodlecore&lt;br /&gt;
 * @subpackage backup-moodle2&lt;br /&gt;
 * @copyright 2010 onwards YOUR_NAME_GOES_HERE {@link YOUR_URL_GOES_HERE}&lt;br /&gt;
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
 // This activity has not particular settings but the inherited from the generic&lt;br /&gt;
 // backup_activity_task so here there isn&#039;t any class definition, like the ones&lt;br /&gt;
 // existing in /backup/moodle2/backup_settingslib.php (activities section)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Looks simple, isn&#039;t it? Nothing but a few comments. This is looking really easy. For now, we aren&#039;t going to introduce any custom setting for any module, only the &amp;quot;core activity settings&amp;quot; will be used. Once backup and Moodle 2.0 is stable we can start analyzing useful settings to customize how the module&#039;s backup is performed. For now, just leave it blank, please. In fact, if it&#039;s empty,&#039;&#039;&#039; you can safely not create it&#039;&#039;&#039; at all. It&#039;s here just for explanation purposes.&lt;br /&gt;
&lt;br /&gt;
Side note: To save some space, we only will be showing the © message/license in the code above. Just add it to each file created.&lt;br /&gt;
&lt;br /&gt;
Now we are going to create the steps file, where all the steps to be executed by our backup activity task will be defined and implemented. It must be named &#039;&#039;&#039;mod/choice/backup/moodle2/backup_choice_stepslib.php&#039;&#039;&#039; and their contents are these:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Define all the backup steps that will be used by the backup_choice_activity_task&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Yes, it&#039;s another empty file. No worries we&#039;ll fill it later.&lt;br /&gt;
&lt;br /&gt;
Finally we need to create the task file, where our just created settings and steps files will be included and used. It must be named &#039;&#039;&#039;mod/choice/backup/moodle2/backup_choice_activity_task.class.php&#039;&#039;&#039; and their contents are these:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
require_once($CFG-&amp;gt;dirroot . &#039;/mod/choice/backup/moodle2/backup_choice_stepslib.php&#039;); // Because it exists (must)&lt;br /&gt;
require_once($CFG-&amp;gt;dirroot . &#039;/mod/choice/backup/moodle2/backup_choice_settingslib.php&#039;); // Because it exists (optional)&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * choice backup task that provides all the settings and steps to perform one&lt;br /&gt;
 * complete backup of the activity&lt;br /&gt;
 */&lt;br /&gt;
class backup_choice_activity_task extends backup_activity_task {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define (add) particular settings this activity can have&lt;br /&gt;
     */&lt;br /&gt;
    protected function define_my_settings() {&lt;br /&gt;
        // No particular settings for this activity&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define (add) particular steps this activity can have&lt;br /&gt;
     */&lt;br /&gt;
    protected function define_my_steps() {&lt;br /&gt;
        // Choice only has one structure step&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Code the transformations to perform in the activity in&lt;br /&gt;
     * order to get transportable (encoded) links&lt;br /&gt;
     */&lt;br /&gt;
    static public function encode_content_links($content) {&lt;br /&gt;
        return $content;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the main file (class) of the choice backup and it will be used to define any setting (define_my_settings()  method) and any step (define_my_steps() method) to be executed in the backup process. It also contains one 3rd method (encode_content_links($content)) that will allow manually URLs pointing to the choice to be properly converted when the choice is moved to other site / course using the backup / restore functionality.&lt;br /&gt;
&lt;br /&gt;
As you see, for now, the three required methods are doing nothing. Just execute the backup again. Wow, no errors anymore. We have already fulfilled all the minimum coding needs in order to have the choice backup working. &lt;br /&gt;
&lt;br /&gt;
Now you should go to your $CFG-&amp;gt;dataroot/temp/backup/xxxx directory (the more recent) and spend some time looking what has been created under the activities/choice_XX directory. There is already a lot of stuff that backup has generated for you: comments, blocks, logs, grades, module info, roles... some of them empty and others already showing real information.&lt;br /&gt;
&lt;br /&gt;
In any case, now, we need to add one important file there, the &amp;quot;choice.xml&amp;quot; where the real information for your module will be present, following the specs given by the schema we decided some sections above.&lt;br /&gt;
&lt;br /&gt;
So, let&#039;s go, it&#039;s time to create our choice structure step. To do so, we edit the &#039;&#039;&#039;mod/choice/backup/moodle2/backup_choice_stepslib.php&#039;&#039;&#039; file and add this code after the existing comments:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the complete choice structure for backup, with file and id annotations&lt;br /&gt;
 */     &lt;br /&gt;
class backup_choice_activity_structure_step extends backup_activity_structure_step {&lt;br /&gt;
&lt;br /&gt;
    protected function define_structure() {&lt;br /&gt;
&lt;br /&gt;
        // To know if we are including userinfo&lt;br /&gt;
        $userinfo = $this-&amp;gt;get_setting_value(&#039;userinfo&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Define each element separated&lt;br /&gt;
&lt;br /&gt;
        // Build the tree&lt;br /&gt;
&lt;br /&gt;
        // Define sources&lt;br /&gt;
&lt;br /&gt;
        // Define id annotations&lt;br /&gt;
&lt;br /&gt;
        // Define file annotations&lt;br /&gt;
&lt;br /&gt;
        // Return the root element (choice), wrapped into standard activity structure&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, we have defined one structure step that will be the responsible, using one PHP API to provide backup with all the information needed to generate the choice.xml file. Now, go back to the task file and add these contents in the define_my_steps() methods:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
        $this-&amp;gt;add_step(new backup_choice_activity_structure_step(&#039;choice_structure&#039;, &#039;choice.xml&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That way, our choice task knows it must execute one new step, the one involving the generation of the choice.xml file. Let&#039;s execute one new backup and see results.You should be getting one new error with something like that &amp;quot;backup_structure_step_wrong_structure&amp;quot;. It means that everything is ok up to now, the task has tried to execute the structure step, but this is not properly defined.&lt;br /&gt;
&lt;br /&gt;
===== Defining each element =====&lt;br /&gt;
&lt;br /&gt;
So, next step is about to define the choice structure. Going back to our step, under the &#039;&#039;// Define each element separate&#039;&#039; comment we&#039;ll add this code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
        $choice = new backup_nested_element(&#039;choice&#039;, array(&#039;id&#039;), array(&lt;br /&gt;
            &#039;name&#039;, &#039;intro&#039;, &#039;introformat&#039;, &#039;publish&#039;,&lt;br /&gt;
            &#039;showresults&#039;, &#039;display&#039;, &#039;allowupdate&#039;, &#039;allowunanswered&#039;,&lt;br /&gt;
            &#039;limitanswers&#039;, &#039;timeopen&#039;, &#039;timeclose&#039;, &#039;timemodified&#039;));&lt;br /&gt;
&lt;br /&gt;
        $options = new backup_nested_element(&#039;options&#039;);&lt;br /&gt;
&lt;br /&gt;
        $option = new backup_nested_element(&#039;option&#039;, array(&#039;id&#039;), array(&lt;br /&gt;
            &#039;text&#039;, &#039;maxanswers&#039;, &#039;timemodified&#039;));&lt;br /&gt;
&lt;br /&gt;
        $answers = new backup_nested_element(&#039;answers&#039;);&lt;br /&gt;
&lt;br /&gt;
        $answer = new backup_nested_element(&#039;answer&#039;, array(&#039;id&#039;), array(&lt;br /&gt;
            &#039;userid&#039;, &#039;optionid&#039;, &#039;timemodified&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Return the root element =====&lt;br /&gt;
&lt;br /&gt;
And also, in order to get it working, let&#039;s define the return element, so, after the &#039;&#039;// Return the root element&#039;&#039; comment, add this code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
        return $this-&amp;gt;prepare_activity_structure($choice);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can execute the backup again and, a long as we have already defined the root element of the module, should have now one choice.xml file created (without proper contents but must be there).&lt;br /&gt;
&lt;br /&gt;
About the code above, all we have done is to define, separately each element that will be part of the choice.xml file. Special note about the $options and $answers elements. Since Moodle 1.9 we use to enclose real elements (the singular ones) inside one extra plural element, hence we create them here (as empty elements without attributes nor child tags). About the rest (the singular ones) we use 3 params in the instantiation:&lt;br /&gt;
* The name of the element&lt;br /&gt;
* One array of attributes of the element&lt;br /&gt;
* One array of child tags (fields) of the element.&lt;br /&gt;
And we include all the fields that previously we had defined in our schema &#039;&#039;&#039;but&#039;&#039;&#039; the ones marked as &#039;&#039;&#039;not needed&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===== Building the tree =====&lt;br /&gt;
&lt;br /&gt;
Now it&#039;s time to declare the relations between all those elements, so, after the &#039;&#039;// Build the tree&#039;&#039; comment we&#039;ll add this code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
        $choice-&amp;gt;add_child($options);&lt;br /&gt;
        $options-&amp;gt;add_child($option);&lt;br /&gt;
&lt;br /&gt;
        $choice-&amp;gt;add_child($answers);&lt;br /&gt;
        $answers-&amp;gt;add_child($answer);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Self explanatory, using $choice as root element, we define the whole tree using the add_child() method. And, of course, we respect &#039;&#039;&#039;the order&#039;&#039;&#039; that we had decided, so $options will be &#039;&#039;&#039;physically&#039;&#039;&#039; before $answers in the tree (and in the generated XML).&lt;br /&gt;
&lt;br /&gt;
===== Defining the sources =====&lt;br /&gt;
&lt;br /&gt;
After this, it&#039;s the moment to instruct our tree about how to fetch information from DB in order to generate the choice.xml file, so after the &#039;&#039;// Define sources&#039;&#039; comment, we&#039;ll add this code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
        $choice-&amp;gt;set_source_table(&#039;choice&#039;, array(&#039;id&#039; =&amp;gt; backup::VAR_ACTIVITYID));&lt;br /&gt;
&lt;br /&gt;
        $option-&amp;gt;set_source_sql(&#039;&lt;br /&gt;
            SELECT *&lt;br /&gt;
              FROM {choice_options}&lt;br /&gt;
             WHERE choiceid = ?&#039;,&lt;br /&gt;
            array(backup::VAR_PARENTID));&lt;br /&gt;
&lt;br /&gt;
        // All the rest of elements only happen if we are including user info&lt;br /&gt;
        if ($userinfo) {&lt;br /&gt;
            $answer-&amp;gt;set_source_table(&#039;choice_answers&#039;, array(&#039;choiceid&#039; =&amp;gt; &#039;../../id&#039;));&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So we define the source for the $choice element as the information present in the &#039;choice&#039; table for the id that is being backup (backup::VAR_ACTIVITYID). Or we define the source for the $option element like one SQL (could have been one table too, just to show more possibilities of the API, using the parent (the choice one) as value for the query (backup::VAR_PARENTID).&lt;br /&gt;
&lt;br /&gt;
And finally, conditionally, if user information is going to be included, we define the source for the $answer (note we had already annotated in our schema that this element was dependent of that). &lt;br /&gt;
&lt;br /&gt;
And we define the source condition as having the &#039;choiceid&#039; field matching the value of the &#039;id&#039; field two levels above (../../id). If you follow the tree created in the previous code section, that&#039;s exactly the choice-&amp;gt;id (remember we have introduced one extra level (the plural one between the &#039;choice&#039; and the &#039;answer&#039; singular elements). Note this is 100% equivalent to use backup::VAR_PARENTID (as we have done in the $option source) just used to show possibilities of the API.&lt;br /&gt;
&lt;br /&gt;
Summarizing, with the API, we have these 3 method for defining sources:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_source_table($tablename, array $conditions)&#039;&#039;&#039;: When the information is get straight from one table (vast majority of cases in backup)&lt;br /&gt;
* &#039;&#039;&#039;set_source_sql($sql, array $params)&#039;&#039;&#039;: When the information doesn&#039;t map one table directly and we need something more complex&lt;br /&gt;
* &#039;&#039;&#039;set_source_array($array)&#039;&#039;&#039;: When we have some fixed information to backup. Not really useful in nested information, but used by core here and there.&lt;br /&gt;
&lt;br /&gt;
Note that both the $conditions and $params above only accept one limited number of constants or values (not all available in all backups!), mainly:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;backup::VAR_COURSEID&#039;&#039;&#039;: The id of the course this activity belongs to / the course id being backup&lt;br /&gt;
* &#039;&#039;&#039;backup::VAR_SECTIONID&#039;&#039;&#039;: The id of the section this activity belongs to / the section id being backup&lt;br /&gt;
* &#039;&#039;&#039;backup::VAR_ACTIVITYID&#039;&#039;&#039;: The id of the activity id being backup&lt;br /&gt;
* &#039;&#039;&#039;backup::VAR_MODID&#039;&#039;&#039;: The id of the course_module being backup&lt;br /&gt;
* &#039;&#039;&#039;backup::VAR_MODULENAME&#039;&#039;&#039;: The name of the module being backup&lt;br /&gt;
* &#039;&#039;&#039;backup::VAR_BLOCKID&#039;&#039;&#039;: The id of the block being backup&lt;br /&gt;
* &#039;&#039;&#039;backup::VAR_BLOCKNAME&#039;&#039;&#039;: The name of the block being backup&lt;br /&gt;
* &#039;&#039;&#039;backup::VAR_CONTEXTID&#039;&#039;&#039;: The context id of the activity / course being backup&lt;br /&gt;
* &#039;&#039;&#039;backup::VAR_PARENTID&#039;&#039;&#039;: The value of the first parent id found in the structure.&lt;br /&gt;
* &#039;&#039;&#039;../some/path/to/parent&#039;&#039;&#039;: To manually point to other value of any parent.&lt;br /&gt;
&lt;br /&gt;
Is important to note that, in 99% of the case we should be using, exclusively, some of the constants above, and backup, automatically will handle them properly. In case we have any other condition or param to be added like, for example, the number 23, or the string &#039;user&#039;, we cannot add them directly, but enclose them with the helper &#039;&#039;&#039;backup_helper::is_sqlparam(23)&#039;&#039;&#039; or &#039;&#039;&#039;backup_helper::is_sqlparam(&#039;user&#039;)&#039;&#039;&#039;. That way backup will know they are raw SQL params not needing any special handling, like the constants above.&lt;br /&gt;
&lt;br /&gt;
Well, now it&#039;s time to run backup again and take a look to our activities/choice_XX/choice.xml file. Now everything should be there, properly nested, the choice, the options and the answers. We are really near finishing now.&lt;br /&gt;
&lt;br /&gt;
===== Annotating IDs =====&lt;br /&gt;
&lt;br /&gt;
Now it&#039;s time to perform all the annotations that we had already detected in our analysis of the module. So, after the &#039;&#039;// Define id annotations&#039;&#039; comment we&#039;ll be adding:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
        $answer-&amp;gt;annotate_ids(&#039;user&#039;, &#039;userid&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It simply means, annotate for each $answer element, the value of the &#039;userid&#039; field as one &#039;user&#039; to be backup. In other words we are instructing backup that there is one new user to include later, when generating the users information and, at the same time, we are determining that those users are needed for the choice XX, so, if on restore we decide to skip that choice and such user isn&#039;t necessary for any other module / subsystem, it won&#039;t be restored. All this &amp;quot;uses&amp;quot; information is handled by the &amp;quot;inforef.xml&amp;quot; files within each activity backup, just in case you&#039;re interested to take a look to them. Else, just consider them, &amp;quot;dark magic&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: If you&#039;re not sure about which elements must be annotated, review [[Development:Backup 2.0 for developers #annotate_is_important|&amp;quot;Annotating some important bits&amp;quot;]] above for details about &#039;&#039;&#039;must-be-done&#039;&#039;&#039; annotations.&lt;br /&gt;
&lt;br /&gt;
===== Annotating files =====&lt;br /&gt;
&lt;br /&gt;
And, finally, reviewing our ellaborated and detailed schema, the last thing we need to take rid of are the file areas used, so, after the &#039;&#039;// Define file annotations&#039;&#039; comment we&#039;ll add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
        $choice-&amp;gt;annotate_files(&#039;mod_choice&#039;, &#039;intro&#039;, null); // This file area hasn&#039;t itemid&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That means that, within the $choice element we have one file area in use, of course belonging to the &#039;mod_choice&#039; component (where all the choice module files belong to), named &#039;intro&#039; and not using itemid (null). Note that, as far as it is possible to have multiple file areas in the same element (table), you may end having multiple calls to that method, one for each filearea to be added to backup. About the third parameter, in case we need it, it must be the name of one of the attributes or fields of the $choice element (usually, in the vast majority of cases, the &#039;id&#039; of the element), otherwise we&#039;ll use null.&lt;br /&gt;
&lt;br /&gt;
=== One encoded wor(l)d ===&lt;br /&gt;
&lt;br /&gt;
Already out from the step definition, that should be working ok now, with the choice.xml file being generated and all the annotations being processed and added to the inforef.xml file, there is one more point to fulfill before considering choice&#039;s backup 100% finished.&lt;br /&gt;
&lt;br /&gt;
And it&#039;s about how to provide the ability to transform some links to the choice module when the backup file is restored into another server / course. That is automatically handled by a two-step approach:&lt;br /&gt;
# On backup, we transform as many well-know URLs as possible to one encoded form.&lt;br /&gt;
# On restore, we transform those encoded URLS back to their original form, but pointing to their new targets.&lt;br /&gt;
&lt;br /&gt;
So, in backup, we must provide services for the point 1 above, and that is done by adding some &amp;quot;encoding&amp;quot; conversions to the &#039;&#039;&#039;encode_content_links()&#039;&#039;&#039; method in our &#039;&#039;&#039;backup_choice_activity_task&#039;&#039;&#039;. So, define it to look like this&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Code the transformations to perform in the activity in&lt;br /&gt;
     * order to get transportable (encoded) links&lt;br /&gt;
     */&lt;br /&gt;
    static public function encode_content_links($content) {&lt;br /&gt;
        global $CFG;&lt;br /&gt;
&lt;br /&gt;
        $base = preg_quote($CFG-&amp;gt;wwwroot,&amp;quot;/&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        // Link to the list of choices&lt;br /&gt;
        $search=&amp;quot;/(&amp;quot;.$base.&amp;quot;\/mod\/choice\/index.php\?id\=)([0-9]+)/&amp;quot;;&lt;br /&gt;
        $content= preg_replace($search, &#039;$@CHOICEINDEX*$2@$&#039;, $content);&lt;br /&gt;
&lt;br /&gt;
        // Link to choice view by moduleid&lt;br /&gt;
        $search=&amp;quot;/(&amp;quot;.$base.&amp;quot;\/mod\/choice\/view.php\?id\=)([0-9]+)/&amp;quot;;&lt;br /&gt;
        $content= preg_replace($search, &#039;$@CHOICEVIEWBYID*$2@$&#039;, $content);&lt;br /&gt;
&lt;br /&gt;
        return $content;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Básically, it gets any URL (manually written) in any content of the backup being generated and transform some (well-known) URLs to one encoded alternative. In this case we are encoding:&lt;br /&gt;
* Any URL pointing to the list of choices in a course == is changed to ==&amp;gt; $@CHOICEINDEX*XX@$&lt;br /&gt;
* Any URL pointing to one exact choice == is changed to ==&amp;gt; $@CHOICEVIEWBYID*YY@$&lt;br /&gt;
&lt;br /&gt;
In restore, well have the opposite conversions happening, replacing the XX and YY above to their new equivalents, so those links will be transportable via backup/restore without any need to edit them manually later.&lt;br /&gt;
&lt;br /&gt;
Tip: Don&#039;t forget to look to the module&#039;s 1.9 backup code, as far as there you&#039;ll find which conversions must be considered to be added here.&lt;br /&gt;
&lt;br /&gt;
=== Final notes ===&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t get stressed, it&#039;s really more difficult / longer to explain than to do it. 100% guaranteed, else we&#039;ll return your money. :-P&lt;br /&gt;
* If you&#039;ve become lost, or your code is not working properly, you always can [http://cvs.moodle.org/moodle/mod/choice/backup/moodle2/ see the complete choice working code in CVS]. Or, alternatively, look to other well known modules, like [http://cvs.moodle.org/moodle/mod/forum/backup/moodle2/ forum] or also the [http://cvs.moodle.org/moodle/backup/moodle2/backup_stepslib.php?view=markup big core library of steps], where you will find all sort of uses of the API.&lt;br /&gt;
* If you are going to implement backup for module XXXX, just copy this document, replace any &amp;quot;choice&amp;quot; occurrence within it by XXXX and follow it from the beginning to the end. Should work.&lt;br /&gt;
* If the module already existed in Moodle 1.x, try to follow the same structure if possible, that will make things easier for restore / conversion.&lt;br /&gt;
* Right now there are some pending tasks related with different exceptions that will be thrown if you code something wrongly (bad nesting, incorrect field names, bad constant/sql param uses...) and how they are shown. Fix for that will be coming soon.&lt;br /&gt;
* Also, as stated at the beginning of this tutorial, we are still missing &amp;quot;subplugins&amp;quot; final support in modules, so try to avoid implementing backup on them for now. Once ready, there will be one new section in this document about them.&lt;br /&gt;
* Any question / improvement / comment, feel free to contact with Moodle HQ, better if using the Tracker or, alternatively, forums / email / direct contact with your very-best-developer-friend. Note this is version x.0 (dot zero) of the backup API, so sure there are possibilities to improve it along the time.&lt;br /&gt;
&lt;br /&gt;
== Other components that can be backed up ==&lt;br /&gt;
&lt;br /&gt;
Most plugins which can be added to a course can also include data in a course backup. Some examples:&lt;br /&gt;
&lt;br /&gt;
* Course formats&lt;br /&gt;
* Themes: [[Development: Backup 2.0 theme data| Backup 2.0 theme data]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81341</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81341"/>
		<updated>2011-02-16T18:17:24Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* get_fast_modinfo data */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The features described here are available since Moodle 2.0.2.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
A new API allows you to customise how your module displays on the main course page:&lt;br /&gt;
&lt;br /&gt;
* You can display custom HTML below the link to your module. &lt;br /&gt;
&lt;br /&gt;
* If your module does not have a link (like Label, where it is only for display on the main page) then you can remove the link from the main page and from all navigation etc.&lt;br /&gt;
&lt;br /&gt;
* You can display HTML next to the link to your module that indicates dynamic information (like Forum, where it displays information about unread messages).&lt;br /&gt;
&lt;br /&gt;
* You can display additional icons next to the other module editing icons when the user is editing the page.&lt;br /&gt;
&lt;br /&gt;
In addition, existing things you could already do (like change the icon on the main page) are still available when using the new API.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt; function now returns specific classes which are documented and which you can use to obtain new information about modules.&lt;br /&gt;
&lt;br /&gt;
== Backward compatibility ==&lt;br /&gt;
&lt;br /&gt;
All modules and code written for Moodle 2.0 should continue to behave in exactly the same manner. There is no need to change existing modules for this API unless you want to use the new features.&lt;br /&gt;
&lt;br /&gt;
== Removing your link ==&lt;br /&gt;
&lt;br /&gt;
If your module should not appear in navigation and in other lists of modules to visit or get information for, like Label, the easiest way to remove that link is to return true for FEATURE_NO_VIEW_LINK in your module&#039;s &amp;lt;tt&amp;gt;_supports&amp;lt;/tt&amp;gt; function.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, in cache ==&lt;br /&gt;
&lt;br /&gt;
The first place you can customise your module display is in the existing &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; API function. This function obtains information about the module which will be stored in the course cache (the &amp;lt;tt&amp;gt;modinfo&amp;lt;/tt&amp;gt; field of the course table).&lt;br /&gt;
&lt;br /&gt;
The course cache is only updated when somebody edits a module, so it can&#039;t be used for dynamic information - but it&#039;s okay if it takes a few database queries to calculate the data because it will be cached for future use.&lt;br /&gt;
&lt;br /&gt;
The function should return a value of class &amp;lt;tt&amp;gt;cached_cm_info&amp;lt;/tt&amp;gt;. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_get_coursemodule_info($cm) {&lt;br /&gt;
    $info = new cached_cm_info;&lt;br /&gt;
    $info-&amp;gt;content = &#039;&amp;lt;p&amp;gt;This will display below the module.&amp;lt;/p&amp;gt;&#039;;&lt;br /&gt;
    return $info;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can change several properties which are documented in that class definition. If you don&#039;t change a property, its value remains default.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; - name of activity (text of the link on course page).&lt;br /&gt;
* &amp;lt;tt&amp;gt;icon&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;iconcomponent&amp;lt;/tt&amp;gt; - name and component name of icon to display by the link.&lt;br /&gt;
* &amp;lt;tt&amp;gt;content&amp;lt;/tt&amp;gt; - extra HTML content to display below the module link on course page (not shown in navigation etc).&lt;br /&gt;
* &amp;lt;tt&amp;gt;customdata&amp;lt;/tt&amp;gt; - arbitrary extra PHP data to store in modinfo cache; useful if, for performance reasons, your module needs to store data that should be accessible very quickly from other parts of the course.&lt;br /&gt;
* &amp;lt;tt&amp;gt;extraclasses&amp;lt;/tt&amp;gt; - extra CSS class or classes that will be added to the activity on the main page, so that you can alter the styling.&lt;br /&gt;
* &amp;lt;tt&amp;gt;onclick&amp;lt;/tt&amp;gt; - already-escaped HTML that will be inserted as the value of the onclick attribute.&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t need the information to be cached (it can be retrieved very quickly without making any database queries) then you might consider using one of the functions below instead, in order to avoid unnecessarily increasing the size of the course cache. Although the headings mention the current user, you can of course use those functions in a way that doesn&#039;t depend on the current user.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user ==&lt;br /&gt;
&lt;br /&gt;
You can customise module display dynamically (when the page loads). For example you might want to alter it based on the permissions of the current user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_dynamic(cm_info $cm) {&lt;br /&gt;
    $context = get_context_instance(CONTEXT_MODULE, $cm-&amp;gt;id);&lt;br /&gt;
    if (!has_capability(&#039;some/capability&#039;, $context)) {&lt;br /&gt;
        $cm-&amp;gt;set_user_visible(false);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This code can affect the navigation, and whether users are permitted to access the module (as above). It runs on all pages within the course, so it&#039;s very important that you do not put slow code in this function: it should not make any database queries. &lt;br /&gt;
&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;set_user_visible&amp;lt;/tt&amp;gt; function shown, you can also set many other things such as additional editing icons which will appear if editing mode is enabled. See the cm_info class documentation for more information. &lt;br /&gt;
&lt;br /&gt;
Most things are set using functions (as above; another example would be &amp;lt;tt&amp;gt;set_content&amp;lt;/tt&amp;gt; which sets the same content data as mentioned in the previous section) while other things can be set directly using public variables.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user, on course page only ==&lt;br /&gt;
&lt;br /&gt;
Sometimes you need to display custom information for the current user that appears only on the course view page. For example, the forum module displays unread information on the view page. This information doesn&#039;t show on other pages (it isn&#039;t included in the navigation, for instance).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_view(cm_info $cm) {&lt;br /&gt;
    $cm-&amp;gt;set_after_link(&#039;Last tadpole: 22:17&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Because this function only runs when looking at the course page:&lt;br /&gt;
&lt;br /&gt;
* It is OK to do tasks which may require some database queries (such as checking for unread forum messages), although obviously this should be kept to a minimum. In particular, care should be taken so that if there are 20 instances of the activity on the course page, it doesn&#039;t make 20 separate queries to obtain the information.&lt;br /&gt;
&lt;br /&gt;
* Inside this function you cannot set options which affect the appearance or access to the activity on other pages; for example, you cannot turn off the uservisible flag as shown in the previous example. This is because these options are required on other pages (e.g. to display navigation) so it does not make sense to set them only for the course page. If you try, you&#039;ll get a &amp;lt;tt&amp;gt;coding_exception&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== get_fast_modinfo data ==&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt; now returns an object of class course_modinfo, which itself contains cm_info objects about each activity. (These are entirely backward-compatible with the previous return value.)&lt;br /&gt;
&lt;br /&gt;
In addition to the old methods for obtaining data from $modinfo, there are some new functions. For example, here is how to get a single cm_info from $modinfo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$modinfo = get_fast_modinfo($course);&lt;br /&gt;
$cm = $modinfo-&amp;gt;get_cm($cmid);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The cm_info objects contain additional information that is not present in the course_modules database row, such as the module&#039;s name, and the icon and associated content mentioned above. In order to distinguish these from the plain database objects, you can specify the cm_info class in a function definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function my_clever_function(cm_info $cm) {&lt;br /&gt;
    if (!$cm-&amp;gt;uservisible) {&lt;br /&gt;
        // the module is not visible or available to current user,&lt;br /&gt;
        // so do something clever instead&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By specifying cm_info in the parameter list, you&#039;ll cause PHP to give an error if anyone tries to call that function with a $cm object that just came from the database row, instead of from &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt;. (It is good practice to always get $cm from get_fast_modinfo, but there might be exceptions.) &lt;br /&gt;
&lt;br /&gt;
Of course, this is only necessary if your function relies on a feature that is specific to cm_info, such as the &#039;uservisible&#039; field above. If your function only uses fields which are present in the database row, then there&#039;s no need to require cm_info.&lt;br /&gt;
&lt;br /&gt;
== More documentation ==&lt;br /&gt;
&lt;br /&gt;
All three classes for this API are in the core file &amp;lt;tt&amp;gt;lib/modinfolib.php&amp;lt;/tt&amp;gt; and contain complete PHPdoc information for all fields and functions.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81340</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81340"/>
		<updated>2011-02-16T18:15:33Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Customising module display, in cache: _get_coursemodule_info */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The features described here are available since Moodle 2.0.2.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
A new API allows you to customise how your module displays on the main course page:&lt;br /&gt;
&lt;br /&gt;
* You can display custom HTML below the link to your module. &lt;br /&gt;
&lt;br /&gt;
* If your module does not have a link (like Label, where it is only for display on the main page) then you can remove the link from the main page and from all navigation etc.&lt;br /&gt;
&lt;br /&gt;
* You can display HTML next to the link to your module that indicates dynamic information (like Forum, where it displays information about unread messages).&lt;br /&gt;
&lt;br /&gt;
* You can display additional icons next to the other module editing icons when the user is editing the page.&lt;br /&gt;
&lt;br /&gt;
In addition, existing things you could already do (like change the icon on the main page) are still available when using the new API.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt; function now returns specific classes which are documented and which you can use to obtain new information about modules.&lt;br /&gt;
&lt;br /&gt;
== Backward compatibility ==&lt;br /&gt;
&lt;br /&gt;
All modules and code written for Moodle 2.0 should continue to behave in exactly the same manner. There is no need to change existing modules for this API unless you want to use the new features.&lt;br /&gt;
&lt;br /&gt;
== Removing your link ==&lt;br /&gt;
&lt;br /&gt;
If your module should not appear in navigation and in other lists of modules to visit or get information for, like Label, the easiest way to remove that link is to return true for FEATURE_NO_VIEW_LINK in your module&#039;s &amp;lt;tt&amp;gt;_supports&amp;lt;/tt&amp;gt; function.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, in cache ==&lt;br /&gt;
&lt;br /&gt;
The first place you can customise your module display is in the existing &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; API function. This function obtains information about the module which will be stored in the course cache (the &amp;lt;tt&amp;gt;modinfo&amp;lt;/tt&amp;gt; field of the course table).&lt;br /&gt;
&lt;br /&gt;
The course cache is only updated when somebody edits a module, so it can&#039;t be used for dynamic information - but it&#039;s okay if it takes a few database queries to calculate the data because it will be cached for future use.&lt;br /&gt;
&lt;br /&gt;
The function should return a value of class &amp;lt;tt&amp;gt;cached_cm_info&amp;lt;/tt&amp;gt;. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_get_coursemodule_info($cm) {&lt;br /&gt;
    $info = new cached_cm_info;&lt;br /&gt;
    $info-&amp;gt;content = &#039;&amp;lt;p&amp;gt;This will display below the module.&amp;lt;/p&amp;gt;&#039;;&lt;br /&gt;
    return $info;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can change several properties which are documented in that class definition. If you don&#039;t change a property, its value remains default.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; - name of activity (text of the link on course page).&lt;br /&gt;
* &amp;lt;tt&amp;gt;icon&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;iconcomponent&amp;lt;/tt&amp;gt; - name and component name of icon to display by the link.&lt;br /&gt;
* &amp;lt;tt&amp;gt;content&amp;lt;/tt&amp;gt; - extra HTML content to display below the module link on course page (not shown in navigation etc).&lt;br /&gt;
* &amp;lt;tt&amp;gt;customdata&amp;lt;/tt&amp;gt; - arbitrary extra PHP data to store in modinfo cache; useful if, for performance reasons, your module needs to store data that should be accessible very quickly from other parts of the course.&lt;br /&gt;
* &amp;lt;tt&amp;gt;extraclasses&amp;lt;/tt&amp;gt; - extra CSS class or classes that will be added to the activity on the main page, so that you can alter the styling.&lt;br /&gt;
* &amp;lt;tt&amp;gt;onclick&amp;lt;/tt&amp;gt; - already-escaped HTML that will be inserted as the value of the onclick attribute.&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t need the information to be cached (it can be retrieved very quickly without making any database queries) then you might consider using one of the functions below instead, in order to avoid unnecessarily increasing the size of the course cache. Although the headings mention the current user, you can of course use those functions in a way that doesn&#039;t depend on the current user.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user ==&lt;br /&gt;
&lt;br /&gt;
You can customise module display dynamically (when the page loads). For example you might want to alter it based on the permissions of the current user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_dynamic(cm_info $cm) {&lt;br /&gt;
    $context = get_context_instance(CONTEXT_MODULE, $cm-&amp;gt;id);&lt;br /&gt;
    if (!has_capability(&#039;some/capability&#039;, $context)) {&lt;br /&gt;
        $cm-&amp;gt;set_user_visible(false);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This code can affect the navigation, and whether users are permitted to access the module (as above). It runs on all pages within the course, so it&#039;s very important that you do not put slow code in this function: it should not make any database queries. &lt;br /&gt;
&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;set_user_visible&amp;lt;/tt&amp;gt; function shown, you can also set many other things such as additional editing icons which will appear if editing mode is enabled. See the cm_info class documentation for more information. &lt;br /&gt;
&lt;br /&gt;
Most things are set using functions (as above; another example would be &amp;lt;tt&amp;gt;set_content&amp;lt;/tt&amp;gt; which sets the same content data as mentioned in the previous section) while other things can be set directly using public variables.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user, on course page only ==&lt;br /&gt;
&lt;br /&gt;
Sometimes you need to display custom information for the current user that appears only on the course view page. For example, the forum module displays unread information on the view page. This information doesn&#039;t show on other pages (it isn&#039;t included in the navigation, for instance).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_view(cm_info $cm) {&lt;br /&gt;
    $cm-&amp;gt;set_after_link(&#039;Last tadpole: 22:17&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Because this function only runs when looking at the course page:&lt;br /&gt;
&lt;br /&gt;
* It is OK to do tasks which may require some database queries (such as checking for unread forum messages), although obviously this should be kept to a minimum. In particular, care should be taken so that if there are 20 instances of the activity on the course page, it doesn&#039;t make 20 separate queries to obtain the information.&lt;br /&gt;
&lt;br /&gt;
* Inside this function you cannot set options which affect the appearance or access to the activity on other pages; for example, you cannot turn off the uservisible flag as shown in the previous example. This is because these options are required on other pages (e.g. to display navigation) so it does not make sense to set them only for the course page. If you try, you&#039;ll get a &amp;lt;tt&amp;gt;coding_exception&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== get_fast_modinfo data ==&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt; now returns an object of class course_modinfo, which itself contains cm_info objects about each activity. (These are entirely backward-compatible with the previous return value.)&lt;br /&gt;
&lt;br /&gt;
Please see the documentation for more information about these classes. All fields are now documented and there are some new functions. For example, here is how to get a single cm_info from $modinfo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$modinfo = get_fast_modinfo($course);&lt;br /&gt;
$cm = $modinfo-&amp;gt;get_cm($cmid);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The cm_info objects contain additional information that is not present in the course_modules database row, such as the module&#039;s name, and the icon and associated content mentioned above. In order to distinguish these from the plain database objects, you can specify the cm_info class in a function definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function my_clever_function(cm_info $cm) {&lt;br /&gt;
    if (!$cm-&amp;gt;uservisible) {&lt;br /&gt;
        // the module is not visible or available to current user,&lt;br /&gt;
        // so do something clever instead&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By specifying cm_info in the parameter list, you&#039;ll cause PHP to give an error if anyone tries to call that function with a $cm object that just came from the database row, instead of from &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt;. (It is good practice to always get $cm from get_fast_modinfo, but there might be exceptions.) &lt;br /&gt;
&lt;br /&gt;
Of course, this is only necessary if your function relies on a feature that is specific to cm_info, such as the &#039;uservisible&#039; field above. If your function only uses fields which are present in the database row, then there&#039;s no need to require cm_info.&lt;br /&gt;
&lt;br /&gt;
== More documentation ==&lt;br /&gt;
&lt;br /&gt;
All three classes for this API are in the core file &amp;lt;tt&amp;gt;lib/modinfolib.php&amp;lt;/tt&amp;gt; and contain complete PHPdoc information for all fields and functions.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81339</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81339"/>
		<updated>2011-02-16T18:15:17Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The features described here are available since Moodle 2.0.2.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
A new API allows you to customise how your module displays on the main course page:&lt;br /&gt;
&lt;br /&gt;
* You can display custom HTML below the link to your module. &lt;br /&gt;
&lt;br /&gt;
* If your module does not have a link (like Label, where it is only for display on the main page) then you can remove the link from the main page and from all navigation etc.&lt;br /&gt;
&lt;br /&gt;
* You can display HTML next to the link to your module that indicates dynamic information (like Forum, where it displays information about unread messages).&lt;br /&gt;
&lt;br /&gt;
* You can display additional icons next to the other module editing icons when the user is editing the page.&lt;br /&gt;
&lt;br /&gt;
In addition, existing things you could already do (like change the icon on the main page) are still available when using the new API.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt; function now returns specific classes which are documented and which you can use to obtain new information about modules.&lt;br /&gt;
&lt;br /&gt;
== Backward compatibility ==&lt;br /&gt;
&lt;br /&gt;
All modules and code written for Moodle 2.0 should continue to behave in exactly the same manner. There is no need to change existing modules for this API unless you want to use the new features.&lt;br /&gt;
&lt;br /&gt;
== Removing your link ==&lt;br /&gt;
&lt;br /&gt;
If your module should not appear in navigation and in other lists of modules to visit or get information for, like Label, the easiest way to remove that link is to return true for FEATURE_NO_VIEW_LINK in your module&#039;s &amp;lt;tt&amp;gt;_supports&amp;lt;/tt&amp;gt; function.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, in cache: &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
The first place you can customise your module display is in the existing &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; API function. This function obtains information about the module which will be stored in the course cache (the &amp;lt;tt&amp;gt;modinfo&amp;lt;/tt&amp;gt; field of the course table).&lt;br /&gt;
&lt;br /&gt;
The course cache is only updated when somebody edits a module, so it can&#039;t be used for dynamic information - but it&#039;s okay if it takes a few database queries to calculate the data because it will be cached for future use.&lt;br /&gt;
&lt;br /&gt;
The function should return a value of class &amp;lt;tt&amp;gt;cached_cm_info&amp;lt;/tt&amp;gt;. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_get_coursemodule_info($cm) {&lt;br /&gt;
    $info = new cached_cm_info;&lt;br /&gt;
    $info-&amp;gt;content = &#039;&amp;lt;p&amp;gt;This will display below the module.&amp;lt;/p&amp;gt;&#039;;&lt;br /&gt;
    return $info;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can change several properties which are documented in that class definition. If you don&#039;t change a property, its value remains default.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; - name of activity (text of the link on course page).&lt;br /&gt;
* &amp;lt;tt&amp;gt;icon&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;iconcomponent&amp;lt;/tt&amp;gt; - name and component name of icon to display by the link.&lt;br /&gt;
* &amp;lt;tt&amp;gt;content&amp;lt;/tt&amp;gt; - extra HTML content to display below the module link on course page (not shown in navigation etc).&lt;br /&gt;
* &amp;lt;tt&amp;gt;customdata&amp;lt;/tt&amp;gt; - arbitrary extra PHP data to store in modinfo cache; useful if, for performance reasons, your module needs to store data that should be accessible very quickly from other parts of the course.&lt;br /&gt;
* &amp;lt;tt&amp;gt;extraclasses&amp;lt;/tt&amp;gt; - extra CSS class or classes that will be added to the activity on the main page, so that you can alter the styling.&lt;br /&gt;
* &amp;lt;tt&amp;gt;onclick&amp;lt;/tt&amp;gt; - already-escaped HTML that will be inserted as the value of the onclick attribute.&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t need the information to be cached (it can be retrieved very quickly without making any database queries) then you might consider using one of the functions below instead, in order to avoid unnecessarily increasing the size of the course cache. Although the headings mention the current user, you can of course use those functions in a way that doesn&#039;t depend on the current user.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user ==&lt;br /&gt;
&lt;br /&gt;
You can customise module display dynamically (when the page loads). For example you might want to alter it based on the permissions of the current user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_dynamic(cm_info $cm) {&lt;br /&gt;
    $context = get_context_instance(CONTEXT_MODULE, $cm-&amp;gt;id);&lt;br /&gt;
    if (!has_capability(&#039;some/capability&#039;, $context)) {&lt;br /&gt;
        $cm-&amp;gt;set_user_visible(false);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This code can affect the navigation, and whether users are permitted to access the module (as above). It runs on all pages within the course, so it&#039;s very important that you do not put slow code in this function: it should not make any database queries. &lt;br /&gt;
&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;set_user_visible&amp;lt;/tt&amp;gt; function shown, you can also set many other things such as additional editing icons which will appear if editing mode is enabled. See the cm_info class documentation for more information. &lt;br /&gt;
&lt;br /&gt;
Most things are set using functions (as above; another example would be &amp;lt;tt&amp;gt;set_content&amp;lt;/tt&amp;gt; which sets the same content data as mentioned in the previous section) while other things can be set directly using public variables.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user, on course page only ==&lt;br /&gt;
&lt;br /&gt;
Sometimes you need to display custom information for the current user that appears only on the course view page. For example, the forum module displays unread information on the view page. This information doesn&#039;t show on other pages (it isn&#039;t included in the navigation, for instance).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_view(cm_info $cm) {&lt;br /&gt;
    $cm-&amp;gt;set_after_link(&#039;Last tadpole: 22:17&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Because this function only runs when looking at the course page:&lt;br /&gt;
&lt;br /&gt;
* It is OK to do tasks which may require some database queries (such as checking for unread forum messages), although obviously this should be kept to a minimum. In particular, care should be taken so that if there are 20 instances of the activity on the course page, it doesn&#039;t make 20 separate queries to obtain the information.&lt;br /&gt;
&lt;br /&gt;
* Inside this function you cannot set options which affect the appearance or access to the activity on other pages; for example, you cannot turn off the uservisible flag as shown in the previous example. This is because these options are required on other pages (e.g. to display navigation) so it does not make sense to set them only for the course page. If you try, you&#039;ll get a &amp;lt;tt&amp;gt;coding_exception&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== get_fast_modinfo data ==&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt; now returns an object of class course_modinfo, which itself contains cm_info objects about each activity. (These are entirely backward-compatible with the previous return value.)&lt;br /&gt;
&lt;br /&gt;
Please see the documentation for more information about these classes. All fields are now documented and there are some new functions. For example, here is how to get a single cm_info from $modinfo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$modinfo = get_fast_modinfo($course);&lt;br /&gt;
$cm = $modinfo-&amp;gt;get_cm($cmid);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The cm_info objects contain additional information that is not present in the course_modules database row, such as the module&#039;s name, and the icon and associated content mentioned above. In order to distinguish these from the plain database objects, you can specify the cm_info class in a function definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function my_clever_function(cm_info $cm) {&lt;br /&gt;
    if (!$cm-&amp;gt;uservisible) {&lt;br /&gt;
        // the module is not visible or available to current user,&lt;br /&gt;
        // so do something clever instead&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By specifying cm_info in the parameter list, you&#039;ll cause PHP to give an error if anyone tries to call that function with a $cm object that just came from the database row, instead of from &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt;. (It is good practice to always get $cm from get_fast_modinfo, but there might be exceptions.) &lt;br /&gt;
&lt;br /&gt;
Of course, this is only necessary if your function relies on a feature that is specific to cm_info, such as the &#039;uservisible&#039; field above. If your function only uses fields which are present in the database row, then there&#039;s no need to require cm_info.&lt;br /&gt;
&lt;br /&gt;
== More documentation ==&lt;br /&gt;
&lt;br /&gt;
All three classes for this API are in the core file &amp;lt;tt&amp;gt;lib/modinfolib.php&amp;lt;/tt&amp;gt; and contain complete PHPdoc information for all fields and functions.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81338</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81338"/>
		<updated>2011-02-16T18:06:02Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Removing your link */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The features described here are available since Moodle 2.0.2.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
A new API allows you to customise how your module displays on the main course page:&lt;br /&gt;
&lt;br /&gt;
* You can display custom HTML below the link to your module. &lt;br /&gt;
&lt;br /&gt;
* If your module does not have a link (like Label, where it is only for display on the main page) then you can remove the link from the main page and from all navigation etc.&lt;br /&gt;
&lt;br /&gt;
* You can display HTML next to the link to your module that indicates dynamic information (like Forum, where it displays information about unread messages).&lt;br /&gt;
&lt;br /&gt;
* You can display additional icons next to the other module editing icons when the user is editing the page.&lt;br /&gt;
&lt;br /&gt;
In addition, existing things you could already do (like change the icon on the main page) are still available when using the new API.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt; function now returns specific classes which are documented and which you can use to obtain new information about modules.&lt;br /&gt;
&lt;br /&gt;
== Removing your link ==&lt;br /&gt;
&lt;br /&gt;
If your module should not appear in navigation and in other lists of modules to visit or get information for, like Label, the easiest way to remove that link is to return true for FEATURE_NO_VIEW_LINK in your module&#039;s &amp;lt;tt&amp;gt;_supports&amp;lt;/tt&amp;gt; function.&lt;br /&gt;
&lt;br /&gt;
== Backward compatibility ==&lt;br /&gt;
&lt;br /&gt;
All modules and code written for Moodle 2.0 should continue to behave in exactly the same manner. There is no need to change existing modules for this API.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, in cache: &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
The first place you can customise your module display is in the existing &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; API function. This function obtains information about the module which will be stored in the course cache (the &amp;lt;tt&amp;gt;modinfo&amp;lt;/tt&amp;gt; field of the course table).&lt;br /&gt;
&lt;br /&gt;
The course cache is only updated when somebody edits a module, so it can&#039;t be used for dynamic information - but it&#039;s okay if it takes a few database queries to calculate the data because it will be cached for future use.&lt;br /&gt;
&lt;br /&gt;
The function should return a value of class &amp;lt;tt&amp;gt;cached_cm_info&amp;lt;/tt&amp;gt;. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_get_coursemodule_info($cm) {&lt;br /&gt;
    $info = new cached_cm_info;&lt;br /&gt;
    $info-&amp;gt;content = &#039;&amp;lt;p&amp;gt;This will display below the module.&amp;lt;/p&amp;gt;&#039;;&lt;br /&gt;
    return $info;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can change several properties which are documented in that class definition. If you don&#039;t change a property, its value remains default.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; - name of activity (text of the link on course page).&lt;br /&gt;
* &amp;lt;tt&amp;gt;icon&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;iconcomponent&amp;lt;/tt&amp;gt; - name and component name of icon to display by the link.&lt;br /&gt;
* &amp;lt;tt&amp;gt;content&amp;lt;/tt&amp;gt; - extra HTML content to display below the module link on course page (not shown in navigation etc).&lt;br /&gt;
* &amp;lt;tt&amp;gt;customdata&amp;lt;/tt&amp;gt; - arbitrary extra PHP data to store in modinfo cache; useful if, for performance reasons, your module needs to store data that should be accessible very quickly from other parts of the course.&lt;br /&gt;
* &amp;lt;tt&amp;gt;extraclasses&amp;lt;/tt&amp;gt; - extra CSS class or classes that will be added to the activity on the main page, so that you can alter the styling.&lt;br /&gt;
* &amp;lt;tt&amp;gt;onclick&amp;lt;/tt&amp;gt; - already-escaped HTML that will be inserted as the value of the onclick attribute.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user ==&lt;br /&gt;
&lt;br /&gt;
You can customise module display dynamically (when the page loads). For example you might want to alter it based on the permissions of the current user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_dynamic(cm_info $cm) {&lt;br /&gt;
    $context = get_context_instance(CONTEXT_MODULE, $cm-&amp;gt;id);&lt;br /&gt;
    if (!has_capability(&#039;some/capability&#039;, $context)) {&lt;br /&gt;
        $cm-&amp;gt;set_user_visible(false);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This code can affect the navigation, and whether users are permitted to access the module (as above). It runs on all pages within the course, so it&#039;s very important that you do not put slow code in this function: it should not make any database queries. &lt;br /&gt;
&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;set_user_visible&amp;lt;/tt&amp;gt; function shown, you can also set many other things such as additional editing icons which will appear if editing mode is enabled. See the cm_info class documentation for more information. &lt;br /&gt;
&lt;br /&gt;
Most things are set using functions (as above; another example would be &amp;lt;tt&amp;gt;set_content&amp;lt;/tt&amp;gt; which sets the same content data as mentioned in the previous section) while other things can be set directly using public variables.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user, on course page only ==&lt;br /&gt;
&lt;br /&gt;
Sometimes you need to display custom information for the current user that appears only on the course view page. For example, the forum module displays unread information on the view page. This information doesn&#039;t show on other pages (it isn&#039;t included in the navigation, for instance).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_view(cm_info $cm) {&lt;br /&gt;
    $cm-&amp;gt;set_after_link(&#039;Last tadpole: 22:17&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Because this function only runs when looking at the course page:&lt;br /&gt;
&lt;br /&gt;
* It is OK to do tasks which may require some database queries (such as checking for unread forum messages), although obviously this should be kept to a minimum. In particular, care should be taken so that if there are 20 instances of the activity on the course page, it doesn&#039;t make 20 separate queries to obtain the information.&lt;br /&gt;
&lt;br /&gt;
* Inside this function you cannot set options which affect the appearance or access to the activity on other pages; for example, you cannot turn off the uservisible flag as shown in the previous example. This is because these options are required on other pages (e.g. to display navigation) so it does not make sense to set them only for the course page. If you try, you&#039;ll get a &amp;lt;tt&amp;gt;coding_exception&amp;lt;/tt&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81337</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81337"/>
		<updated>2011-02-16T18:05:39Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Summary */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The features described here are available since Moodle 2.0.2.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
A new API allows you to customise how your module displays on the main course page:&lt;br /&gt;
&lt;br /&gt;
* You can display custom HTML below the link to your module. &lt;br /&gt;
&lt;br /&gt;
* If your module does not have a link (like Label, where it is only for display on the main page) then you can remove the link from the main page and from all navigation etc.&lt;br /&gt;
&lt;br /&gt;
* You can display HTML next to the link to your module that indicates dynamic information (like Forum, where it displays information about unread messages).&lt;br /&gt;
&lt;br /&gt;
* You can display additional icons next to the other module editing icons when the user is editing the page.&lt;br /&gt;
&lt;br /&gt;
In addition, existing things you could already do (like change the icon on the main page) are still available when using the new API.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;get_fast_modinfo&amp;lt;/tt&amp;gt; function now returns specific classes which are documented and which you can use to obtain new information about modules.&lt;br /&gt;
&lt;br /&gt;
== Removing your link ==&lt;br /&gt;
&lt;br /&gt;
If your module should not have a link in navigation etc., like Label, the easiest way to remove that link is to return true for FEATURE_NO_VIEW_LINK in your module&#039;s &amp;lt;tt&amp;gt;_supports&amp;lt;/tt&amp;gt; function.&lt;br /&gt;
&lt;br /&gt;
== Backward compatibility ==&lt;br /&gt;
&lt;br /&gt;
All modules and code written for Moodle 2.0 should continue to behave in exactly the same manner. There is no need to change existing modules for this API.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, in cache: &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
The first place you can customise your module display is in the existing &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; API function. This function obtains information about the module which will be stored in the course cache (the &amp;lt;tt&amp;gt;modinfo&amp;lt;/tt&amp;gt; field of the course table).&lt;br /&gt;
&lt;br /&gt;
The course cache is only updated when somebody edits a module, so it can&#039;t be used for dynamic information - but it&#039;s okay if it takes a few database queries to calculate the data because it will be cached for future use.&lt;br /&gt;
&lt;br /&gt;
The function should return a value of class &amp;lt;tt&amp;gt;cached_cm_info&amp;lt;/tt&amp;gt;. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_get_coursemodule_info($cm) {&lt;br /&gt;
    $info = new cached_cm_info;&lt;br /&gt;
    $info-&amp;gt;content = &#039;&amp;lt;p&amp;gt;This will display below the module.&amp;lt;/p&amp;gt;&#039;;&lt;br /&gt;
    return $info;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can change several properties which are documented in that class definition. If you don&#039;t change a property, its value remains default.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; - name of activity (text of the link on course page).&lt;br /&gt;
* &amp;lt;tt&amp;gt;icon&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;iconcomponent&amp;lt;/tt&amp;gt; - name and component name of icon to display by the link.&lt;br /&gt;
* &amp;lt;tt&amp;gt;content&amp;lt;/tt&amp;gt; - extra HTML content to display below the module link on course page (not shown in navigation etc).&lt;br /&gt;
* &amp;lt;tt&amp;gt;customdata&amp;lt;/tt&amp;gt; - arbitrary extra PHP data to store in modinfo cache; useful if, for performance reasons, your module needs to store data that should be accessible very quickly from other parts of the course.&lt;br /&gt;
* &amp;lt;tt&amp;gt;extraclasses&amp;lt;/tt&amp;gt; - extra CSS class or classes that will be added to the activity on the main page, so that you can alter the styling.&lt;br /&gt;
* &amp;lt;tt&amp;gt;onclick&amp;lt;/tt&amp;gt; - already-escaped HTML that will be inserted as the value of the onclick attribute.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user ==&lt;br /&gt;
&lt;br /&gt;
You can customise module display dynamically (when the page loads). For example you might want to alter it based on the permissions of the current user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_dynamic(cm_info $cm) {&lt;br /&gt;
    $context = get_context_instance(CONTEXT_MODULE, $cm-&amp;gt;id);&lt;br /&gt;
    if (!has_capability(&#039;some/capability&#039;, $context)) {&lt;br /&gt;
        $cm-&amp;gt;set_user_visible(false);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This code can affect the navigation, and whether users are permitted to access the module (as above). It runs on all pages within the course, so it&#039;s very important that you do not put slow code in this function: it should not make any database queries. &lt;br /&gt;
&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;set_user_visible&amp;lt;/tt&amp;gt; function shown, you can also set many other things such as additional editing icons which will appear if editing mode is enabled. See the cm_info class documentation for more information. &lt;br /&gt;
&lt;br /&gt;
Most things are set using functions (as above; another example would be &amp;lt;tt&amp;gt;set_content&amp;lt;/tt&amp;gt; which sets the same content data as mentioned in the previous section) while other things can be set directly using public variables.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user, on course page only ==&lt;br /&gt;
&lt;br /&gt;
Sometimes you need to display custom information for the current user that appears only on the course view page. For example, the forum module displays unread information on the view page. This information doesn&#039;t show on other pages (it isn&#039;t included in the navigation, for instance).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_view(cm_info $cm) {&lt;br /&gt;
    $cm-&amp;gt;set_after_link(&#039;Last tadpole: 22:17&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Because this function only runs when looking at the course page:&lt;br /&gt;
&lt;br /&gt;
* It is OK to do tasks which may require some database queries (such as checking for unread forum messages), although obviously this should be kept to a minimum. In particular, care should be taken so that if there are 20 instances of the activity on the course page, it doesn&#039;t make 20 separate queries to obtain the information.&lt;br /&gt;
&lt;br /&gt;
* Inside this function you cannot set options which affect the appearance or access to the activity on other pages; for example, you cannot turn off the uservisible flag as shown in the previous example. This is because these options are required on other pages (e.g. to display navigation) so it does not make sense to set them only for the course page. If you try, you&#039;ll get a &amp;lt;tt&amp;gt;coding_exception&amp;lt;/tt&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81336</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81336"/>
		<updated>2011-02-16T18:03:45Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Customising module display, in course cache: _get_coursemodule_info */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The features described here are available since Moodle 2.0.2.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
A new API allows you to customise how your module displays on the main course page:&lt;br /&gt;
&lt;br /&gt;
* You can display custom HTML below the link to your module. &lt;br /&gt;
&lt;br /&gt;
* If your module does not have a link (like Label, where it is only for display on the main page) then you can remove the link from the main page and from all navigation etc.&lt;br /&gt;
&lt;br /&gt;
* You can display HTML next to the link to your module that indicates dynamic information (like Forum, where it displays information about unread messages).&lt;br /&gt;
&lt;br /&gt;
* You can display additional icons next to the other module editing icons when the user is editing the page.&lt;br /&gt;
&lt;br /&gt;
In addition, existing things you could already do (like change the icon on the main page) are still available when using the new API.&lt;br /&gt;
&lt;br /&gt;
== Backward compatibility ==&lt;br /&gt;
&lt;br /&gt;
All modules and code written for Moodle 2.0 should continue to behave in exactly the same manner. There is no need to change existing modules for this API.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, in cache: &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
The first place you can customise your module display is in the existing &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; API function. This function obtains information about the module which will be stored in the course cache (the &amp;lt;tt&amp;gt;modinfo&amp;lt;/tt&amp;gt; field of the course table).&lt;br /&gt;
&lt;br /&gt;
The course cache is only updated when somebody edits a module, so it can&#039;t be used for dynamic information - but it&#039;s okay if it takes a few database queries to calculate the data because it will be cached for future use.&lt;br /&gt;
&lt;br /&gt;
The function should return a value of class &amp;lt;tt&amp;gt;cached_cm_info&amp;lt;/tt&amp;gt;. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_get_coursemodule_info($cm) {&lt;br /&gt;
    $info = new cached_cm_info;&lt;br /&gt;
    $info-&amp;gt;content = &#039;&amp;lt;p&amp;gt;This will display below the module.&amp;lt;/p&amp;gt;&#039;;&lt;br /&gt;
    return $info;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can change several properties which are documented in that class definition. If you don&#039;t change a property, its value remains default.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; - name of activity (text of the link on course page).&lt;br /&gt;
* &amp;lt;tt&amp;gt;icon&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;iconcomponent&amp;lt;/tt&amp;gt; - name and component name of icon to display by the link.&lt;br /&gt;
* &amp;lt;tt&amp;gt;content&amp;lt;/tt&amp;gt; - extra HTML content to display below the module link on course page (not shown in navigation etc).&lt;br /&gt;
* &amp;lt;tt&amp;gt;customdata&amp;lt;/tt&amp;gt; - arbitrary extra PHP data to store in modinfo cache; useful if, for performance reasons, your module needs to store data that should be accessible very quickly from other parts of the course.&lt;br /&gt;
* &amp;lt;tt&amp;gt;extraclasses&amp;lt;/tt&amp;gt; - extra CSS class or classes that will be added to the activity on the main page, so that you can alter the styling.&lt;br /&gt;
* &amp;lt;tt&amp;gt;onclick&amp;lt;/tt&amp;gt; - already-escaped HTML that will be inserted as the value of the onclick attribute.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user ==&lt;br /&gt;
&lt;br /&gt;
You can customise module display dynamically (when the page loads). For example you might want to alter it based on the permissions of the current user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_dynamic(cm_info $cm) {&lt;br /&gt;
    $context = get_context_instance(CONTEXT_MODULE, $cm-&amp;gt;id);&lt;br /&gt;
    if (!has_capability(&#039;some/capability&#039;, $context)) {&lt;br /&gt;
        $cm-&amp;gt;set_user_visible(false);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
This code can affect the navigation, and whether users are permitted to access the module (as above). It runs on all pages within the course, so it&#039;s very important that you do not put slow code in this function: it should not make any database queries. &lt;br /&gt;
&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;set_user_visible&amp;lt;/tt&amp;gt; function shown, you can also set many other things such as additional editing icons which will appear if editing mode is enabled. See the cm_info class documentation for more information. &lt;br /&gt;
&lt;br /&gt;
Most things are set using functions (as above; another example would be &amp;lt;tt&amp;gt;set_content&amp;lt;/tt&amp;gt; which sets the same content data as mentioned in the previous section) while other things can be set directly using public variables.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Customising module display, for current user, on course page only ==&lt;br /&gt;
&lt;br /&gt;
Sometimes you need to display custom information for the current user that appears only on the course view page. For example, the forum module displays unread information on the view page. This information doesn&#039;t show on other pages (it isn&#039;t included in the navigation, for instance).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_cm_info_view(cm_info $cm) {&lt;br /&gt;
    $cm-&amp;gt;set_after_link(&#039;Last tadpole: 22:17&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Because this function only runs when looking at the course page:&lt;br /&gt;
&lt;br /&gt;
* It is OK to do tasks which may require some database queries (such as checking for unread forum messages), although obviously this should be kept to a minimum. In particular, care should be taken so that if there are 20 instances of the activity on the course page, it doesn&#039;t make 20 separate queries to obtain the information.&lt;br /&gt;
&lt;br /&gt;
* Inside this function you cannot set options which affect the appearance or access to the activity on other pages; for example, you cannot turn off the uservisible flag as shown in the previous example. This is because these options are required on other pages (e.g. to display navigation) so it does not make sense to set them only for the course page. If you try, you&#039;ll get a &amp;lt;tt&amp;gt;coding_exception&amp;lt;/tt&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81335</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81335"/>
		<updated>2011-02-16T17:44:11Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Customising module display, in course cache: _get_coursemodule_info */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The features described here are available since Moodle 2.0.2.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
A new API allows you to customise how your module displays on the main course page:&lt;br /&gt;
&lt;br /&gt;
* You can display custom HTML below the link to your module. &lt;br /&gt;
&lt;br /&gt;
* If your module does not have a link (like Label, where it is only for display on the main page) then you can remove the link from the main page and from all navigation etc.&lt;br /&gt;
&lt;br /&gt;
* You can display HTML next to the link to your module that indicates dynamic information (like Forum, where it displays information about unread messages).&lt;br /&gt;
&lt;br /&gt;
* You can display additional icons next to the other module editing icons when the user is editing the page.&lt;br /&gt;
&lt;br /&gt;
In addition, existing things you could already do (like change the icon on the main page) are still available when using the new API.&lt;br /&gt;
&lt;br /&gt;
== Backward compatibility ==&lt;br /&gt;
&lt;br /&gt;
All modules and code written for Moodle 2.0 should continue to behave in exactly the same manner. There is no need to change existing modules for this API.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, in course cache: &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
The first place you can customise your module display is in the existing &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; API function. This function obtains information about the module which will be stored in the course cache (the &amp;lt;tt&amp;gt;modinfo&amp;lt;/tt&amp;gt; field of the course table).&lt;br /&gt;
&lt;br /&gt;
The course cache is only updated when somebody edits a module, so it can&#039;t be used for dynamic information - but it&#039;s okay if it takes a few database queries to calculate the data because it will be cached for future use.&lt;br /&gt;
&lt;br /&gt;
The function should return a value of class &amp;lt;tt&amp;gt;cached_cm_info&amp;lt;/tt&amp;gt;. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_get_coursemodule_info($cm) {&lt;br /&gt;
    $info = new cached_cm_info;&lt;br /&gt;
    $info-&amp;gt;content = &#039;&amp;lt;p&amp;gt;This will display below the module.&amp;lt;/p&amp;gt;&#039;;&lt;br /&gt;
    return $info;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can change several properties which are documented in that class definition. If you don&#039;t change a property, its value remains default.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; - name of activity (text of the link on course page).&lt;br /&gt;
* &amp;lt;tt&amp;gt;icon&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;iconcomponent&amp;lt;/tt&amp;gt; - name and component name of icon to display by the link.&lt;br /&gt;
* &amp;lt;tt&amp;gt;content&amp;lt;/tt&amp;gt; - extra HTML content to display below the module link on course page (not shown in navigation etc).&lt;br /&gt;
* &amp;lt;tt&amp;gt;customdata&amp;lt;/tt&amp;gt; - arbitrary extra PHP data to store in modinfo cache; useful if, for performance reasons, your module needs to store data that should be accessible very quickly from other parts of the course.&lt;br /&gt;
* &amp;lt;tt&amp;gt;extraclasses&amp;lt;/tt&amp;gt; - extra CSS class or classes that will be added to the activity on the main page, so that you can alter the styling.&lt;br /&gt;
* &amp;lt;tt&amp;gt;onclick&amp;lt;/tt&amp;gt; - already-escaped HTML that will be inserted as the value of the onclick attribute.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81334</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=81334"/>
		<updated>2011-02-16T17:43:55Z</updated>

		<summary type="html">&lt;p&gt;Quen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The features described here are available since Moodle 2.0.2.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
A new API allows you to customise how your module displays on the main course page:&lt;br /&gt;
&lt;br /&gt;
* You can display custom HTML below the link to your module. &lt;br /&gt;
&lt;br /&gt;
* If your module does not have a link (like Label, where it is only for display on the main page) then you can remove the link from the main page and from all navigation etc.&lt;br /&gt;
&lt;br /&gt;
* You can display HTML next to the link to your module that indicates dynamic information (like Forum, where it displays information about unread messages).&lt;br /&gt;
&lt;br /&gt;
* You can display additional icons next to the other module editing icons when the user is editing the page.&lt;br /&gt;
&lt;br /&gt;
In addition, existing things you could already do (like change the icon on the main page) are still available when using the new API.&lt;br /&gt;
&lt;br /&gt;
== Backward compatibility ==&lt;br /&gt;
&lt;br /&gt;
All modules and code written for Moodle 2.0 should continue to behave in exactly the same manner. There is no need to change existing modules for this API.&lt;br /&gt;
&lt;br /&gt;
== Customising module display, in course cache: &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
The first place you can customise your module display is in the existing &amp;lt;tt&amp;gt;_get_coursemodule_info&amp;lt;/tt&amp;gt; API function. This function obtains information about the module which will be stored in the course cache (the &amp;lt;tt&amp;gt;modinfo&amp;lt;/tt&amp;gt; field of the course table).&lt;br /&gt;
&lt;br /&gt;
The course cache is only updated when somebody edits a module, so it can&#039;t be used for dynamic information - but it&#039;s okay if it takes a few database queries to calculate the data because it will be cached for future use.&lt;br /&gt;
&lt;br /&gt;
The function should return a value of class &amp;lt;tt&amp;gt;cached_cm_info&amp;lt;/tt&amp;gt;. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function mod_frog_get_coursemodule_info($cm) {&lt;br /&gt;
    $info = new cached_cm_info;&lt;br /&gt;
    $info-&amp;gt;content = &#039;&amp;lt;p&amp;gt;This will display below the module.&amp;lt;/p&amp;gt;&#039;;&lt;br /&gt;
    return $info;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code php&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can change several properties which are documented in that class definition. If you don&#039;t change a property, its value remains default.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; - name of activity (text of the link on course page).&lt;br /&gt;
* &amp;lt;tt&amp;gt;icon&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;iconcomponent&amp;lt;/tt&amp;gt; - name and component name of icon to display by the link.&lt;br /&gt;
* &amp;lt;tt&amp;gt;content&amp;lt;/tt&amp;gt; - extra HTML content to display below the module link on course page (not shown in navigation etc).&lt;br /&gt;
* &amp;lt;tt&amp;gt;customdata&amp;lt;/tt&amp;gt; - arbitrary extra PHP data to store in modinfo cache; useful if, for performance reasons, your module needs to store data that should be accessible very quickly from other parts of the course.&lt;br /&gt;
* &amp;lt;tt&amp;gt;extraclasses&amp;lt;/tt&amp;gt; - extra CSS class or classes that will be added to the activity on the main page, so that you can alter the styling.&lt;br /&gt;
* &amp;lt;tt&amp;gt;onclick&amp;lt;/tt&amp;gt; - already-escaped HTML that will be inserted as the value of the onclick attribute.&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Using_the_File_API_in_Moodle_forms&amp;diff=81272</id>
		<title>Development:Using the File API in Moodle forms</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Using_the_File_API_in_Moodle_forms&amp;diff=81272"/>
		<updated>2011-02-11T17:23:47Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Convert internal relative links to absolute links */&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 [[Development:File API|File API]], and every file is associated with a component and 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 [[Development: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 [[Development: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 [[Development: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 [[Development:Using_the_File_API]].&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;accepted_types&#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;accepted_types&#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;
;accepted_types:(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;mod_glossary&#039;, &#039;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;mod_glossary&#039;, &#039;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;mod_somemodule&#039;, &#039;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;mod_somemodule&#039;, &#039;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));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The editor element can take following options: maxfiles, maxbytes, 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;mod_glossary&#039;, &#039;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;mod_glossary&#039;, &#039;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;component&#039;, &#039;proper_file_area&#039; and $entry-&amp;gt;id : correspond to the contextid, filearea and itemid columns in the [[Development: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;
        $context-&amp;gt;id, &#039;mod_mymodule&#039;, &#039;proper_file_area&#039;, $itemid);&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;
; $context-&amp;gt;id, &#039;mod_mymodule&#039;, &#039;proper_file_area&#039;, $itemid : 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;mod_glossary&#039;, &#039;entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_prepare_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;mod_glossary&#039;, &#039;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;mod_glossary&#039;, &#039;entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_postupdate_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;mod_glossary&#039;, &#039;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;
* [[Development:File API]]&lt;br /&gt;
* [[Development:Using the file API]]&lt;br /&gt;
* [[Development:Repository API]]&lt;br /&gt;
* [[Development: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>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Using_the_File_API_in_Moodle_forms&amp;diff=81271</id>
		<title>Development:Using the File API in Moodle forms</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Using_the_File_API_in_Moodle_forms&amp;diff=81271"/>
		<updated>2011-02-11T17:23:20Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Convert internal relative links to absolute links */&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 [[Development:File API|File API]], and every file is associated with a component and 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 [[Development: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 [[Development: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 [[Development: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 [[Development:Using_the_File_API]].&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;accepted_types&#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;accepted_types&#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;
;accepted_types:(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;mod_glossary&#039;, &#039;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;mod_glossary&#039;, &#039;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;mod_somemodule&#039;, &#039;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;mod_somemodule&#039;, &#039;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));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The editor element can take following options: maxfiles, maxbytes, 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;mod_glossary&#039;, &#039;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;mod_glossary&#039;, &#039;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;component&#039;, &#039;proper_file_area&#039; and $entry-&amp;gt;id : correspond to the contextid, filearea and itemid columns in the [[Development: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;
        $context-&amp;gt;id, &#039;mod_mymodule&#039;, &#039;proper_file_area&#039;, $itemid);&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/component/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;mod_glossary&#039;, &#039;entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_prepare_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;mod_glossary&#039;, &#039;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;mod_glossary&#039;, &#039;entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_postupdate_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;mod_glossary&#039;, &#039;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;
* [[Development:File API]]&lt;br /&gt;
* [[Development:Using the file API]]&lt;br /&gt;
* [[Development:Repository API]]&lt;br /&gt;
* [[Development: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>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Using_the_File_API_in_Moodle_forms&amp;diff=81270</id>
		<title>Development:Using the File API in Moodle forms</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Using_the_File_API_in_Moodle_forms&amp;diff=81270"/>
		<updated>2011-02-11T17:23:05Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Convert internal relative links to absolute links */&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 [[Development:File API|File API]], and every file is associated with a component and 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 [[Development: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 [[Development: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 [[Development: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 [[Development:Using_the_File_API]].&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;accepted_types&#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;accepted_types&#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;
;accepted_types:(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;mod_glossary&#039;, &#039;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;mod_glossary&#039;, &#039;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;mod_somemodule&#039;, &#039;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;mod_somemodule&#039;, &#039;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));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
The editor element can take following options: maxfiles, maxbytes, 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;mod_glossary&#039;, &#039;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;mod_glossary&#039;, &#039;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;component&#039;, &#039;proper_file_area&#039; and $entry-&amp;gt;id : correspond to the contextid, filearea and itemid columns in the [[Development: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;
        $context-&amp;gt;id, &#039;mod_moymodule&#039;, &#039;proper_file_area&#039;, $itemid);&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/component/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;mod_glossary&#039;, &#039;entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_prepare_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;mod_glossary&#039;, &#039;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;mod_glossary&#039;, &#039;entry&#039;, $entry-&amp;gt;id);&lt;br /&gt;
$entry = file_postupdate_standard_filemanager($entry, &#039;attachment&#039;, $attachmentoptions, $context, &#039;mod_glossary&#039;, &#039;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;
* [[Development:File API]]&lt;br /&gt;
* [[Development:Using the file API]]&lt;br /&gt;
* [[Development:Repository API]]&lt;br /&gt;
* [[Development: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>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Migrating_contrib_code_to_2.0&amp;diff=81015</id>
		<title>Development:Migrating contrib code to 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Migrating_contrib_code_to_2.0&amp;diff=81015"/>
		<updated>2011-02-02T12:21:19Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Backup and Restore */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The goal of this page is to provide a checklist of tasks to be considered to upgrade CONTRIB code to Moodle 2.0. Several significant changes have taken place including the File API, DB Layer, Navigation, Output renderer, etc. It would be good if we had a list that contributors could follow to help move them toward being able to migrate the code. Any help in building up this list is greatly appreciated. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Please add useful links...&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
The &#039;requires&#039; version in the plugin&#039;s version.php is checked to make sure it looks like Moodle 2.0. You should change this value to &#039;2010112400&#039; or later otherwise plugin installation will abort.&lt;br /&gt;
&lt;br /&gt;
== Database ==&lt;br /&gt;
* [[Development:DB_layer_2.0_migration_docs]]&lt;br /&gt;
&lt;br /&gt;
== File system ==&lt;br /&gt;
&lt;br /&gt;
== Forms API ==&lt;br /&gt;
* [[Development:Using the File API in Moodle forms]]&lt;br /&gt;
* [[Development:Using_the_file_API]]&lt;br /&gt;
&lt;br /&gt;
== Themes ==&lt;br /&gt;
* [[Development:Output_renderers]]&lt;br /&gt;
* [[Development:Text formats 2.0|Text formats 2.0]] how user-entered content is handled.&lt;br /&gt;
* [[Development:Migrating_your_code_to_the_2.0_rendering_API]] (n.b., some info outdated and may need updating)&lt;br /&gt;
* [[Development:Themes_2.0]]&lt;br /&gt;
&lt;br /&gt;
Some details of how to re-design image CSS for plugin code are in the main themes documentation: [[Development:Themes_2.0_creating_your_first_theme#Using_images_within_CSS]]&lt;br /&gt;
&lt;br /&gt;
Be aware that themes are now cached on the server and so emptying your browser cache will not refresh the CSS. This can only be done by going to the site administration-&amp;gt;appearance-&amp;gt;themes-&amp;gt;theme selector page and clicking the &#039;invalidate theme caches&#039; button.&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
JavaScript is included in different ways now and will need to be migrated, see the PHPDoc comments in /lib/outputrequirements.lib and some (slightly outdated) advice here: [[Development:JavaScript_guidelines]]&lt;br /&gt;
&lt;br /&gt;
== Backup and Restore ==&lt;br /&gt;
&lt;br /&gt;
* [[Development:Backup_2.0_for_developers]]&lt;br /&gt;
* [[Development:Restore_2.0_for_developers]]&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous ==&lt;br /&gt;
There are other coding changes such as:&lt;br /&gt;
* MDL-24063 which eliminates PARAM_CLEAN &lt;br /&gt;
* MDL-24058 about no longer using stripslashes or addslashes&lt;br /&gt;
* [[Deprecated_functions_in_2.0]]&lt;br /&gt;
&lt;br /&gt;
Please feel free to add others that come to mind. CONTRIB maintainers should check their 2.0 versions for these and make sure that they are appropriately handled. &lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Moodle_2.0_release_notes]]&lt;br /&gt;
* CONTRIB-1988&lt;br /&gt;
* http://cvs.moodle.org/moodle/blocks/upgrade.txt?view=markup&lt;br /&gt;
* http://cvs.moodle.org/moodle/mod/upgrade.txt?view=markup&lt;br /&gt;
* [[User:Frank_Ralf/Experience_of_converting_a_module_to_Moodle_2|Experience of converting a module to Moodle 2]] by Sam Marshall (perhaps that documentation should better live here? --[[User:Frank Ralf|Frank Ralf]] 16:36, 15 November 2010 (UTC))&lt;br /&gt;
* [[Development:Local_customisation]]&lt;br /&gt;
* [[Development:Migrating to 2.0 checklist]] list of things to look out for when upgrading a plug-in&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Migrating_contrib_code_to_2.0&amp;diff=81014</id>
		<title>Development:Migrating contrib code to 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Migrating_contrib_code_to_2.0&amp;diff=81014"/>
		<updated>2011-02-02T12:05:55Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Backup and Restore */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The goal of this page is to provide a checklist of tasks to be considered to upgrade CONTRIB code to Moodle 2.0. Several significant changes have taken place including the File API, DB Layer, Navigation, Output renderer, etc. It would be good if we had a list that contributors could follow to help move them toward being able to migrate the code. Any help in building up this list is greatly appreciated. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Please add useful links...&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
The &#039;requires&#039; version in the plugin&#039;s version.php is checked to make sure it looks like Moodle 2.0. You should change this value to &#039;2010112400&#039; or later otherwise plugin installation will abort.&lt;br /&gt;
&lt;br /&gt;
== Database ==&lt;br /&gt;
* [[Development:DB_layer_2.0_migration_docs]]&lt;br /&gt;
&lt;br /&gt;
== File system ==&lt;br /&gt;
&lt;br /&gt;
== Forms API ==&lt;br /&gt;
* [[Development:Using the File API in Moodle forms]]&lt;br /&gt;
* [[Development:Using_the_file_API]]&lt;br /&gt;
&lt;br /&gt;
== Themes ==&lt;br /&gt;
* [[Development:Output_renderers]]&lt;br /&gt;
* [[Development:Text formats 2.0|Text formats 2.0]] how user-entered content is handled.&lt;br /&gt;
* [[Development:Migrating_your_code_to_the_2.0_rendering_API]] (n.b., some info outdated and may need updating)&lt;br /&gt;
* [[Development:Themes_2.0]]&lt;br /&gt;
&lt;br /&gt;
Some details of how to re-design image CSS for plugin code are in the main themes documentation: [[Development:Themes_2.0_creating_your_first_theme#Using_images_within_CSS]]&lt;br /&gt;
&lt;br /&gt;
Be aware that themes are now cached on the server and so emptying your browser cache will not refresh the CSS. This can only be done by going to the site administration-&amp;gt;appearance-&amp;gt;themes-&amp;gt;theme selector page and clicking the &#039;invalidate theme caches&#039; button.&lt;br /&gt;
&lt;br /&gt;
== JavaScript ==&lt;br /&gt;
JavaScript is included in different ways now and will need to be migrated, see the PHPDoc comments in /lib/outputrequirements.lib and some (slightly outdated) advice here: [[Development:JavaScript_guidelines]]&lt;br /&gt;
&lt;br /&gt;
== Backup and Restore ==&lt;br /&gt;
&lt;br /&gt;
* [[Development:Backup_2.0_for_developers]]&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous ==&lt;br /&gt;
There are other coding changes such as:&lt;br /&gt;
* MDL-24063 which eliminates PARAM_CLEAN &lt;br /&gt;
* MDL-24058 about no longer using stripslashes or addslashes&lt;br /&gt;
* [[Deprecated_functions_in_2.0]]&lt;br /&gt;
&lt;br /&gt;
Please feel free to add others that come to mind. CONTRIB maintainers should check their 2.0 versions for these and make sure that they are appropriately handled. &lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Moodle_2.0_release_notes]]&lt;br /&gt;
* CONTRIB-1988&lt;br /&gt;
* http://cvs.moodle.org/moodle/blocks/upgrade.txt?view=markup&lt;br /&gt;
* http://cvs.moodle.org/moodle/mod/upgrade.txt?view=markup&lt;br /&gt;
* [[User:Frank_Ralf/Experience_of_converting_a_module_to_Moodle_2|Experience of converting a module to Moodle 2]] by Sam Marshall (perhaps that documentation should better live here? --[[User:Frank Ralf|Frank Ralf]] 16:36, 15 November 2010 (UTC))&lt;br /&gt;
* [[Development:Local_customisation]]&lt;br /&gt;
* [[Development:Migrating to 2.0 checklist]] list of things to look out for when upgrading a plug-in&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer]]&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=80345</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=80345"/>
		<updated>2011-01-14T12:13:07Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* course_modinfo */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is &#039;&#039;&#039;only a proposal&#039;&#039;&#039;. The functionality described doesn&#039;t exist yet in Moodle.&lt;br /&gt;
&lt;br /&gt;
== DRAFT 2 ==&lt;br /&gt;
&lt;br /&gt;
This is the second draft of the proposal, with changes based on Petr&#039;s comments. As well as/because of adding all the class stuff, this version no longer requires rebuild_course_cache and should be 100% backward compatible with all existing uses of modinfo.&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
&lt;br /&gt;
* Petr wanted dynamic/lazy computation of some of the access stuff - this is necessary for the forum unread data etc, but not feasible to do in this version for any of the data that is already included in modinfo, because of the requirement for 100% BC which is not possible with __get methods; so all the existing property values must be initialised (see below). This also means it&#039;s a less scary change, i.e. more moving code around and less new code.&lt;br /&gt;
&lt;br /&gt;
* I wanted a class for the overall thing, which I called course_modinfo (because it&#039;s basically from the modinfo field of the course table). Then I neeed a name for the class for an individual module instance info. Petr suggested module_info but I thought cm_info was a bit closer (module_info might sound like it was about a whole module, not one instance). Anyhow, both names subject to change if anyone has better suggestions.&lt;br /&gt;
&lt;br /&gt;
* Linking the cm_info to its parent course_modinfo gives a nice framework to do  caching/lazy-initialisation stuff. For instance when we calculate forum unread data in the mod_forum_cm_info_view function, we will get a request for a single $cminfo, but can actually retrieve the data for all forum instances on the course at once (same as at present).&lt;br /&gt;
&lt;br /&gt;
* I didn&#039;t specify exactly how to address modinfo caching issues (...if there are any) but because this proposal suggests moving the &#039;work out the values&#039; stuff into the class, and leaving get_fast_modinfo to deal only with caching, it should be a whole lot simpler to see how the caching works and alter it in future.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
We would like to create a generic API that allows the following:&lt;br /&gt;
&lt;br /&gt;
* Module display on front page can be customised, for example making it possible to create another module that behaves like Label (displaying arbitrary html rather than a link to activity view.php) - currently this is hardcoded hack for Label. This change should apply to other areas such as navigation block as well as course page.&lt;br /&gt;
&lt;br /&gt;
* Some modules can provide dynamic text, such as forum displaying unread messages. At present this is hardcoded so that only forum can do it.&lt;br /&gt;
&lt;br /&gt;
* Modules can be hidden completely, or greyed out, from the view of particular students according to either custom module behaviour, or (if not specified by module) default behaviour regarding a new capability moodle/course:viewactivity and the existing option for whether a non-available module is greyed out or hidden entirely.&lt;br /&gt;
&lt;br /&gt;
This should take advantage of the existing modinfo cache in order that performance is not adversely affected.&lt;br /&gt;
&lt;br /&gt;
== API improvement ==&lt;br /&gt;
&lt;br /&gt;
Following Petr&#039;s suggestions, we would also like to improve the API by switching the modinfo in-memory data structures to use defined classes instead of anonymous stdClass objects. This achieves the following:&lt;br /&gt;
&lt;br /&gt;
* Provides a location for documentation of these structures. (Currently they are undocumented.)&lt;br /&gt;
&lt;br /&gt;
* In future, allows functions to specify the required type (i.e. some functions require the information from modinfo, not just a row from the table; they will now be able to specify this). There is no plan to change function definitions immediately.&lt;br /&gt;
&lt;br /&gt;
* Where the type is defined, makes metadata available to IDEs, allowing code completion and automatic documentation viewing.&lt;br /&gt;
&lt;br /&gt;
The following requirements should also be met:&lt;br /&gt;
&lt;br /&gt;
* 100% backward compatibility for existing use of these structures by core and third-party modules. (Obviously, where these want to support the new features, such as the navigation supporting other label-like modules, there do need to be changes which are included in these patch. But if we forget something or it&#039;s third-party, it should keep working as at present.)&lt;br /&gt;
&lt;br /&gt;
* 100% backward compatibility for existing modinfo data (in database).&lt;br /&gt;
&lt;br /&gt;
== Removing existing hacks ==&lt;br /&gt;
&lt;br /&gt;
* Existing hacks regarding label in all areas of code (e.g. navigation, etc) will be changed from the logic &#039;is this a label?&#039; to the logic &#039;does this activity have a view page?&#039; (which will work for label too)&lt;br /&gt;
* Existing hacks regarding forum unread data will be removed and the forum unread code will be moved into the new API function. The code will be written in such a way as to have the same performance characteristics.&lt;br /&gt;
&lt;br /&gt;
== get_fast_modinfo change ==&lt;br /&gt;
&lt;br /&gt;
The get_fast_modinfo function will be changed to return a new object of type course_modinfo, which will be compatible with the existing $modinfo return value.&lt;br /&gt;
&lt;br /&gt;
While constructing this object, in addition to current behaviour, the system will:&lt;br /&gt;
&lt;br /&gt;
* support new values defined in _get_coursemodule_info&lt;br /&gt;
&lt;br /&gt;
* extend dynamic per-user calculation (that checks is something is visible to current user, -&amp;gt;uservisible) with additional checks&lt;br /&gt;
&lt;br /&gt;
* call the new module API (if provided) after calculating modinfo, to get dynamic information&lt;br /&gt;
&lt;br /&gt;
== course_modinfo ==&lt;br /&gt;
&lt;br /&gt;
The new class course_modinfo will contain properties:&lt;br /&gt;
&lt;br /&gt;
* courseid, userid (ints)&lt;br /&gt;
* sections (array of int =&amp;gt; array of int)&lt;br /&gt;
* cms (array of int =&amp;gt; cm_info)&lt;br /&gt;
* instances (array of string =&amp;gt; array of int =&amp;gt; cm_info)&lt;br /&gt;
* groups - groups that the current user belongs to - loaded only if needed i.e. if there are activities that are groupmembersonly, otherwise null&lt;br /&gt;
&lt;br /&gt;
These are identical to the values currently returned by modinfo, except for the use of cm_info class instead of bare stdClass for the information about modules.&lt;br /&gt;
&lt;br /&gt;
There are three options for handling this class:&lt;br /&gt;
&lt;br /&gt;
# Make these properties public so that they can be accessed exactly as at present.&lt;br /&gt;
# Make them private and use PHP magic __set and __get functions so that they can be read as normal but any attempt to write them would (while working) also cause a developer debug warning.&lt;br /&gt;
# Make the properties public, but deprecate them, and create separate get methods (get_courseid, get_userid, etc) which are recommended for future use.&lt;br /&gt;
&lt;br /&gt;
I initially favoured the second option but unfortunately, PHP being PHP, this doesn&#039;t quite work properly: specifically, you can&#039;t use the empty() function on such values. Since there might be places in the code that call empty(), maybe this isn&#039;t a good idea. &lt;br /&gt;
&lt;br /&gt;
Consequently I tend to the third one; we should make better get methods such as get_cm($cmid) as well as just get_cms(), which may help make new code more readable.&lt;br /&gt;
&lt;br /&gt;
=== Construction ===&lt;br /&gt;
&lt;br /&gt;
The code that creates this information should largely be moved to this class (either in a constructor or in init methods etc) from its current location in get_fast_modinfo. get_fast_modinfo should remain responsible only for caching these objects.&lt;br /&gt;
&lt;br /&gt;
== cm_info class ==&lt;br /&gt;
&lt;br /&gt;
=== Basic structure ===&lt;br /&gt;
&lt;br /&gt;
The new class cm_info will be constructed with a $parent (course_modinfo). Knowing its parent allows the class to carry out operations with regard to the whole course, where this is beneficial for performance, without needing extra parameters. There will be a get_modinfo() method to return this.&lt;br /&gt;
&lt;br /&gt;
=== Defined states ===&lt;br /&gt;
&lt;br /&gt;
This class has three states:&lt;br /&gt;
&lt;br /&gt;
* BASIC_DATA: class has the data from database modinfo table, and automatically completed data that is not related to the current user.&lt;br /&gt;
* DYNAMIC_DATA: class has all data that is available for most pages, including data related to the current request from the current user&#039;s permissions and the _cm_info_dynamic function if applicable.&lt;br /&gt;
* VIEW_DATA: class also has the data that is needed only on the course view page (or similar pages) from the _cm_info_view function if applicable (basically only used for forum unread data and stuff like that).&lt;br /&gt;
&lt;br /&gt;
When get_fast_modinfo returns, all returned objects will be in DYNAMIC_DATA state. This will be sufficient for determining whether the current user has access to the activity and for displaying the basic link etc. Basically the only thing it doesn&#039;t include is supplemental data that is only needed on the course page, such as forum unread data (get_after_link function). This data takes time to calculate so is generated on request.&lt;br /&gt;
&lt;br /&gt;
The intention is that getting to DYNAMIC_DATA state shouldn&#039;t require any database calls (certainly for the core modules). Getting to VIEW_DATA state may require database calls, e.g. to calculate the number of unread forum posts.&lt;br /&gt;
&lt;br /&gt;
=== Existing properties ===&lt;br /&gt;
&lt;br /&gt;
These properties are in current modinfo:&lt;br /&gt;
&lt;br /&gt;
* id, instance, course, idnumber, visible, groupmode, groupingid, groupmembersonly, indent, completion, availablefrom, availableuntil, showavailability - data from course_modules row&lt;br /&gt;
&lt;br /&gt;
* extra, icon, iconcomponent, modname, name, sectionnum, conditionscompletion, conditionsgrade, modplural (wtf is this there when the singular name isn&#039;t and both should be available with get_string?!) - data from modinfo which was computed when generating the cache&lt;br /&gt;
&lt;br /&gt;
* availableinfo, available, uservisible - data relating to the current user which is computed dynamically when obtaining the modinfo object&lt;br /&gt;
&lt;br /&gt;
This is the list of properties currently available in modinfo objects, so will be implemented as-is. The same question regarding use of __set, __get applies as above, but in this case it&#039;s perhaps more likely that people might call empty() on the existing properties; again, I propose retaining the above as public properties, but deprecated, and providing get_x methods for new use. Any new properties would be private and available only with get_ methods.&lt;br /&gt;
&lt;br /&gt;
Some of the get methods might have a slightly different definition to the raw properties. For example get_icon should use the provided data to return a moodle_icon object (if there is one? I am not sure), not a string.&lt;br /&gt;
&lt;br /&gt;
=== New data / get methods ===&lt;br /&gt;
&lt;br /&gt;
The new data and corresponding get methods are:&lt;br /&gt;
&lt;br /&gt;
* has_view() - true if the module should have a link to its view.php shown in navigation, be included in lists of &#039;did the user visit this module&#039; stats, etc. (Basically this is the &#039;is not a label&#039; method.)&lt;br /&gt;
* get_url() - returns moodle_url to module view.php, or null if has_view is false (this reduces code duplication in various places)&lt;br /&gt;
* get_content() # - returns HTML content to be displayed on the course page where this module is placed; appears below the link, if present (this is how Label displays content on course page) &lt;br /&gt;
* get_extra_classes() # - returns additional CSS classes to be added to the A or DIV tag(s) for this item on main course page. &lt;br /&gt;
* get_custom_data() - returns an optional mixed value containing custom data for this module, which needs to be available course-wide via the get_fast_modinfo function.&lt;br /&gt;
* get_after_link() # - returns HTML code which displays after the link.&lt;br /&gt;
* get_after_edit_icons() # - returns HTML code which displays after the standard icons  (hide, edit, delete, etc) when editing.&lt;br /&gt;
&lt;br /&gt;
Functions marked # require &#039;view information&#039;. This is additional information intended for use on the course view page and not in other places that use modinfo: the most obvious example (and the only one that affects core) is forum unread data. Consequently it is only obtained on request (see state information above). Obtaining view info requires calling the module&#039;s _cm_info_view function if it has one.&lt;br /&gt;
&lt;br /&gt;
=== Set methods ===&lt;br /&gt;
&lt;br /&gt;
Set methods are available for use only by _cm_info_dynamic and _cm_info_view functions; see below.&lt;br /&gt;
&lt;br /&gt;
== Modify _get_coursemodule_info ==&lt;br /&gt;
&lt;br /&gt;
There will be a change to the existing module API function _get_coursemodule_info which is used to update information before storing it in the database modinfo field. This change will retain backward compatibility. &lt;br /&gt;
&lt;br /&gt;
The function returns an object which is currently a stdClass object. This possibility will be retained for backward compatibility, with unchanged behaviour. All existing fields are supported:&lt;br /&gt;
&lt;br /&gt;
* name: name of instance or (for labels only) content of instance&lt;br /&gt;
* icon: name of icon or special weird string&lt;br /&gt;
* iconcomponent: component of icon, possibly works&lt;br /&gt;
* extra: extra data inserted somewhere horrible in the html&lt;br /&gt;
&lt;br /&gt;
These fields are all optional and may be left unset to accept the defaults.&lt;br /&gt;
&lt;br /&gt;
However a new class cached_cm_info can be returned instead.&lt;br /&gt;
&lt;br /&gt;
The class cached_cm_info has the following properties:&lt;br /&gt;
&lt;br /&gt;
* name, icon, iconcomponent: as before&lt;br /&gt;
* content: HTML content to be displayed on the course page where this module is placed; appears below the link, if present (this is how a Label-like module can display content on course page)&lt;br /&gt;
* customdata: A place to store a string or object containing custom data for this module, which needs to be available course-wide via the get_fast_modinfo function. If present, this data should be small in size.&lt;br /&gt;
* extraclasses: Extra CSS classes to add to the item on course page display, if required.&lt;br /&gt;
&lt;br /&gt;
(It does not support extra, which is deprecated on account of being stupid, at least unless I figure out some existing use case that really needs to be carried forward into a non-deprecated system.)&lt;br /&gt;
&lt;br /&gt;
=== Storing data ===&lt;br /&gt;
&lt;br /&gt;
Returning a stdClass object should result in identical content of the modinfo field in the database to present.&lt;br /&gt;
&lt;br /&gt;
Return cached_cm_info results in similar content but with extra fields -&amp;gt;content and -&amp;gt;customdata. These fields are only stored if they are non-null, i.e. it won&#039;t bulk up the database with empty &#039;content=nothing&#039; type data against every module.&lt;br /&gt;
&lt;br /&gt;
=== Reading label data ===&lt;br /&gt;
&lt;br /&gt;
The new content field is different to existing behaviour of the Label module, which stores its content in the &#039;name&#039; field as noted. When reading this data or processing it (for example in cm_info class), the system takes the following approach:&lt;br /&gt;
&lt;br /&gt;
* if the modname is &#039;label&#039; and there is no &#039;content&#039; field, then copy the &#039;name&#039; field into &#039;content&#039;.&lt;br /&gt;
&lt;br /&gt;
Note that this behaviour retains backward compatibility; the label still has a silly &#039;name&#039; value. For new code, has_view() returns false so it won&#039;t use name; for old code, the existing hard-coded exceptions for label will continue to work.&lt;br /&gt;
&lt;br /&gt;
== Extend dynamic calculation ==&lt;br /&gt;
&lt;br /&gt;
Currently the modinfo code makes the following checks that apply dynamically per-request (and do not directly come from the cache) in order to create the -&amp;gt;uservisible member variable.&lt;br /&gt;
&lt;br /&gt;
* If -&amp;gt;groupmembersonly is set, checks if the user belongs to group or has accessallgroups.&lt;br /&gt;
* If availability restrictions (date, grade, completion) are set, checks these (also even if available to current user, stores information into -&amp;gt;availableinfo, -&amp;gt;available for information when editing).&lt;br /&gt;
&lt;br /&gt;
My proposal is:&lt;br /&gt;
&lt;br /&gt;
* Make this part of the code (that &#039;specialises&#039; a single mod value for the current user/request) into a separate function within cm_info class, just to simplify it. This function should be the one that changes the cm_info state from BASIC_DATA to DYNAMIC_DATA.&lt;br /&gt;
* Add a check for the moodle/course:viewactivity capability; if user doesn&#039;t have this capability, set -&amp;gt;uservisible to false. Also check the option about what to do with hidden activities; if this is set to the default &#039;grey it out&#039;, then set -&amp;gt;inactive to true.&lt;br /&gt;
** Note: The default value for moodle/course:viewactivity should be true for all roles, even guest. This maintains existing behaviour. Sites that don&#039;t want guests to view activities can change the main role definition for guest.&lt;br /&gt;
* Call the _cm_info_dynamic function (below) if the current module supplies one.&lt;br /&gt;
&lt;br /&gt;
PERFORMANCE CONCERNS: Minimal. No new database queries are required, just a capability check and a function existence check.&lt;br /&gt;
&lt;br /&gt;
== New module API ==&lt;br /&gt;
&lt;br /&gt;
Two new module API functions will be defined. They both have one parameter: the cm_info object for this module, containing all the data from the modinfo cache.&lt;br /&gt;
&lt;br /&gt;
The functions are:&lt;br /&gt;
&lt;br /&gt;
* _cm_info_dynamic - add basic data that is always required (must be fast; should not make any database calls)&lt;br /&gt;
* _cm_info_view - add data that is required for the course view page (may make database calls)&lt;br /&gt;
&lt;br /&gt;
The functions both call set methods in the cm_info object. Examples:&lt;br /&gt;
&lt;br /&gt;
* set_user_visible(false) - hide activity entirely&lt;br /&gt;
* set_available(false) - grey out activity while it remains visible&lt;br /&gt;
* set_available_info(&#039;not available because you suck&#039;) - add explanation for why it&#039;s not available&lt;br /&gt;
* &#039;&#039;&#039;set_after_link&#039;&#039;&#039;(&#039;16 unread&#039;) # - add html code to appear after link&lt;br /&gt;
* &#039;&#039;&#039;set_edit_icons&#039;&#039;&#039;($html) # - add some code to appear when editing&lt;br /&gt;
* set_has_view(false) - make module not have link, navigation, etc even if it has a view.php&lt;br /&gt;
* set_content($html) # - add/change code to appear below the link&lt;br /&gt;
* set_name($text) - change text of the link&lt;br /&gt;
* set_extra_classes(&#039;whatever classes&#039;) # - add CSS classes&lt;br /&gt;
&lt;br /&gt;
The bold functions set cm_info data that cannot be set anywhere else (such as by system defaults or modinfo classes). This is basically because there didn&#039;t seem any general need to cache this data, particularly bearing in mind that modules can cache anything they like using the customdata property of cached_cm_info.&lt;br /&gt;
&lt;br /&gt;
_cm_info_view is restricted: for example, you cannot change user_visible status in view (because then it wouldn&#039;t work when checking access etc on other pages). You can only change the &#039;view&#039; properties. This can be enforced by making the set methods throw exceptions if called when the cm_info is in its already-defined state.&lt;br /&gt;
&lt;br /&gt;
Should it be necessary, this function can access the existing information in the cm_info object and also in the parent course_modinfo object (such as the user id) by calling get_modinfo on the cm_info.&lt;br /&gt;
&lt;br /&gt;
PERFORMANCE CONCERNS: None. Of standard modules, only the forum will implement this and only in the &#039;view&#039; version used just on course page; it will use the same code as at present. Custom modules will need to be written carefully in a similar manner so that they also perform well (ie do any queries once for whole course and store in global cache, not once per module).&lt;br /&gt;
&lt;br /&gt;
== Access permissions == &lt;br /&gt;
&lt;br /&gt;
When calling require_login with a $cm parameter, this will do get_fast_modinfo and check the is_user_visible() method in order to process per-user access restrictions:&lt;br /&gt;
&lt;br /&gt;
* groupmembersonly&lt;br /&gt;
* moodle/course:viewactivity&lt;br /&gt;
&lt;br /&gt;
PERFORMANCE CONCERNS: Because require_login like this is used on all module pages, we should use a profiler to evaluate before/after performance of this change and ensure that it isn&#039;t worsened. I suspect it won&#039;t be, because I think it already checks groupmembersonly anyway, so it&#039;s doing basically the same test; and I think most pages already call get_fast_modinfo e.g. for navigation block.&lt;br /&gt;
&lt;br /&gt;
NOTE: The way modules currently initialise is a bit inefficient, if get_fast_modinfo is to be used. The current sequence on most module pages is something like:&lt;br /&gt;
&lt;br /&gt;
* get course module (from id, supplied)&lt;br /&gt;
* get course&lt;br /&gt;
* get instance&lt;br /&gt;
&lt;br /&gt;
(3 queries)&lt;br /&gt;
&lt;br /&gt;
we could change this to&lt;br /&gt;
&lt;br /&gt;
* get course (from simple join using cmid, supplied)&lt;br /&gt;
* use get_fast_modinfo to get cm&lt;br /&gt;
* get instance&lt;br /&gt;
&lt;br /&gt;
(2 queries)&lt;br /&gt;
&lt;br /&gt;
Not suggesting that as part of this change (and it probably shouldn&#039;t be in this document), just saying...&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development_talk:Coding_style&amp;diff=80282</id>
		<title>Development talk:Coding style</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development_talk:Coding_style&amp;diff=80282"/>
		<updated>2011-01-13T17:46:01Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Database code */ new section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== PHP5 constructor ==&lt;br /&gt;
&lt;br /&gt;
Should we enforce the PHP5 constructor __construct() instead of $classname() [[User:Nicolas Connault|Nicolas Connault]] 19:55, 19 May 2009 (UTC)&lt;br /&gt;
:+1. I think we agreed about that some time ago (when igniting some 2.0 developments) --[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 00:03, 20 May 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Inline comments ==&lt;br /&gt;
&lt;br /&gt;
I have been using since ages ago &amp;quot;///&amp;quot; with outer alignment (I think that it was caused by some agreement long time ago, not my decision), for example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function check_moodle_environment(..., ..., ..., ...) {&lt;br /&gt;
&lt;br /&gt;
    $status = true;&lt;br /&gt;
&lt;br /&gt;
/// This are cached per request&lt;br /&gt;
    static $result = true;&lt;br /&gt;
    static $env_results;&lt;br /&gt;
    static $cache_exists = false;&lt;br /&gt;
&lt;br /&gt;
/// if we have results cached, use them&lt;br /&gt;
    if ($cache_exists) {&lt;br /&gt;
        $environment_results = $env_results;&lt;br /&gt;
/// No cache exists, calculate everything&lt;br /&gt;
    } else {&lt;br /&gt;
    /// Get the more recent version before the requested&lt;br /&gt;
        if (!$version = get_latest_version_available($version, $env_select)) {&lt;br /&gt;
            $status = false;&lt;br /&gt;
        }&lt;br /&gt;
        ....&lt;br /&gt;
        ....&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Is that allowed, or only the &amp;quot;//&amp;quot; with inner alignment as commented at [[Development:Coding style#Inline comments|inline comments]] --[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 00:03, 20 May 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I did start doing all mine this way, but if you look at the code it seems you and I are the only ones.  :P  It seems sensible to make the standard fit what most people are used to (and how most of the code looks). &lt;br /&gt;
-- [[User:Martin Dougiamas|Martin Dougiamas]] 06:02, 9 June 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eloy and Martin, this the only thing which consistently offends me about moodle coding style! Please go inline like all the cool kids ;-) --[[User:Dan Poltawski|Dan Poltawski]] 00:15, 12 June 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
LOL. +1 for inline in even lines + out in odd one  :-P&lt;br /&gt;
(seriously np with inline at all, it&#039;s only one habit easily modifiable) --[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 01:03, 12 June 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
==Trailing spaces==&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Lines should not contain trailing spaces. In order to facilitate this convention, most editors can be configured to strip trailing spaces, such as upon a save operation. However, if you are editing an existing Moodle file and are planning to submit your changes to CVS, please switch off that feature so that whitespace changes do not pollute CVS history (other developers will have trouble viewing what you&#039;ve done if every other line has been edited for whitespace).&amp;quot;&lt;br /&gt;
Wouldn&#039;t the CVS history only get &#039;polluted&#039; when someone fetched a file that DIDN&#039;T follow this principle and returned it following the principle? Isn&#039;t that the price that has to be paid to clean things up? Otherwise this is pretty scary for someone who feels: &amp;quot;Great I can turn on my editor to autostrip those when saving. BUT I have to remember to turn that off if submitting it! (Or become a &#039;polluter&#039;!)&amp;quot; &lt;br /&gt;
And what happens if I have already saved it locally while working on (and striped those trailing spaces) and later try to commit it. Maybe my ignorance about CVS is making me pose a &amp;quot;silly&amp;quot; question. [[User:Jeff Forssell|Jeff Forssell]] 07:17, 27 May 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
: Jeff, the point with coding guidelines is that all new code must follow all the guidelines. With old bad code, mostly it is better to make the smallest change necessary when, for example, you fix a bug. That makes it clearest what the purpose of your change was. However, any line you do change while fixing the bug, you can then improve the coding style on that line. &lt;br /&gt;
&lt;br /&gt;
: Really good editors actually have an option &amp;quot;Strip trailing whitespace when saving a file - but only from lines that I have edited&amp;quot;, which is what you really want.&lt;br /&gt;
&lt;br /&gt;
: The real way to avoid being a CVS polluter is to follow the best practice and always do a cvs diff and review all your changes (and if necessary amend them) before you do a CVS commit.--[[User:Tim Hunt|Tim Hunt]] 03:52, 1 June 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
=== Breaking up lines (e.g. huge arrays) ===&lt;br /&gt;
&lt;br /&gt;
The coding style says to align the start of the following lines with the element in the first.. e.g.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$myarray = array( &#039;fred&#039; =&amp;gt; &#039;blue&#039;,&lt;br /&gt;
                  &#039;tom&#039;  =&amp;gt; &#039;green&#039; );&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I have to say I don&#039;t like this. While it looks very pretty it is a code maintenance nightmare. If you change (say) the array name you have to change every other line just to keep it looking nice. Surely if you &#039;&#039;&#039;must&#039;&#039;&#039; have it looking like this, do this...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$myarray = array(&lt;br /&gt;
    &#039;fred&#039; =&amp;gt; &#039;blue&#039;,&lt;br /&gt;
    &#039;tom&#039;  =&amp;gt; &#039;green&#039; );&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It&#039;s the same issue with long lists of assignments - making all the RHSs line up. Completely unnecessary and just asking for errors. Don&#039;t encourage people to change code that they don&#039;t have to. If they don&#039;t touch it then they can&#039;t break it.   --[[User:Howard Miller|Howard Miller]] 13:52, 1 June 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
:Agree 100% with Howard, I prefer the 2nd alternative for sure, if not always, at least in places where indentation is already considerable. BTW, same for harcoded SQLs and other strings. --[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 13:58, 1 June 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
: +1 from me too for a fixed indent for follow-on lines. Actually I have always used 8-spaces (double the normal indent) ever since I started programming in Java and read their docing guidelines. I think it is helpful to have a different level of indent for a follow-on line and a block.&lt;br /&gt;
&lt;br /&gt;
: Sure, makes sense.  I&#039;ve fixed the docs.  The only exception IMO would be wrapping function parameters, which just look too wierd to me if you wrap them the same as arrays.  See [https://docs.moodle.org/en/Development:Coding_style#Wrapping_function_declarations the example]. -- [[User:Martin Dougiamas|Martin Dougiamas]] 06:08, 9 June 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
: At the expense of some white space...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function graded_users_iterator(&lt;br /&gt;
    $course, &lt;br /&gt;
    $grade_items = null, &lt;br /&gt;
    $groupid = 0,&lt;br /&gt;
    $sortfield1 = &#039;lastname&#039;, &lt;br /&gt;
    $sortorder1 = &#039;ASC&#039;,&lt;br /&gt;
    $sortfield2 = &#039;firstname&#039;, &lt;br /&gt;
    $sortorder2 = &#039;ASC&#039;) {&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: There is an argument that a function is badly thought out if it needs line after line of parameters. Won&#039;t go there though :-) --[[User:Howard Miller|Howard Miller]] 09:51, 23 June 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Function and method declaration ==&lt;br /&gt;
&lt;br /&gt;
[[Development:Coding_style#Function_and_method_declaration]] says &amp;quot;Don&#039;t leave spaces between the function name and the opening parenthesis for the arguments.&amp;quot; and yet the example below it completely disregards it. Please fix it. --[[User:Kumaraguru G|Kumaraguru G]] 21:32, 4 July 2009 (UTC)&lt;br /&gt;
: Hi, Kumaraguru. This is a wiki so you can just do it yourself ;-) --[[User:Frank Ralf|Frank Ralf]] 21:45, 4 July 2009 (UTC)&lt;br /&gt;
::Hi Frank, the page is protected and I don&#039;t have edit access :) --[[User:Kumaraguru G|Kumaraguru G]] 00:30, 5 July 2009 (UTC)&lt;br /&gt;
:::: Oops, sorry, totally forgot about that. --[[User:Frank Ralf|Frank Ralf]] 06:11, 6 July 2009 (UTC)&lt;br /&gt;
:::I just fixed that, and a few other things I noticed.--[[User:Tim Hunt|Tim Hunt]] 03:13, 5 July 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Running empty lines ==&lt;br /&gt;
&lt;br /&gt;
I propose to allow only single empty line as a separator. We can get rid of running empty lines at once using&lt;br /&gt;
&lt;br /&gt;
 for file in `find . -name &amp;quot;*.php&amp;quot;`; do cat -s $file &amp;gt; /tmp/cat.php &amp;amp;&amp;amp; cat /tmp/cat.php &amp;gt; $file  ; done;&lt;br /&gt;
&lt;br /&gt;
If agreed, the fix of the template for the beginning of a file is needed.&lt;br /&gt;
--[[User:David Mudrak|David Mudrak]] 15:25, 18 July 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
: I use a single line everywhere, except that I use two blank lines between classes. I don&#039;t think we need to make a big deal about this.--[[User:Tim Hunt|Tim Hunt]] 09:42, 19 July 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
== File comments ==&lt;br /&gt;
&lt;br /&gt;
1. About packages. I think what is there is OK. Separate packages for moodlecore and plugs. However, I don&#039;t think it is sufficient. There are some parts of the code (I am thinking of question) that are relatively self-contained. I think it is silly to put them all in moodlecore. I would rather use packages questionbank and questionengine. Is that OK. If so, can someone (I am happy to) add this to the docs. message, notes and comments may be other good candidates for separate packages. Also course and user.--[[User:Tim Hunt|Tim Hunt]] 10:10, 2 October 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
: [[User:Martin Dougiamas|Martin Dougiamas]] 06:31, 5 October 2009 (UTC): The problem is that we don&#039;t want to have hundreds of packages in the list at the top of http://phpdocs.moodle.org/HEAD/index.html so this has to be controlled.  In fact we already have all the ones you mentioned there ... there seems to be some confusion with different guesses/versions at what the name should be (group/groups is one example).  I&#039;m against courses and users because functions for these are in many different mixed files.   We have the new Moodle API externallib.php files in 2.0 which will really list all the main functions people would need.  Frankly it&#039;s a mess and I think trying to add lots more distinction makes it worse...  The main thing we were trying to do was separate core from modules.&lt;br /&gt;
&lt;br /&gt;
:: I disagree. It is stupid to worry about the list of packages being so long that it is difficult to understand if, once you get into a particular package, the list of stuff in there is so long it is completely incomprehensible. The whole point of packages is to break the codebase up into manageable and relatively independent chunks to help people understand it.&lt;br /&gt;
&lt;br /&gt;
:: Really, the problem here is that the PHPdocumentor template we are using is not up to the job of displaying a project as big as Moodle. We should use a template more like the JavaDoc on (http://java.sun.com/javase/6/docs/api/). That scales much better. Job for Jordan?&lt;br /&gt;
&lt;br /&gt;
:: And it would make sense to ensure that all the core packages were listed together. Perhpas we should adopt a convention like core-question-bank and core-question-engine for core sub-packages. I agree that too many packages would still be a mistake, but so is too few.--[[User:Tim Hunt|Tim Hunt]] 08:37, 5 October 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
::: [[User:Martin Dougiamas|Martin Dougiamas]] 12:58, 5 October 2009 (UTC): Isn&#039;t that what we have subpackages for?  We&#039;re using them that way all over the place already (eg see blog and deprecated etc under moodlecore).  That makes more sense to me than going for long names.  At least for now.  A lot of core functions are not as easy to separate as questions might be, and if we do it for some and not others then we have to explain that decision-making process too ... &lt;br /&gt;
&lt;br /&gt;
:::: [[User:Tim Hunt|Tim Hunt]] 20:32, 5 October 2009 (UTC) OK. I am happy to use subpackages. I&#039;ll follow those two examples.&lt;br /&gt;
&lt;br /&gt;
2. The OU likes me to put &lt;br /&gt;
&lt;br /&gt;
 * @copyright © 2006 The Open University&lt;br /&gt;
&lt;br /&gt;
which is fair enough. In this case, I think it would make sense to add an extra&lt;br /&gt;
&lt;br /&gt;
 * @author ...&lt;br /&gt;
&lt;br /&gt;
tag, even though in other cases we don&#039;t require them.--[[User:Tim Hunt|Tim Hunt]] 10:10, 2 October 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
: [[User:Martin Dougiamas|Martin Dougiamas]] 06:31, 5 October 2009 (UTC): Sorry but I totally disagree this should be in a @author tag at all.  You are probably not the sole author in the first place, and others will edit them in future and think they have to start adding their own name to any file they touch.  It gets contentious.  That&#039;s why we pulled them all out in the first place.  CVS has full author information for every line, no need to put it in the file.  You can put your name in the comments at the top of you like (something like &amp;quot;Initially written by Tim Hunt&amp;quot;).  &lt;br /&gt;
&lt;br /&gt;
:: Ah, fair enough. I was forgetting that the purpose of that like was just to make the copyright owner clear. I completely agree with you about not duplicating information in CVS.--[[User:Tim Hunt|Tim Hunt]] 08:37, 5 October 2009 (UTC)&lt;br /&gt;
&lt;br /&gt;
I just updated the bit about @package names, following discussion and a clear consensus with Petr: http://moodle.org/mod/cvsadmin/view.php?conversationid=4294.--[[User:Tim Hunt|Tim Hunt]] 10:47, 3 March 2010 (UTC)&lt;br /&gt;
&lt;br /&gt;
==  Wrapping Control Structures ==&lt;br /&gt;
&lt;br /&gt;
The example given suggests that:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (f() and g()) {&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
is equivalent to:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$tmp1 = f();&lt;br /&gt;
$tmp2 = g();&lt;br /&gt;
if ($tmp1 and $tmp2){&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But in the former g() is evaluated only if f() returns a value that is true (in a boolean context).&lt;br /&gt;
&lt;br /&gt;
I suggest something like&lt;br /&gt;
&lt;br /&gt;
BETTER GOOD:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$coursecategory = $element[&#039;object&#039;]-&amp;gt;is_course_item() or $element[&#039;object&#039;]-&amp;gt;is_category_item();&lt;br /&gt;
if ($coursecategory) {&lt;br /&gt;
    $scalevalue = in_array($element[&#039;object&#039;]-&amp;gt;gradetype, array(GRADE_TYPE_SCALE, GRADE_TYPE_VALUE));&lt;br /&gt;
    if ($scalevalue){&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I also suggest that the code fragments in GOOD and BAD be made to agree in the rest of the logic.  (GOOD has form ((a or b) and c) while BAD has form ((a or b) and (c or d)).&lt;br /&gt;
&lt;br /&gt;
[[User:jfine]] 7.35, 1 Aug 2010 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Sensible temporaries ==&lt;br /&gt;
&lt;br /&gt;
Here&#039;s some code thought to be BAD, improved with temporaries (and a probably trivial change of meaning).&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$eo = element[&#039;object&#039;];&lt;br /&gt;
$eog = $eo-&amp;gt;gradetype;&lt;br /&gt;
&lt;br /&gt;
if (($eo-&amp;gt;is_course_item() or $eo-&amp;gt;is_category_item())&lt;br /&gt;
    and ($eog == GRADE_TYPE_SCALE or $eog == GRADE_TYPE_VALUE)) {&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There.  The BAD code is now not so bad after all.  [[User:jfine]]  00:76, 1 Aug 2010 (UTC)&lt;br /&gt;
&lt;br /&gt;
== Database code ==&lt;br /&gt;
&lt;br /&gt;
I would suggest the following database coding guidelines&lt;br /&gt;
&lt;br /&gt;
== Database code using SQL ==&lt;br /&gt;
&lt;br /&gt;
* Where possible, use the plain get_records, get_recordset, etc functions instead of the _sql variants.&lt;br /&gt;
&lt;br /&gt;
Correct:&lt;br /&gt;
&lt;br /&gt;
 $DB-&amp;gt;get_record(&#039;course&#039;, array(&#039;id&#039; =&amp;gt; $id));&lt;br /&gt;
&lt;br /&gt;
Wrong:&lt;br /&gt;
&lt;br /&gt;
 $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {course} WHERE id=?&#039;, array($id));&lt;br /&gt;
&lt;br /&gt;
* When using SQL code, do not add variable parameters directly into the string. Instead, use the new methods for including parameters.&lt;br /&gt;
&lt;br /&gt;
Correct:&lt;br /&gt;
&lt;br /&gt;
 $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {course} WHERE id &amp;lt;&amp;gt; ?&#039;, array($exceptid));&lt;br /&gt;
&lt;br /&gt;
Wrong:&lt;br /&gt;
&lt;br /&gt;
 $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {course} WHERE id &amp;lt;&amp;gt; &#039; . $exceptid);&lt;br /&gt;
&lt;br /&gt;
(Using parameters properly avoids possible serious security holes.)&lt;br /&gt;
&lt;br /&gt;
* When using SQL code, do not use the $CFG-&amp;gt;prefix variable. Instead use the new {tablename} syntax.&lt;br /&gt;
&lt;br /&gt;
Correct:&lt;br /&gt;
&lt;br /&gt;
 $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {course} WHERE id &amp;lt;&amp;gt; ?&#039;, array($exceptid));&lt;br /&gt;
&lt;br /&gt;
Wrong:&lt;br /&gt;
&lt;br /&gt;
 $DB-&amp;gt;get_record_sql(&amp;quot;SELECT * FROM {$CFG-&amp;gt;prefix}course WHERE id &amp;lt;&amp;gt; ?&#039;, array($exceptid));&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=80149</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=80149"/>
		<updated>2011-01-10T13:52:22Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Access permissions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is &#039;&#039;&#039;only a proposal&#039;&#039;&#039;. The functionality described doesn&#039;t exist yet in Moodle.&lt;br /&gt;
&lt;br /&gt;
== DRAFT 2 ==&lt;br /&gt;
&lt;br /&gt;
This is the second draft of the proposal, with changes based on Petr&#039;s comments. As well as/because of adding all the class stuff, this version no longer requires rebuild_course_cache and should be 100% backward compatible with all existing uses of modinfo.&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
&lt;br /&gt;
* Petr wanted dynamic/lazy computation of some of the access stuff - this is necessary for the forum unread data etc, but not feasible to do in this version for any of the data that is already included in modinfo, because of the requirement for 100% BC which is not possible with __get methods; so all the existing property values must be initialised (see below). This also means it&#039;s a less scary change, i.e. more moving code around and less new code.&lt;br /&gt;
&lt;br /&gt;
* I wanted a class for the overall thing, which I called course_modinfo (because it&#039;s basically from the modinfo field of the course table). Then I neeed a name for the class for an individual module instance info. Petr suggested module_info but I thought cm_info was a bit closer (module_info might sound like it was about a whole module, not one instance). Anyhow, both names subject to change if anyone has better suggestions.&lt;br /&gt;
&lt;br /&gt;
* Linking the cm_info to its parent course_modinfo gives a nice framework to do  caching/lazy-initialisation stuff. For instance when we calculate forum unread data in the mod_forum_cm_info_view function, we will get a request for a single $cminfo, but can actually retrieve the data for all forum instances on the course at once (same as at present).&lt;br /&gt;
&lt;br /&gt;
* I didn&#039;t specify exactly how to address modinfo caching issues (...if there are any) but because this proposal suggests moving the &#039;work out the values&#039; stuff into the class, and leaving get_fast_modinfo to deal only with caching, it should be a whole lot simpler to see how the caching works and alter it in future.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
We would like to create a generic API that allows the following:&lt;br /&gt;
&lt;br /&gt;
* Module display on front page can be customised, for example making it possible to create another module that behaves like Label (displaying arbitrary html rather than a link to activity view.php) - currently this is hardcoded hack for Label. This change should apply to other areas such as navigation block as well as course page.&lt;br /&gt;
&lt;br /&gt;
* Some modules can provide dynamic text, such as forum displaying unread messages. At present this is hardcoded so that only forum can do it.&lt;br /&gt;
&lt;br /&gt;
* Modules can be hidden completely, or greyed out, from the view of particular students according to either custom module behaviour, or (if not specified by module) default behaviour regarding a new capability moodle/course:viewactivity and the existing option for whether a non-available module is greyed out or hidden entirely.&lt;br /&gt;
&lt;br /&gt;
This should take advantage of the existing modinfo cache in order that performance is not adversely affected.&lt;br /&gt;
&lt;br /&gt;
== API improvement ==&lt;br /&gt;
&lt;br /&gt;
Following Petr&#039;s suggestions, we would also like to improve the API by switching the modinfo in-memory data structures to use defined classes instead of anonymous stdClass objects. This achieves the following:&lt;br /&gt;
&lt;br /&gt;
* Provides a location for documentation of these structures. (Currently they are undocumented.)&lt;br /&gt;
&lt;br /&gt;
* In future, allows functions to specify the required type (i.e. some functions require the information from modinfo, not just a row from the table; they will now be able to specify this). There is no plan to change function definitions immediately.&lt;br /&gt;
&lt;br /&gt;
* Where the type is defined, makes metadata available to IDEs, allowing code completion and automatic documentation viewing.&lt;br /&gt;
&lt;br /&gt;
The following requirements should also be met:&lt;br /&gt;
&lt;br /&gt;
* 100% backward compatibility for existing use of these structures by core and third-party modules. (Obviously, where these want to support the new features, such as the navigation supporting other label-like modules, there do need to be changes which are included in these patch. But if we forget something or it&#039;s third-party, it should keep working as at present.)&lt;br /&gt;
&lt;br /&gt;
* 100% backward compatibility for existing modinfo data (in database).&lt;br /&gt;
&lt;br /&gt;
== Removing existing hacks ==&lt;br /&gt;
&lt;br /&gt;
* Existing hacks regarding label in all areas of code (e.g. navigation, etc) will be changed from the logic &#039;is this a label?&#039; to the logic &#039;does this activity have a view page?&#039; (which will work for label too)&lt;br /&gt;
* Existing hacks regarding forum unread data will be removed and the forum unread code will be moved into the new API function. The code will be written in such a way as to have the same performance characteristics.&lt;br /&gt;
&lt;br /&gt;
== get_fast_modinfo change ==&lt;br /&gt;
&lt;br /&gt;
The get_fast_modinfo function will be changed to return a new object of type course_modinfo, which will be compatible with the existing $modinfo return value.&lt;br /&gt;
&lt;br /&gt;
While constructing this object, in addition to current behaviour, the system will:&lt;br /&gt;
&lt;br /&gt;
* support new values defined in _get_coursemodule_info&lt;br /&gt;
&lt;br /&gt;
* extend dynamic per-user calculation (that checks is something is visible to current user, -&amp;gt;uservisible) with additional checks&lt;br /&gt;
&lt;br /&gt;
* call the new module API (if provided) after calculating modinfo, to get dynamic information&lt;br /&gt;
&lt;br /&gt;
== course_modinfo ==&lt;br /&gt;
&lt;br /&gt;
The new class course_modinfo will contain properties:&lt;br /&gt;
&lt;br /&gt;
* courseid, userid (ints)&lt;br /&gt;
* sections (array of int =&amp;gt; array of int)&lt;br /&gt;
* cms (array of int =&amp;gt; cm_info)&lt;br /&gt;
* instances (array of string =&amp;gt; array of int =&amp;gt; cm_info)&lt;br /&gt;
* groups - at present I can&#039;t tell what this is for as it seems to be empty for me even though there are groups on the course and I&#039;m a member of one of them - hmmm - anyhow I will figure it out and implement it&lt;br /&gt;
&lt;br /&gt;
These are identical to the values currently returned by modinfo, except for the use of cm_info class instead of bare stdClass for the information about modules.&lt;br /&gt;
&lt;br /&gt;
There are three options for handling this class:&lt;br /&gt;
&lt;br /&gt;
# Make these properties public so that they can be accessed exactly as at present.&lt;br /&gt;
# Make them private and use PHP magic __set and __get functions so that they can be read as normal but any attempt to write them would (while working) also cause a developer debug warning.&lt;br /&gt;
# Make the properties public, but deprecate them, and create separate get methods (get_courseid, get_userid, etc) which are recommended for future use.&lt;br /&gt;
&lt;br /&gt;
I initially favoured the second option but unfortunately, PHP being PHP, this doesn&#039;t quite work properly: specifically, you can&#039;t use the empty() function on such values. Since there might be places in the code that call empty(), maybe this isn&#039;t a good idea. &lt;br /&gt;
&lt;br /&gt;
Consequently I tend to the third one; we should make better get methods such as get_cm($cmid) as well as just get_cms(), which may help make new code more readable.&lt;br /&gt;
&lt;br /&gt;
=== Construction ===&lt;br /&gt;
&lt;br /&gt;
The code that creates this information should largely be moved to this class (either in a constructor or in init methods etc) from its current location in get_fast_modinfo. get_fast_modinfo should remain responsible only for caching these objects.&lt;br /&gt;
&lt;br /&gt;
== cm_info class ==&lt;br /&gt;
&lt;br /&gt;
=== Basic structure ===&lt;br /&gt;
&lt;br /&gt;
The new class cm_info will be constructed with a $parent (course_modinfo). Knowing its parent allows the class to carry out operations with regard to the whole course, where this is beneficial for performance, without needing extra parameters. There will be a get_modinfo() method to return this.&lt;br /&gt;
&lt;br /&gt;
=== Defined states ===&lt;br /&gt;
&lt;br /&gt;
This class has three states:&lt;br /&gt;
&lt;br /&gt;
* BASIC_DATA: class has the data from database modinfo table, and automatically completed data that is not related to the current user.&lt;br /&gt;
* DYNAMIC_DATA: class has all data that is available for most pages, including data related to the current request from the current user&#039;s permissions and the _cm_info_dynamic function if applicable.&lt;br /&gt;
* VIEW_DATA: class also has the data that is needed only on the course view page (or similar pages) from the _cm_info_view function if applicable (basically only used for forum unread data and stuff like that).&lt;br /&gt;
&lt;br /&gt;
When get_fast_modinfo returns, all returned objects will be in DYNAMIC_DATA state. This will be sufficient for determining whether the current user has access to the activity and for displaying the basic link etc. Basically the only thing it doesn&#039;t include is supplemental data that is only needed on the course page, such as forum unread data (get_after_link function). This data takes time to calculate so is generated on request.&lt;br /&gt;
&lt;br /&gt;
The intention is that getting to DYNAMIC_DATA state shouldn&#039;t require any database calls (certainly for the core modules). Getting to VIEW_DATA state may require database calls, e.g. to calculate the number of unread forum posts.&lt;br /&gt;
&lt;br /&gt;
=== Existing properties ===&lt;br /&gt;
&lt;br /&gt;
These properties are in current modinfo:&lt;br /&gt;
&lt;br /&gt;
* id, instance, course, idnumber, visible, groupmode, groupingid, groupmembersonly, indent, completion, availablefrom, availableuntil, showavailability - data from course_modules row&lt;br /&gt;
&lt;br /&gt;
* extra, icon, iconcomponent, modname, name, sectionnum, conditionscompletion, conditionsgrade, modplural (wtf is this there when the singular name isn&#039;t and both should be available with get_string?!) - data from modinfo which was computed when generating the cache&lt;br /&gt;
&lt;br /&gt;
* availableinfo, available, uservisible - data relating to the current user which is computed dynamically when obtaining the modinfo object&lt;br /&gt;
&lt;br /&gt;
This is the list of properties currently available in modinfo objects, so will be implemented as-is. The same question regarding use of __set, __get applies as above, but in this case it&#039;s perhaps more likely that people might call empty() on the existing properties; again, I propose retaining the above as public properties, but deprecated, and providing get_x methods for new use. Any new properties would be private and available only with get_ methods.&lt;br /&gt;
&lt;br /&gt;
Some of the get methods might have a slightly different definition to the raw properties. For example get_icon should use the provided data to return a moodle_icon object (if there is one? I am not sure), not a string.&lt;br /&gt;
&lt;br /&gt;
=== New data / get methods ===&lt;br /&gt;
&lt;br /&gt;
The new data and corresponding get methods are:&lt;br /&gt;
&lt;br /&gt;
* has_view() - true if the module should have a link to its view.php shown in navigation, be included in lists of &#039;did the user visit this module&#039; stats, etc. (Basically this is the &#039;is not a label&#039; method.)&lt;br /&gt;
* get_url() - returns moodle_url to module view.php, or null if has_view is false (this reduces code duplication in various places)&lt;br /&gt;
* get_content() # - returns HTML content to be displayed on the course page where this module is placed; appears below the link, if present (this is how Label displays content on course page) &lt;br /&gt;
* get_extra_classes() # - returns additional CSS classes to be added to the A or DIV tag(s) for this item on main course page. &lt;br /&gt;
* get_custom_data() - returns an optional mixed value containing custom data for this module, which needs to be available course-wide via the get_fast_modinfo function.&lt;br /&gt;
* get_after_link() # - returns HTML code which displays after the link.&lt;br /&gt;
* get_after_edit_icons() # - returns HTML code which displays after the standard icons  (hide, edit, delete, etc) when editing.&lt;br /&gt;
&lt;br /&gt;
Functions marked # require &#039;view information&#039;. This is additional information intended for use on the course view page and not in other places that use modinfo: the most obvious example (and the only one that affects core) is forum unread data. Consequently it is only obtained on request (see state information above). Obtaining view info requires calling the module&#039;s _cm_info_view function if it has one.&lt;br /&gt;
&lt;br /&gt;
=== Set methods ===&lt;br /&gt;
&lt;br /&gt;
Set methods are available for use only by _cm_info_dynamic and _cm_info_view functions; see below.&lt;br /&gt;
&lt;br /&gt;
== Modify _get_coursemodule_info ==&lt;br /&gt;
&lt;br /&gt;
There will be a change to the existing module API function _get_coursemodule_info which is used to update information before storing it in the database modinfo field. This change will retain backward compatibility. &lt;br /&gt;
&lt;br /&gt;
The function returns an object which is currently a stdClass object. This possibility will be retained for backward compatibility, with unchanged behaviour. All existing fields are supported:&lt;br /&gt;
&lt;br /&gt;
* name: name of instance or (for labels only) content of instance&lt;br /&gt;
* icon: name of icon or special weird string&lt;br /&gt;
* iconcomponent: component of icon, possibly works&lt;br /&gt;
* extra: extra data inserted somewhere horrible in the html&lt;br /&gt;
&lt;br /&gt;
These fields are all optional and may be left unset to accept the defaults.&lt;br /&gt;
&lt;br /&gt;
However a new class cached_cm_info can be returned instead.&lt;br /&gt;
&lt;br /&gt;
The class cached_cm_info has the following properties:&lt;br /&gt;
&lt;br /&gt;
* name, icon, iconcomponent: as before&lt;br /&gt;
* content: HTML content to be displayed on the course page where this module is placed; appears below the link, if present (this is how a Label-like module can display content on course page)&lt;br /&gt;
* customdata: A place to store a string or object containing custom data for this module, which needs to be available course-wide via the get_fast_modinfo function. If present, this data should be small in size.&lt;br /&gt;
* extraclasses: Extra CSS classes to add to the item on course page display, if required.&lt;br /&gt;
&lt;br /&gt;
(It does not support extra, which is deprecated on account of being stupid, at least unless I figure out some existing use case that really needs to be carried forward into a non-deprecated system.)&lt;br /&gt;
&lt;br /&gt;
=== Storing data ===&lt;br /&gt;
&lt;br /&gt;
Returning a stdClass object should result in identical content of the modinfo field in the database to present.&lt;br /&gt;
&lt;br /&gt;
Return cached_cm_info results in similar content but with extra fields -&amp;gt;content and -&amp;gt;customdata. These fields are only stored if they are non-null, i.e. it won&#039;t bulk up the database with empty &#039;content=nothing&#039; type data against every module.&lt;br /&gt;
&lt;br /&gt;
=== Reading label data ===&lt;br /&gt;
&lt;br /&gt;
The new content field is different to existing behaviour of the Label module, which stores its content in the &#039;name&#039; field as noted. When reading this data or processing it (for example in cm_info class), the system takes the following approach:&lt;br /&gt;
&lt;br /&gt;
* if the modname is &#039;label&#039; and there is no &#039;content&#039; field, then copy the &#039;name&#039; field into &#039;content&#039;.&lt;br /&gt;
&lt;br /&gt;
Note that this behaviour retains backward compatibility; the label still has a silly &#039;name&#039; value. For new code, has_view() returns false so it won&#039;t use name; for old code, the existing hard-coded exceptions for label will continue to work.&lt;br /&gt;
&lt;br /&gt;
== Extend dynamic calculation ==&lt;br /&gt;
&lt;br /&gt;
Currently the modinfo code makes the following checks that apply dynamically per-request (and do not directly come from the cache) in order to create the -&amp;gt;uservisible member variable.&lt;br /&gt;
&lt;br /&gt;
* If -&amp;gt;groupmembersonly is set, checks if the user belongs to group or has accessallgroups.&lt;br /&gt;
* If availability restrictions (date, grade, completion) are set, checks these (also even if available to current user, stores information into -&amp;gt;availableinfo, -&amp;gt;available for information when editing).&lt;br /&gt;
&lt;br /&gt;
My proposal is:&lt;br /&gt;
&lt;br /&gt;
* Make this part of the code (that &#039;specialises&#039; a single mod value for the current user/request) into a separate function within cm_info class, just to simplify it. This function should be the one that changes the cm_info state from BASIC_DATA to DYNAMIC_DATA.&lt;br /&gt;
* Add a check for the moodle/course:viewactivity capability; if user doesn&#039;t have this capability, set -&amp;gt;uservisible to false. Also check the option about what to do with hidden activities; if this is set to the default &#039;grey it out&#039;, then set -&amp;gt;inactive to true.&lt;br /&gt;
** Note: The default value for moodle/course:viewactivity should be true for all roles, even guest. This maintains existing behaviour. Sites that don&#039;t want guests to view activities can change the main role definition for guest.&lt;br /&gt;
* Call the _cm_info_dynamic function (below) if the current module supplies one.&lt;br /&gt;
&lt;br /&gt;
PERFORMANCE CONCERNS: Minimal. No new database queries are required, just a capability check and a function existence check.&lt;br /&gt;
&lt;br /&gt;
== New module API ==&lt;br /&gt;
&lt;br /&gt;
Two new module API functions will be defined. They both have one parameter: the cm_info object for this module, containing all the data from the modinfo cache.&lt;br /&gt;
&lt;br /&gt;
The functions are:&lt;br /&gt;
&lt;br /&gt;
* _cm_info_dynamic - add basic data that is always required (must be fast; should not make any database calls)&lt;br /&gt;
* _cm_info_view - add data that is required for the course view page (may make database calls)&lt;br /&gt;
&lt;br /&gt;
The functions both call set methods in the cm_info object. Examples:&lt;br /&gt;
&lt;br /&gt;
* set_user_visible(false) - hide activity entirely&lt;br /&gt;
* set_available(false) - grey out activity while it remains visible&lt;br /&gt;
* set_available_info(&#039;not available because you suck&#039;) - add explanation for why it&#039;s not available&lt;br /&gt;
* &#039;&#039;&#039;set_after_link&#039;&#039;&#039;(&#039;16 unread&#039;) # - add html code to appear after link&lt;br /&gt;
* &#039;&#039;&#039;set_edit_icons&#039;&#039;&#039;($html) # - add some code to appear when editing&lt;br /&gt;
* set_has_view(false) - make module not have link, navigation, etc even if it has a view.php&lt;br /&gt;
* set_content($html) # - add/change code to appear below the link&lt;br /&gt;
* set_name($text) - change text of the link&lt;br /&gt;
* set_extra_classes(&#039;whatever classes&#039;) # - add CSS classes&lt;br /&gt;
&lt;br /&gt;
The bold functions set cm_info data that cannot be set anywhere else (such as by system defaults or modinfo classes). This is basically because there didn&#039;t seem any general need to cache this data, particularly bearing in mind that modules can cache anything they like using the customdata property of cached_cm_info.&lt;br /&gt;
&lt;br /&gt;
_cm_info_view is restricted: for example, you cannot change user_visible status in view (because then it wouldn&#039;t work when checking access etc on other pages). You can only change the &#039;view&#039; properties. This can be enforced by making the set methods throw exceptions if called when the cm_info is in its already-defined state.&lt;br /&gt;
&lt;br /&gt;
Should it be necessary, this function can access the existing information in the cm_info object and also in the parent course_modinfo object (such as the user id) by calling get_modinfo on the cm_info.&lt;br /&gt;
&lt;br /&gt;
PERFORMANCE CONCERNS: None. Of standard modules, only the forum will implement this and only in the &#039;view&#039; version used just on course page; it will use the same code as at present. Custom modules will need to be written carefully in a similar manner so that they also perform well (ie do any queries once for whole course and store in global cache, not once per module).&lt;br /&gt;
&lt;br /&gt;
== Access permissions == &lt;br /&gt;
&lt;br /&gt;
When calling require_login with a $cm parameter, this will do get_fast_modinfo and check the is_user_visible() method in order to process per-user access restrictions:&lt;br /&gt;
&lt;br /&gt;
* groupmembersonly&lt;br /&gt;
* moodle/course:viewactivity&lt;br /&gt;
&lt;br /&gt;
PERFORMANCE CONCERNS: Because require_login like this is used on all module pages, we should use a profiler to evaluate before/after performance of this change and ensure that it isn&#039;t worsened. I suspect it won&#039;t be, because I think it already checks groupmembersonly anyway, so it&#039;s doing basically the same test; and I think most pages already call get_fast_modinfo e.g. for navigation block.&lt;br /&gt;
&lt;br /&gt;
NOTE: The way modules currently initialise is a bit inefficient, if get_fast_modinfo is to be used. The current sequence on most module pages is something like:&lt;br /&gt;
&lt;br /&gt;
* get course module (from id, supplied)&lt;br /&gt;
* get course&lt;br /&gt;
* get instance&lt;br /&gt;
&lt;br /&gt;
(3 queries)&lt;br /&gt;
&lt;br /&gt;
we could change this to&lt;br /&gt;
&lt;br /&gt;
* get course (from simple join using cmid, supplied)&lt;br /&gt;
* use get_fast_modinfo to get cm&lt;br /&gt;
* get instance&lt;br /&gt;
&lt;br /&gt;
(2 queries)&lt;br /&gt;
&lt;br /&gt;
Not suggesting that as part of this change (and it probably shouldn&#039;t be in this document), just saying...&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=80148</id>
		<title>Development:Module visibility and display</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/39/en/index.php?title=Development:Module_visibility_and_display&amp;diff=80148"/>
		<updated>2011-01-10T13:43:08Z</updated>

		<summary type="html">&lt;p&gt;Quen: /* Existing properties */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is &#039;&#039;&#039;only a proposal&#039;&#039;&#039;. The functionality described doesn&#039;t exist yet in Moodle.&lt;br /&gt;
&lt;br /&gt;
== DRAFT 2 ==&lt;br /&gt;
&lt;br /&gt;
This is the second draft of the proposal, with changes based on Petr&#039;s comments. As well as/because of adding all the class stuff, this version no longer requires rebuild_course_cache and should be 100% backward compatible with all existing uses of modinfo.&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
&lt;br /&gt;
* Petr wanted dynamic/lazy computation of some of the access stuff - this is necessary for the forum unread data etc, but not feasible to do in this version for any of the data that is already included in modinfo, because of the requirement for 100% BC which is not possible with __get methods; so all the existing property values must be initialised (see below). This also means it&#039;s a less scary change, i.e. more moving code around and less new code.&lt;br /&gt;
&lt;br /&gt;
* I wanted a class for the overall thing, which I called course_modinfo (because it&#039;s basically from the modinfo field of the course table). Then I neeed a name for the class for an individual module instance info. Petr suggested module_info but I thought cm_info was a bit closer (module_info might sound like it was about a whole module, not one instance). Anyhow, both names subject to change if anyone has better suggestions.&lt;br /&gt;
&lt;br /&gt;
* Linking the cm_info to its parent course_modinfo gives a nice framework to do  caching/lazy-initialisation stuff. For instance when we calculate forum unread data in the mod_forum_cm_info_view function, we will get a request for a single $cminfo, but can actually retrieve the data for all forum instances on the course at once (same as at present).&lt;br /&gt;
&lt;br /&gt;
* I didn&#039;t specify exactly how to address modinfo caching issues (...if there are any) but because this proposal suggests moving the &#039;work out the values&#039; stuff into the class, and leaving get_fast_modinfo to deal only with caching, it should be a whole lot simpler to see how the caching works and alter it in future.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
We would like to create a generic API that allows the following:&lt;br /&gt;
&lt;br /&gt;
* Module display on front page can be customised, for example making it possible to create another module that behaves like Label (displaying arbitrary html rather than a link to activity view.php) - currently this is hardcoded hack for Label. This change should apply to other areas such as navigation block as well as course page.&lt;br /&gt;
&lt;br /&gt;
* Some modules can provide dynamic text, such as forum displaying unread messages. At present this is hardcoded so that only forum can do it.&lt;br /&gt;
&lt;br /&gt;
* Modules can be hidden completely, or greyed out, from the view of particular students according to either custom module behaviour, or (if not specified by module) default behaviour regarding a new capability moodle/course:viewactivity and the existing option for whether a non-available module is greyed out or hidden entirely.&lt;br /&gt;
&lt;br /&gt;
This should take advantage of the existing modinfo cache in order that performance is not adversely affected.&lt;br /&gt;
&lt;br /&gt;
== API improvement ==&lt;br /&gt;
&lt;br /&gt;
Following Petr&#039;s suggestions, we would also like to improve the API by switching the modinfo in-memory data structures to use defined classes instead of anonymous stdClass objects. This achieves the following:&lt;br /&gt;
&lt;br /&gt;
* Provides a location for documentation of these structures. (Currently they are undocumented.)&lt;br /&gt;
&lt;br /&gt;
* In future, allows functions to specify the required type (i.e. some functions require the information from modinfo, not just a row from the table; they will now be able to specify this). There is no plan to change function definitions immediately.&lt;br /&gt;
&lt;br /&gt;
* Where the type is defined, makes metadata available to IDEs, allowing code completion and automatic documentation viewing.&lt;br /&gt;
&lt;br /&gt;
The following requirements should also be met:&lt;br /&gt;
&lt;br /&gt;
* 100% backward compatibility for existing use of these structures by core and third-party modules. (Obviously, where these want to support the new features, such as the navigation supporting other label-like modules, there do need to be changes which are included in these patch. But if we forget something or it&#039;s third-party, it should keep working as at present.)&lt;br /&gt;
&lt;br /&gt;
* 100% backward compatibility for existing modinfo data (in database).&lt;br /&gt;
&lt;br /&gt;
== Removing existing hacks ==&lt;br /&gt;
&lt;br /&gt;
* Existing hacks regarding label in all areas of code (e.g. navigation, etc) will be changed from the logic &#039;is this a label?&#039; to the logic &#039;does this activity have a view page?&#039; (which will work for label too)&lt;br /&gt;
* Existing hacks regarding forum unread data will be removed and the forum unread code will be moved into the new API function. The code will be written in such a way as to have the same performance characteristics.&lt;br /&gt;
&lt;br /&gt;
== get_fast_modinfo change ==&lt;br /&gt;
&lt;br /&gt;
The get_fast_modinfo function will be changed to return a new object of type course_modinfo, which will be compatible with the existing $modinfo return value.&lt;br /&gt;
&lt;br /&gt;
While constructing this object, in addition to current behaviour, the system will:&lt;br /&gt;
&lt;br /&gt;
* support new values defined in _get_coursemodule_info&lt;br /&gt;
&lt;br /&gt;
* extend dynamic per-user calculation (that checks is something is visible to current user, -&amp;gt;uservisible) with additional checks&lt;br /&gt;
&lt;br /&gt;
* call the new module API (if provided) after calculating modinfo, to get dynamic information&lt;br /&gt;
&lt;br /&gt;
== course_modinfo ==&lt;br /&gt;
&lt;br /&gt;
The new class course_modinfo will contain properties:&lt;br /&gt;
&lt;br /&gt;
* courseid, userid (ints)&lt;br /&gt;
* sections (array of int =&amp;gt; array of int)&lt;br /&gt;
* cms (array of int =&amp;gt; cm_info)&lt;br /&gt;
* instances (array of string =&amp;gt; array of int =&amp;gt; cm_info)&lt;br /&gt;
* groups - at present I can&#039;t tell what this is for as it seems to be empty for me even though there are groups on the course and I&#039;m a member of one of them - hmmm - anyhow I will figure it out and implement it&lt;br /&gt;
&lt;br /&gt;
These are identical to the values currently returned by modinfo, except for the use of cm_info class instead of bare stdClass for the information about modules.&lt;br /&gt;
&lt;br /&gt;
There are three options for handling this class:&lt;br /&gt;
&lt;br /&gt;
# Make these properties public so that they can be accessed exactly as at present.&lt;br /&gt;
# Make them private and use PHP magic __set and __get functions so that they can be read as normal but any attempt to write them would (while working) also cause a developer debug warning.&lt;br /&gt;
# Make the properties public, but deprecate them, and create separate get methods (get_courseid, get_userid, etc) which are recommended for future use.&lt;br /&gt;
&lt;br /&gt;
I initially favoured the second option but unfortunately, PHP being PHP, this doesn&#039;t quite work properly: specifically, you can&#039;t use the empty() function on such values. Since there might be places in the code that call empty(), maybe this isn&#039;t a good idea. &lt;br /&gt;
&lt;br /&gt;
Consequently I tend to the third one; we should make better get methods such as get_cm($cmid) as well as just get_cms(), which may help make new code more readable.&lt;br /&gt;
&lt;br /&gt;
=== Construction ===&lt;br /&gt;
&lt;br /&gt;
The code that creates this information should largely be moved to this class (either in a constructor or in init methods etc) from its current location in get_fast_modinfo. get_fast_modinfo should remain responsible only for caching these objects.&lt;br /&gt;
&lt;br /&gt;
== cm_info class ==&lt;br /&gt;
&lt;br /&gt;
=== Basic structure ===&lt;br /&gt;
&lt;br /&gt;
The new class cm_info will be constructed with a $parent (course_modinfo). Knowing its parent allows the class to carry out operations with regard to the whole course, where this is beneficial for performance, without needing extra parameters. There will be a get_modinfo() method to return this.&lt;br /&gt;
&lt;br /&gt;
=== Defined states ===&lt;br /&gt;
&lt;br /&gt;
This class has three states:&lt;br /&gt;
&lt;br /&gt;
* BASIC_DATA: class has the data from database modinfo table, and automatically completed data that is not related to the current user.&lt;br /&gt;
* DYNAMIC_DATA: class has all data that is available for most pages, including data related to the current request from the current user&#039;s permissions and the _cm_info_dynamic function if applicable.&lt;br /&gt;
* VIEW_DATA: class also has the data that is needed only on the course view page (or similar pages) from the _cm_info_view function if applicable (basically only used for forum unread data and stuff like that).&lt;br /&gt;
&lt;br /&gt;
When get_fast_modinfo returns, all returned objects will be in DYNAMIC_DATA state. This will be sufficient for determining whether the current user has access to the activity and for displaying the basic link etc. Basically the only thing it doesn&#039;t include is supplemental data that is only needed on the course page, such as forum unread data (get_after_link function). This data takes time to calculate so is generated on request.&lt;br /&gt;
&lt;br /&gt;
The intention is that getting to DYNAMIC_DATA state shouldn&#039;t require any database calls (certainly for the core modules). Getting to VIEW_DATA state may require database calls, e.g. to calculate the number of unread forum posts.&lt;br /&gt;
&lt;br /&gt;
=== Existing properties ===&lt;br /&gt;
&lt;br /&gt;
These properties are in current modinfo:&lt;br /&gt;
&lt;br /&gt;
* id, instance, course, idnumber, visible, groupmode, groupingid, groupmembersonly, indent, completion, availablefrom, availableuntil, showavailability - data from course_modules row&lt;br /&gt;
&lt;br /&gt;
* extra, icon, iconcomponent, modname, name, sectionnum, conditionscompletion, conditionsgrade, modplural (wtf is this there when the singular name isn&#039;t and both should be available with get_string?!) - data from modinfo which was computed when generating the cache&lt;br /&gt;
&lt;br /&gt;
* availableinfo, available, uservisible - data relating to the current user which is computed dynamically when obtaining the modinfo object&lt;br /&gt;
&lt;br /&gt;
This is the list of properties currently available in modinfo objects, so will be implemented as-is. The same question regarding use of __set, __get applies as above, but in this case it&#039;s perhaps more likely that people might call empty() on the existing properties; again, I propose retaining the above as public properties, but deprecated, and providing get_x methods for new use. Any new properties would be private and available only with get_ methods.&lt;br /&gt;
&lt;br /&gt;
Some of the get methods might have a slightly different definition to the raw properties. For example get_icon should use the provided data to return a moodle_icon object (if there is one? I am not sure), not a string.&lt;br /&gt;
&lt;br /&gt;
=== New data / get methods ===&lt;br /&gt;
&lt;br /&gt;
The new data and corresponding get methods are:&lt;br /&gt;
&lt;br /&gt;
* has_view() - true if the module should have a link to its view.php shown in navigation, be included in lists of &#039;did the user visit this module&#039; stats, etc. (Basically this is the &#039;is not a label&#039; method.)&lt;br /&gt;
* get_url() - returns moodle_url to module view.php, or null if has_view is false (this reduces code duplication in various places)&lt;br /&gt;
* get_content() # - returns HTML content to be displayed on the course page where this module is placed; appears below the link, if present (this is how Label displays content on course page) &lt;br /&gt;
* get_extra_classes() # - returns additional CSS classes to be added to the A or DIV tag(s) for this item on main course page. &lt;br /&gt;
* get_custom_data() - returns an optional mixed value containing custom data for this module, which needs to be available course-wide via the get_fast_modinfo function.&lt;br /&gt;
* get_after_link() # - returns HTML code which displays after the link.&lt;br /&gt;
* get_after_edit_icons() # - returns HTML code which displays after the standard icons  (hide, edit, delete, etc) when editing.&lt;br /&gt;
&lt;br /&gt;
Functions marked # require &#039;view information&#039;. This is additional information intended for use on the course view page and not in other places that use modinfo: the most obvious example (and the only one that affects core) is forum unread data. Consequently it is only obtained on request (see state information above). Obtaining view info requires calling the module&#039;s _cm_info_view function if it has one.&lt;br /&gt;
&lt;br /&gt;
=== Set methods ===&lt;br /&gt;
&lt;br /&gt;
Set methods are available for use only by _cm_info_dynamic and _cm_info_view functions; see below.&lt;br /&gt;
&lt;br /&gt;
== Modify _get_coursemodule_info ==&lt;br /&gt;
&lt;br /&gt;
There will be a change to the existing module API function _get_coursemodule_info which is used to update information before storing it in the database modinfo field. This change will retain backward compatibility. &lt;br /&gt;
&lt;br /&gt;
The function returns an object which is currently a stdClass object. This possibility will be retained for backward compatibility, with unchanged behaviour. All existing fields are supported:&lt;br /&gt;
&lt;br /&gt;
* name: name of instance or (for labels only) content of instance&lt;br /&gt;
* icon: name of icon or special weird string&lt;br /&gt;
* iconcomponent: component of icon, possibly works&lt;br /&gt;
* extra: extra data inserted somewhere horrible in the html&lt;br /&gt;
&lt;br /&gt;
These fields are all optional and may be left unset to accept the defaults.&lt;br /&gt;
&lt;br /&gt;
However a new class cached_cm_info can be returned instead.&lt;br /&gt;
&lt;br /&gt;
The class cached_cm_info has the following properties:&lt;br /&gt;
&lt;br /&gt;
* name, icon, iconcomponent: as before&lt;br /&gt;
* content: HTML content to be displayed on the course page where this module is placed; appears below the link, if present (this is how a Label-like module can display content on course page)&lt;br /&gt;
* customdata: A place to store a string or object containing custom data for this module, which needs to be available course-wide via the get_fast_modinfo function. If present, this data should be small in size.&lt;br /&gt;
* extraclasses: Extra CSS classes to add to the item on course page display, if required.&lt;br /&gt;
&lt;br /&gt;
(It does not support extra, which is deprecated on account of being stupid, at least unless I figure out some existing use case that really needs to be carried forward into a non-deprecated system.)&lt;br /&gt;
&lt;br /&gt;
=== Storing data ===&lt;br /&gt;
&lt;br /&gt;
Returning a stdClass object should result in identical content of the modinfo field in the database to present.&lt;br /&gt;
&lt;br /&gt;
Return cached_cm_info results in similar content but with extra fields -&amp;gt;content and -&amp;gt;customdata. These fields are only stored if they are non-null, i.e. it won&#039;t bulk up the database with empty &#039;content=nothing&#039; type data against every module.&lt;br /&gt;
&lt;br /&gt;
=== Reading label data ===&lt;br /&gt;
&lt;br /&gt;
The new content field is different to existing behaviour of the Label module, which stores its content in the &#039;name&#039; field as noted. When reading this data or processing it (for example in cm_info class), the system takes the following approach:&lt;br /&gt;
&lt;br /&gt;
* if the modname is &#039;label&#039; and there is no &#039;content&#039; field, then copy the &#039;name&#039; field into &#039;content&#039;.&lt;br /&gt;
&lt;br /&gt;
Note that this behaviour retains backward compatibility; the label still has a silly &#039;name&#039; value. For new code, has_view() returns false so it won&#039;t use name; for old code, the existing hard-coded exceptions for label will continue to work.&lt;br /&gt;
&lt;br /&gt;
== Extend dynamic calculation ==&lt;br /&gt;
&lt;br /&gt;
Currently the modinfo code makes the following checks that apply dynamically per-request (and do not directly come from the cache) in order to create the -&amp;gt;uservisible member variable.&lt;br /&gt;
&lt;br /&gt;
* If -&amp;gt;groupmembersonly is set, checks if the user belongs to group or has accessallgroups.&lt;br /&gt;
* If availability restrictions (date, grade, completion) are set, checks these (also even if available to current user, stores information into -&amp;gt;availableinfo, -&amp;gt;available for information when editing).&lt;br /&gt;
&lt;br /&gt;
My proposal is:&lt;br /&gt;
&lt;br /&gt;
* Make this part of the code (that &#039;specialises&#039; a single mod value for the current user/request) into a separate function within cm_info class, just to simplify it. This function should be the one that changes the cm_info state from BASIC_DATA to DYNAMIC_DATA.&lt;br /&gt;
* Add a check for the moodle/course:viewactivity capability; if user doesn&#039;t have this capability, set -&amp;gt;uservisible to false. Also check the option about what to do with hidden activities; if this is set to the default &#039;grey it out&#039;, then set -&amp;gt;inactive to true.&lt;br /&gt;
** Note: The default value for moodle/course:viewactivity should be true for all roles, even guest. This maintains existing behaviour. Sites that don&#039;t want guests to view activities can change the main role definition for guest.&lt;br /&gt;
* Call the _cm_info_dynamic function (below) if the current module supplies one.&lt;br /&gt;
&lt;br /&gt;
PERFORMANCE CONCERNS: Minimal. No new database queries are required, just a capability check and a function existence check.&lt;br /&gt;
&lt;br /&gt;
== New module API ==&lt;br /&gt;
&lt;br /&gt;
Two new module API functions will be defined. They both have one parameter: the cm_info object for this module, containing all the data from the modinfo cache.&lt;br /&gt;
&lt;br /&gt;
The functions are:&lt;br /&gt;
&lt;br /&gt;
* _cm_info_dynamic - add basic data that is always required (must be fast; should not make any database calls)&lt;br /&gt;
* _cm_info_view - add data that is required for the course view page (may make database calls)&lt;br /&gt;
&lt;br /&gt;
The functions both call set methods in the cm_info object. Examples:&lt;br /&gt;
&lt;br /&gt;
* set_user_visible(false) - hide activity entirely&lt;br /&gt;
* set_available(false) - grey out activity while it remains visible&lt;br /&gt;
* set_available_info(&#039;not available because you suck&#039;) - add explanation for why it&#039;s not available&lt;br /&gt;
* &#039;&#039;&#039;set_after_link&#039;&#039;&#039;(&#039;16 unread&#039;) # - add html code to appear after link&lt;br /&gt;
* &#039;&#039;&#039;set_edit_icons&#039;&#039;&#039;($html) # - add some code to appear when editing&lt;br /&gt;
* set_has_view(false) - make module not have link, navigation, etc even if it has a view.php&lt;br /&gt;
* set_content($html) # - add/change code to appear below the link&lt;br /&gt;
* set_name($text) - change text of the link&lt;br /&gt;
* set_extra_classes(&#039;whatever classes&#039;) # - add CSS classes&lt;br /&gt;
&lt;br /&gt;
The bold functions set cm_info data that cannot be set anywhere else (such as by system defaults or modinfo classes). This is basically because there didn&#039;t seem any general need to cache this data, particularly bearing in mind that modules can cache anything they like using the customdata property of cached_cm_info.&lt;br /&gt;
&lt;br /&gt;
_cm_info_view is restricted: for example, you cannot change user_visible status in view (because then it wouldn&#039;t work when checking access etc on other pages). You can only change the &#039;view&#039; properties. This can be enforced by making the set methods throw exceptions if called when the cm_info is in its already-defined state.&lt;br /&gt;
&lt;br /&gt;
Should it be necessary, this function can access the existing information in the cm_info object and also in the parent course_modinfo object (such as the user id) by calling get_modinfo on the cm_info.&lt;br /&gt;
&lt;br /&gt;
PERFORMANCE CONCERNS: None. Of standard modules, only the forum will implement this and only in the &#039;view&#039; version used just on course page; it will use the same code as at present. Custom modules will need to be written carefully in a similar manner so that they also perform well (ie do any queries once for whole course and store in global cache, not once per module).&lt;br /&gt;
&lt;br /&gt;
== Access permissions == &lt;br /&gt;
&lt;br /&gt;
When calling require_login with a $cm parameter, this will do get_fast_modinfo and check the is_user_visible() method in order to process per-user access restrictions:&lt;br /&gt;
&lt;br /&gt;
* groupmembersonly&lt;br /&gt;
* moodle/course:viewactivity&lt;br /&gt;
&lt;br /&gt;
PERFORMANCE CONCERNS: Because require_login like this is used on all module pages, we should use a profiler to evaluate before/after performance of this change and ensure that it isn&#039;t worsened. I suspect it won&#039;t be, see below.&lt;br /&gt;
&lt;br /&gt;
NOTE: The way modules currently initialise is a bit inefficient, especially if get_fast_modinfo is to be used (and I suspect it already is on most pages, given the navigation block probably uses it). The current sequence on most module pages is something like:&lt;br /&gt;
&lt;br /&gt;
* get course module (from id, supplied)&lt;br /&gt;
* get course&lt;br /&gt;
* get instance&lt;br /&gt;
&lt;br /&gt;
(3 queries)&lt;br /&gt;
&lt;br /&gt;
we could change this to&lt;br /&gt;
&lt;br /&gt;
* get course (from simple join using cmid, supplied)&lt;br /&gt;
* use get_fast_modinfo to get cm&lt;br /&gt;
* get instance&lt;br /&gt;
&lt;br /&gt;
(2 queries)&lt;br /&gt;
&lt;br /&gt;
Not suggesting that as part of this change (and it probably shouldn&#039;t be in this document), just saying...&lt;/div&gt;</summary>
		<author><name>Quen</name></author>
	</entry>
</feed>